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;