PM / devfreq: bw_hwmon: Add HW offload support to governor
Some HW monitors can do a better job of the sampling and the threshold checking than the SW implementation in the governor. Update the governor's API to add support for them. Change-Id: Id4b5593a5ed3290684ba43ebebe2466ba0b730b6 Signed-off-by: Saravana Kannan <skannan@codeaurora.org> Signed-off-by: David Keitel <dkeitel@codeaurora.org>
This commit is contained in:
parent
ab26d09879
commit
5599e9a931
2 changed files with 80 additions and 16 deletions
|
@ -67,8 +67,6 @@ struct hwmon_node {
|
||||||
unsigned long hyst_en;
|
unsigned long hyst_en;
|
||||||
unsigned long above_low_power;
|
unsigned long above_low_power;
|
||||||
unsigned long prev_req;
|
unsigned long prev_req;
|
||||||
unsigned long up_wake_mbps;
|
|
||||||
unsigned long down_wake_mbps;
|
|
||||||
unsigned int wake;
|
unsigned int wake;
|
||||||
unsigned int down_cnt;
|
unsigned int down_cnt;
|
||||||
ktime_t prev_ts;
|
ktime_t prev_ts;
|
||||||
|
@ -191,7 +189,7 @@ static unsigned int mbps_to_bytes(unsigned long mbps, unsigned int ms)
|
||||||
return mbps;
|
return mbps;
|
||||||
}
|
}
|
||||||
|
|
||||||
static int __bw_hwmon_sample_end(struct bw_hwmon *hwmon)
|
static int __bw_hwmon_sw_sample_end(struct bw_hwmon *hwmon)
|
||||||
{
|
{
|
||||||
struct devfreq *df;
|
struct devfreq *df;
|
||||||
struct hwmon_node *node;
|
struct hwmon_node *node;
|
||||||
|
@ -220,9 +218,9 @@ static int __bw_hwmon_sample_end(struct bw_hwmon *hwmon)
|
||||||
* bandwidth usage and do the bandwidth calculation based on just
|
* bandwidth usage and do the bandwidth calculation based on just
|
||||||
* this micro sample.
|
* this micro sample.
|
||||||
*/
|
*/
|
||||||
if (mbps > node->up_wake_mbps) {
|
if (mbps > node->hw->up_wake_mbps) {
|
||||||
wake = UP_WAKE;
|
wake = UP_WAKE;
|
||||||
} else if (mbps < node->down_wake_mbps) {
|
} else if (mbps < node->hw->down_wake_mbps) {
|
||||||
if (node->down_cnt)
|
if (node->down_cnt)
|
||||||
node->down_cnt--;
|
node->down_cnt--;
|
||||||
if (node->down_cnt <= 0)
|
if (node->down_cnt <= 0)
|
||||||
|
@ -241,6 +239,50 @@ static int __bw_hwmon_sample_end(struct bw_hwmon *hwmon)
|
||||||
return wake;
|
return wake;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static int __bw_hwmon_hw_sample_end(struct bw_hwmon *hwmon)
|
||||||
|
{
|
||||||
|
struct devfreq *df;
|
||||||
|
struct hwmon_node *node;
|
||||||
|
unsigned long bytes, mbps;
|
||||||
|
int wake = 0;
|
||||||
|
|
||||||
|
df = hwmon->df;
|
||||||
|
node = df->data;
|
||||||
|
|
||||||
|
/*
|
||||||
|
* If this read is in response to an IRQ, the HW monitor should
|
||||||
|
* return the measurement in the micro sample that triggered the IRQ.
|
||||||
|
* Otherwise, it should return the maximum measured value in any
|
||||||
|
* micro sample since the last time we called get_bytes_and_clear()
|
||||||
|
*/
|
||||||
|
bytes = hwmon->get_bytes_and_clear(hwmon);
|
||||||
|
mbps = bytes_to_mbps(bytes, node->sample_ms * USEC_PER_MSEC);
|
||||||
|
node->max_mbps = mbps;
|
||||||
|
|
||||||
|
if (mbps > node->hw->up_wake_mbps)
|
||||||
|
wake = UP_WAKE;
|
||||||
|
else if (mbps < node->hw->down_wake_mbps)
|
||||||
|
wake = DOWN_WAKE;
|
||||||
|
|
||||||
|
node->wake = wake;
|
||||||
|
node->sampled = true;
|
||||||
|
|
||||||
|
trace_bw_hwmon_meas(dev_name(df->dev.parent),
|
||||||
|
mbps,
|
||||||
|
node->sample_ms * USEC_PER_MSEC,
|
||||||
|
wake);
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static int __bw_hwmon_sample_end(struct bw_hwmon *hwmon)
|
||||||
|
{
|
||||||
|
if (hwmon->set_hw_events)
|
||||||
|
return __bw_hwmon_hw_sample_end(hwmon);
|
||||||
|
else
|
||||||
|
return __bw_hwmon_sw_sample_end(hwmon);
|
||||||
|
}
|
||||||
|
|
||||||
int bw_hwmon_sample_end(struct bw_hwmon *hwmon)
|
int bw_hwmon_sample_end(struct bw_hwmon *hwmon)
|
||||||
{
|
{
|
||||||
unsigned long flags;
|
unsigned long flags;
|
||||||
|
@ -275,12 +317,14 @@ static unsigned long get_bw_and_set_irq(struct hwmon_node *node,
|
||||||
struct bw_hwmon *hw = node->hw;
|
struct bw_hwmon *hw = node->hw;
|
||||||
unsigned int new_bw, io_percent;
|
unsigned int new_bw, io_percent;
|
||||||
ktime_t ts;
|
ktime_t ts;
|
||||||
unsigned int ms;
|
unsigned int ms = 0;
|
||||||
|
|
||||||
spin_lock_irqsave(&irq_lock, flags);
|
spin_lock_irqsave(&irq_lock, flags);
|
||||||
|
|
||||||
ts = ktime_get();
|
if (!hw->set_hw_events) {
|
||||||
ms = ktime_to_ms(ktime_sub(ts, node->prev_ts));
|
ts = ktime_get();
|
||||||
|
ms = ktime_to_ms(ktime_sub(ts, node->prev_ts));
|
||||||
|
}
|
||||||
if (!node->sampled || ms >= node->sample_ms)
|
if (!node->sampled || ms >= node->sample_ms)
|
||||||
__bw_hwmon_sample_end(node->hw);
|
__bw_hwmon_sample_end(node->hw);
|
||||||
node->sampled = false;
|
node->sampled = false;
|
||||||
|
@ -388,9 +432,10 @@ static unsigned long get_bw_and_set_irq(struct hwmon_node *node,
|
||||||
|
|
||||||
/* Stretch the short sample window size, if the traffic is too low */
|
/* Stretch the short sample window size, if the traffic is too low */
|
||||||
if (meas_mbps < MIN_MBPS) {
|
if (meas_mbps < MIN_MBPS) {
|
||||||
node->up_wake_mbps = (max(MIN_MBPS, req_mbps)
|
hw->up_wake_mbps = (max(MIN_MBPS, req_mbps)
|
||||||
* (100 + node->up_thres)) / 100;
|
* (100 + node->up_thres)) / 100;
|
||||||
node->down_wake_mbps = 0;
|
hw->down_wake_mbps = 0;
|
||||||
|
hw->undo_over_req_mbps = 0;
|
||||||
thres = mbps_to_bytes(max(MIN_MBPS, req_mbps / 2),
|
thres = mbps_to_bytes(max(MIN_MBPS, req_mbps / 2),
|
||||||
node->sample_ms);
|
node->sample_ms);
|
||||||
} else {
|
} else {
|
||||||
|
@ -401,13 +446,22 @@ static unsigned long get_bw_and_set_irq(struct hwmon_node *node,
|
||||||
* reduce the vote based on the measured mbps being less than
|
* reduce the vote based on the measured mbps being less than
|
||||||
* the previous measurement that caused the "over request".
|
* the previous measurement that caused the "over request".
|
||||||
*/
|
*/
|
||||||
node->up_wake_mbps = (req_mbps * (100 + node->up_thres)) / 100;
|
hw->up_wake_mbps = (req_mbps * (100 + node->up_thres)) / 100;
|
||||||
node->down_wake_mbps = (meas_mbps * node->down_thres) / 100;
|
hw->down_wake_mbps = (meas_mbps * node->down_thres) / 100;
|
||||||
|
if (node->wake == UP_WAKE)
|
||||||
|
hw->undo_over_req_mbps = min(req_mbps, meas_mbps_zone);
|
||||||
|
else
|
||||||
|
hw->undo_over_req_mbps = 0;
|
||||||
thres = mbps_to_bytes(meas_mbps, node->sample_ms);
|
thres = mbps_to_bytes(meas_mbps, node->sample_ms);
|
||||||
}
|
}
|
||||||
node->down_cnt = node->down_count;
|
|
||||||
|
|
||||||
node->bytes = hw->set_thres(hw, thres);
|
if (hw->set_hw_events) {
|
||||||
|
hw->down_cnt = node->down_count;
|
||||||
|
hw->set_hw_events(hw, node->sample_ms);
|
||||||
|
} else {
|
||||||
|
node->down_cnt = node->down_count;
|
||||||
|
node->bytes = hw->set_thres(hw, thres);
|
||||||
|
}
|
||||||
|
|
||||||
node->wake = 0;
|
node->wake = 0;
|
||||||
node->prev_req = req_mbps;
|
node->prev_req = req_mbps;
|
||||||
|
@ -432,8 +486,8 @@ static unsigned long get_bw_and_set_irq(struct hwmon_node *node,
|
||||||
trace_bw_hwmon_update(dev_name(node->hw->df->dev.parent),
|
trace_bw_hwmon_update(dev_name(node->hw->df->dev.parent),
|
||||||
new_bw,
|
new_bw,
|
||||||
*freq,
|
*freq,
|
||||||
node->up_wake_mbps,
|
hw->up_wake_mbps,
|
||||||
node->down_wake_mbps);
|
hw->down_wake_mbps);
|
||||||
return req_mbps;
|
return req_mbps;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -503,6 +557,9 @@ static int start_monitor(struct devfreq *df, bool init)
|
||||||
node->resume_freq = 0;
|
node->resume_freq = 0;
|
||||||
node->resume_ab = 0;
|
node->resume_ab = 0;
|
||||||
mbps = (df->previous_freq * node->io_percent) / 100;
|
mbps = (df->previous_freq * node->io_percent) / 100;
|
||||||
|
hw->up_wake_mbps = mbps;
|
||||||
|
hw->down_wake_mbps = MIN_MBPS;
|
||||||
|
hw->undo_over_req_mbps = 0;
|
||||||
ret = hw->start_hwmon(hw, mbps);
|
ret = hw->start_hwmon(hw, mbps);
|
||||||
} else {
|
} else {
|
||||||
ret = hw->resume_hwmon(hw);
|
ret = hw->resume_hwmon(hw);
|
||||||
|
|
|
@ -48,6 +48,8 @@ struct bw_hwmon {
|
||||||
int (*suspend_hwmon)(struct bw_hwmon *hw);
|
int (*suspend_hwmon)(struct bw_hwmon *hw);
|
||||||
int (*resume_hwmon)(struct bw_hwmon *hw);
|
int (*resume_hwmon)(struct bw_hwmon *hw);
|
||||||
unsigned long (*set_thres)(struct bw_hwmon *hw, unsigned long bytes);
|
unsigned long (*set_thres)(struct bw_hwmon *hw, unsigned long bytes);
|
||||||
|
unsigned long (*set_hw_events)(struct bw_hwmon *hw,
|
||||||
|
unsigned int sample_ms);
|
||||||
unsigned long (*get_bytes_and_clear)(struct bw_hwmon *hw);
|
unsigned long (*get_bytes_and_clear)(struct bw_hwmon *hw);
|
||||||
int (*set_throttle_adj)(struct bw_hwmon *hw, uint adj);
|
int (*set_throttle_adj)(struct bw_hwmon *hw, uint adj);
|
||||||
u32 (*get_throttle_adj)(struct bw_hwmon *hw);
|
u32 (*get_throttle_adj)(struct bw_hwmon *hw);
|
||||||
|
@ -55,6 +57,11 @@ struct bw_hwmon {
|
||||||
struct device_node *of_node;
|
struct device_node *of_node;
|
||||||
struct devfreq_governor *gov;
|
struct devfreq_governor *gov;
|
||||||
|
|
||||||
|
unsigned long up_wake_mbps;
|
||||||
|
unsigned long undo_over_req_mbps;
|
||||||
|
unsigned long down_wake_mbps;
|
||||||
|
unsigned int down_cnt;
|
||||||
|
|
||||||
struct devfreq *df;
|
struct devfreq *df;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Add table
Reference in a new issue