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 <rohgup@codeaurora.org>
[junjiew@codeaurora.org: dropped changes in dtsi.]
Signed-off-by: Junjie Wu <junjiew@codeaurora.org>
This commit is contained in:
Rohit Gupta 2015-05-08 12:04:56 -07:00 committed by David Keitel
parent e006885fd2
commit c36f6302dc
4 changed files with 84 additions and 5 deletions

View file

@ -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

View file

@ -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) {

View file

@ -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,
};

View file

@ -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;