diff --git a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt index 6bed78506108..d96c174d2581 100644 --- a/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt +++ b/Documentation/devicetree/bindings/devfreq/bimc-bwmon.txt @@ -5,7 +5,8 @@ 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", "qcom,bimc-bwmon2" +- compatible: Must be "qcom,bimc-bwmon", "qcom,bimc-bwmon2" or + "qcom,bimc-bwmon3" - 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 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;