From 4fe3e2f24216428853b043f07641659d91684e98 Mon Sep 17 00:00:00 2001 From: Rohit Gupta Date: Wed, 16 Jan 2019 18:56:01 -0800 Subject: [PATCH] PM / devfreq: bw_hwmon: Expose a throttle adjust tunable Newer versions of bimc-bwmon counters have the capability to fake higher byte count than what's actually transferred between a bus master and DDR if the bus master is being throttled by QoS hardware logic. Add support to set the throttle adjust field that comes with this newer version of bimc-bwmon. Change-Id: I33376c825fb11ab2e378f828b1d2ae46dd582836 Signed-off-by: Rohit Gupta [aparnam@codeaurora.org: Renamed throttle_adj functions] Signed-off-by: Rama Aparna Mallavarapu [avajid@codeaurora.org: resolved minor merge conflicts made minor styling changes] Signed-off-by: Amir Vajid --- drivers/devfreq/bimc-bwmon.c | 40 +++++++++++++++++++++++++--- drivers/devfreq/governor_bw_hwmon.c | 41 +++++++++++++++++++++++++++++ drivers/devfreq/governor_bw_hwmon.h | 5 ++++ 3 files changed, 82 insertions(+), 4 deletions(-) diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c index 05e0ce1715b5..39040fac3171 100644 --- a/drivers/devfreq/bimc-bwmon.c +++ b/drivers/devfreq/bimc-bwmon.c @@ -35,6 +35,7 @@ struct bwmon_spec { bool wrap_on_thres; bool overflow; + bool throt_adj; }; struct bwmon { @@ -45,19 +46,24 @@ struct bwmon { const struct bwmon_spec *spec; struct device *dev; struct bw_hwmon hw; + u32 throttle_adj; }; #define to_bwmon(ptr) container_of(ptr, struct bwmon, hw) +#define ENABLE_MASK BIT(0) +#define THROTTLE_MASK 0x1F +#define THROTTLE_SHIFT 16 + static DEFINE_SPINLOCK(glb_lock); static void mon_enable(struct bwmon *m) { - writel_relaxed(0x1, MON_EN(m)); + writel_relaxed((ENABLE_MASK | m->throttle_adj), MON_EN(m)); } static void mon_disable(struct bwmon *m) { - writel_relaxed(0x0, MON_EN(m)); + writel_relaxed(m->throttle_adj, MON_EN(m)); /* * mon_disable() and mon_irq_clear(), * If latter goes first and count happen to trigger irq, we would @@ -139,6 +145,26 @@ static void mon_irq_clear(struct bwmon *m) mb(); } +static int mon_set_throttle_adj(struct bw_hwmon *hw, uint adj) +{ + struct bwmon *m = to_bwmon(hw); + + if (adj > THROTTLE_MASK) + return -EINVAL; + + adj = (adj & THROTTLE_MASK) << THROTTLE_SHIFT; + m->throttle_adj = adj; + + return 0; +} + +static u32 mon_get_throttle_adj(struct bw_hwmon *hw) +{ + struct bwmon *m = to_bwmon(hw); + + return m->throttle_adj >> THROTTLE_SHIFT; +} + static void mon_set_limit(struct bwmon *m, u32 count) { writel_relaxed(count, MON_THRES(m)); @@ -318,13 +344,15 @@ 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 }, + { .wrap_on_thres = true, .overflow = false, .throt_adj = false}, + { .wrap_on_thres = false, .overflow = true, .throt_adj = false}, + { .wrap_on_thres = false, .overflow = true, .throt_adj = 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] }, + { .compatible = "qcom,bimc-bwmon3", .data = &spec[2] }, {} }; @@ -393,6 +421,10 @@ static int bimc_bwmon_driver_probe(struct platform_device *pdev) m->hw.resume_hwmon = &resume_bw_hwmon; m->hw.get_bytes_and_clear = &get_bytes_and_clear; m->hw.set_thres = &set_thres; + if (m->spec->throt_adj) { + m->hw.set_throttle_adj = &mon_set_throttle_adj; + m->hw.get_throttle_adj = &mon_get_throttle_adj; + } ret = register_bw_hwmon(dev, &m->hw); if (ret < 0) { diff --git a/drivers/devfreq/governor_bw_hwmon.c b/drivers/devfreq/governor_bw_hwmon.c index 139923a5a7cb..56906c055981 100644 --- a/drivers/devfreq/governor_bw_hwmon.c +++ b/drivers/devfreq/governor_bw_hwmon.c @@ -643,6 +643,46 @@ static int devfreq_bw_hwmon_get_freq(struct devfreq *df, return 0; } +static ssize_t throttle_adj_store(struct device *dev, + struct device_attribute *attr, const char *buf, size_t count) +{ + struct devfreq *df = to_devfreq(dev); + struct hwmon_node *node = df->data; + int ret; + unsigned int val; + + if (!node->hw->set_throttle_adj) + return -EPERM; + + ret = kstrtouint(buf, 10, &val); + if (ret < 0) + return ret; + + ret = node->hw->set_throttle_adj(node->hw, val); + + if (!ret) + return count; + else + return ret; +} + +static ssize_t throttle_adj_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct devfreq *df = to_devfreq(dev); + struct hwmon_node *node = df->data; + unsigned int val; + + if (!node->hw->get_throttle_adj) + val = 0; + else + val = node->hw->get_throttle_adj(node->hw); + + return snprintf(buf, PAGE_SIZE, "%u\n", val); +} + +static DEVICE_ATTR_RW(throttle_adj); + show_attr(guard_band_mbps); store_attr(guard_band_mbps, 0U, 2000U); static DEVICE_ATTR_RW(guard_band_mbps); @@ -701,6 +741,7 @@ static struct attribute *dev_attr[] = { &dev_attr_hyst_length.attr, &dev_attr_idle_mbps.attr, &dev_attr_mbps_zones.attr, + &dev_attr_throttle_adj.attr, NULL, }; diff --git a/drivers/devfreq/governor_bw_hwmon.h b/drivers/devfreq/governor_bw_hwmon.h index 4b9c19bfd5b0..832081f41f70 100644 --- a/drivers/devfreq/governor_bw_hwmon.h +++ b/drivers/devfreq/governor_bw_hwmon.h @@ -16,6 +16,8 @@ * @set_thres: Set the count threshold to generate an IRQ * @get_bytes_and_clear: Get the bytes transferred since the last call * and reset the counter to start over. + * @set_throttle_adj: Set throttle adjust field to the given value + * @get_throttle_adj: Get the value written to throttle adjust field * @dev: Pointer to device that this HW monitor can * monitor. * @of_node: OF node of device that this HW monitor can @@ -41,6 +43,9 @@ struct bw_hwmon { unsigned long (*set_thres)(struct bw_hwmon *hw, unsigned long bytes); unsigned long (*get_bytes_and_clear)(struct bw_hwmon *hw); + int (*set_throttle_adj)(struct bw_hwmon *hw, + uint adj); + u32 (*get_throttle_adj)(struct bw_hwmon *hw); struct device *dev; struct device_node *of_node; struct devfreq_governor *gov;