From f482e490771a91bde276437abfee14d7a6e8f9f6 Mon Sep 17 00:00:00 2001 From: Saravana Kannan Date: Wed, 16 Jan 2019 18:28:39 -0800 Subject: [PATCH] PM / devfreq: bimc-bwmon: Add support for version 2 The version 2 of the BIMC BWMON HW doesn't reset the counter to 0 when it hits the threshold. It also has support for an overflow status register. Change-Id: I9f18d2153a2e5e762ec9950f26e0e7601468a80a Signed-off-by: Saravana Kannan [avajid@codeaurora.org: made minor styling changes] Signed-off-by: Amir Vajid --- drivers/devfreq/bimc-bwmon.c | 73 ++++++++++++++++++++++++++---------- 1 file changed, 54 insertions(+), 19 deletions(-) diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c index 8a9e4ef91824..cbe35fb6e97b 100644 --- a/drivers/devfreq/bimc-bwmon.c +++ b/drivers/devfreq/bimc-bwmon.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "governor_bw_hwmon.h" @@ -31,13 +32,19 @@ #define MON_MASK(m) ((m)->base + 0x298) #define MON_MATCH(m) ((m)->base + 0x29C) +struct bwmon_spec { + bool wrap_on_thres; + bool overflow; +}; + struct bwmon { - void __iomem *base; - void __iomem *global_base; - unsigned int mport; - unsigned int irq; - struct device *dev; - struct bw_hwmon hw; + void __iomem *base; + void __iomem *global_base; + unsigned int mport; + unsigned int irq; + const struct bwmon_spec *spec; + struct device *dev; + struct bw_hwmon hw; }; #define to_bwmon(ptr) container_of(ptr, struct bwmon, hw) @@ -94,7 +101,7 @@ static void mon_irq_disable(struct bwmon *m) writel_relaxed(val, MON_INT_EN(m)); } -static int mon_irq_status(struct bwmon *m) +static unsigned int mon_irq_status(struct bwmon *m) { u32 mval; @@ -103,12 +110,12 @@ static int mon_irq_status(struct bwmon *m) dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval, readl_relaxed(GLB_INT_STATUS(m))); - return mval & 0x1; + return mval; } static void mon_irq_clear(struct bwmon *m) { - writel_relaxed(0x1, MON_INT_CLR(m)); + writel_relaxed(0x3, MON_INT_CLR(m)); /* Ensure the monitor IRQ is clear before clearing GLB IRQ */ mb(); writel_relaxed(1 << m->mport, GLB_INT_CLR(m)); @@ -127,14 +134,22 @@ static u32 mon_get_limit(struct bwmon *m) return readl_relaxed(MON_THRES(m)); } +#define THRES_HIT(status) (status & BIT(0)) +#define OVERFLOW(status) (status & BIT(1)) static unsigned long mon_get_count(struct bwmon *m) { - unsigned long count; + unsigned long count, status; count = readl_relaxed(MON_CNT(m)); + status = mon_irq_status(m); + dev_dbg(m->dev, "Counter: %08lx\n", count); - if (mon_irq_status(m)) + + if (OVERFLOW(status) && m->spec->overflow) + count += 0xFFFFFFFF; + if (THRES_HIT(status) && m->spec->wrap_on_thres) count += mon_get_limit(m); + dev_dbg(m->dev, "Actual Count: %08lx\n", count); return count; @@ -173,11 +188,17 @@ static unsigned long meas_bw_and_set_irq(struct bw_hwmon *hw, mbps = mon_get_count(m); mbps = bytes_to_mbps(mbps, us); + /* - * The fudging of mbps when calculating limit is to workaround a HW - * design issue. Needs further tuning. + * If the counter wraps on thres, don't set the thres too low. + * Setting it too low runs the risk of the counter wrapping around + * multiple times before the IRQ is processed. */ - limit = mbps_to_bytes(max(mbps, 400UL), sample_ms, tol); + if (likely(!m->spec->wrap_on_thres)) + limit = mbps_to_bytes(mbps, sample_ms, tol); + else + limit = mbps_to_bytes(max(mbps, 400UL), sample_ms, tol); + mon_set_limit(m, limit); mon_clear(m); @@ -273,11 +294,23 @@ static int resume_bw_hwmon(struct bw_hwmon *hw) /*************************************************************************/ +static const struct bwmon_spec spec[] = { + { .wrap_on_thres = true, .overflow = false }, + { .wrap_on_thres = false, .overflow = true }, +}; + +static const struct of_device_id bimc_bwmon_match_table[] = { + { .compatible = "qcom,bimc-bwmon", .data = &spec[0] }, + { .compatible = "qcom,bimc-bwmon2", .data = &spec[1] }, + {} +}; + static int bimc_bwmon_driver_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct resource *res; struct bwmon *m; + const struct of_device_id *id; int ret; u32 data; @@ -293,6 +326,13 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) } m->mport = data; + id = of_match_device(bimc_bwmon_match_table, dev); + if (!id) { + dev_err(dev, "Unknown device type!\n"); + return -ENODEV; + } + m->spec = id->data; + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); if (!res) { dev_err(dev, "base not found!\n"); @@ -339,11 +379,6 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) return 0; } -static const struct of_device_id bimc_bwmon_match_table[] = { - { .compatible = "qcom,bimc-bwmon" }, - {} -}; - static struct platform_driver bimc_bwmon_driver = { .probe = bimc_bwmon_driver_probe, .driver = {