From c36f6302dc08e5152b6afcfeb4e75a914d8166b1 Mon Sep 17 00:00:00 2001 From: Rohit Gupta Date: Fri, 8 May 2015 12:04:56 -0700 Subject: 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 [junjiew@codeaurora.org: dropped changes in dtsi.] Signed-off-by: Junjie Wu --- drivers/devfreq/bimc-bwmon.c | 40 +++++++++++++++++++++++++++++++---- drivers/devfreq/governor_bw_hwmon.c | 42 +++++++++++++++++++++++++++++++++++++ drivers/devfreq/governor_bw_hwmon.h | 4 ++++ 3 files changed, 82 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/devfreq/bimc-bwmon.c b/drivers/devfreq/bimc-bwmon.c index d4a56c98cb4c..2b0bacdb5f6a 100644 --- a/drivers/devfreq/bimc-bwmon.c +++ b/drivers/devfreq/bimc-bwmon.c @@ -43,6 +43,7 @@ struct bwmon_spec { bool wrap_on_thres; bool overflow; + bool throt_adj; }; struct bwmon { @@ -53,19 +54,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 @@ -145,6 +151,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)); @@ -324,13 +350,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 struct of_device_id match_table[] = { { .compatible = "qcom,bimc-bwmon", .data = &spec[0] }, { .compatible = "qcom,bimc-bwmon2", .data = &spec[1] }, + { .compatible = "qcom,bimc-bwmon3", .data = &spec[2] }, {} }; @@ -399,6 +427,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) { diff --git a/drivers/devfreq/governor_bw_hwmon.c b/drivers/devfreq/governor_bw_hwmon.c index b369fed1a821..05198a17ad5f 100644 --- a/drivers/devfreq/governor_bw_hwmon.c +++ b/drivers/devfreq/governor_bw_hwmon.c @@ -673,6 +673,47 @@ static int devfreq_bw_hwmon_get_freq(struct devfreq *df, return 0; } +static ssize_t store_throttle_adj(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 -ENOSYS; + + ret = kstrtouint(buf, 10, &val); + if (ret) + return ret; + + ret = node->hw->set_throttle_adj(node->hw, val); + + if (!ret) + return count; + else + return ret; +} + +static ssize_t show_throttle_adj(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(throttle_adj, 0644, show_throttle_adj, + store_throttle_adj); + gov_attr(guard_band_mbps, 0U, 2000U); gov_attr(decay_rate, 0U, 100U); gov_attr(io_percent, 1U, 100U); @@ -709,6 +750,7 @@ static struct attribute *dev_attr[] = { &dev_attr_low_power_io_percent.attr, &dev_attr_low_power_delay.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 8e60f9aeceb0..dcae1d6cbeb2 100644 --- a/drivers/devfreq/governor_bw_hwmon.h +++ b/drivers/devfreq/governor_bw_hwmon.h @@ -24,6 +24,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 @@ -47,6 +49,8 @@ struct bw_hwmon { int (*resume_hwmon)(struct bw_hwmon *hw); 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; -- cgit v1.2.3