diff options
-rw-r--r-- | Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt | 2 | ||||
-rw-r--r-- | drivers/devfreq/bimc-bwmon.c | 61 |
2 files changed, 49 insertions, 14 deletions
diff --git a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt index 69c789137a9b..6bed78506108 100644 --- a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt +++ b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt @@ -5,7 +5,7 @@ can be used to measure the bandwidth of read/write traffic from the BIMC master ports. For example, the CPU subsystem sits on one BIMC master port. Required properties: -- compatible: Must be "qcom,bimc-bwmon" +- compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2" - reg: Pairs of physical base addresses and region sizes of memory mapped registers. - reg-names: Names of the bases for the above registers. Expected diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c index eb2ef2aaaa39..e83d9bacbcd1 100644 --- a/drivers/devfreq/bimc-bwmon.c +++ b/drivers/devfreq/bimc-bwmon.c @@ -23,6 +23,7 @@ #include <linux/interrupt.h> #include <linux/platform_device.h> #include <linux/of.h> +#include <linux/of_device.h> #include <linux/spinlock.h> #include "governor_bw_hwmon.h" @@ -39,11 +40,17 @@ #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; + const struct bwmon_spec *spec; struct device *dev; struct bw_hwmon hw; }; @@ -96,7 +103,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, gval; @@ -105,12 +112,12 @@ static int mon_irq_status(struct bwmon *m) dev_dbg(m->dev, "IRQ status p:%x, g:%x\n", mval, gval); - 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)); mb(); writel_relaxed(1 << m->mport, GLB_INT_CLR(m)); mb(); @@ -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); @@ -265,11 +286,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 struct of_device_id 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; @@ -285,6 +318,13 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) } m->mport = data; + id = of_match_device(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"); @@ -331,11 +371,6 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) return 0; } -static struct of_device_id match_table[] = { - { .compatible = "qcom,bimc-bwmon" }, - {} -}; - static struct platform_driver bimc_bwmon_driver = { .probe = bimc_bwmon_driver_probe, .driver = { |