cpuidle: lpm-levels: Add support to parse LPM parameters
Add support for providing LPM prediction parameters in device tree and parsing them. Change-Id: I60fd7e8d32505e212100b886df29b72a9999ca6d Signed-off-by: Raghavendra Kakarla <rkakarla@codeaurora.org>
This commit is contained in:
parent
6657c2cf20
commit
fbcacdf2d5
4 changed files with 104 additions and 34 deletions
|
|
@ -28,6 +28,14 @@ Required properties:
|
|||
mask of the cluster mode in the composite state ID used to define
|
||||
cluster low power modes in PSCI.
|
||||
|
||||
Optional properties:
|
||||
- qcom,disable-prediction: This property is used to indicate the LPM
|
||||
governor will not use LPM prediction for this cluster.
|
||||
- qcom,clstr-tmr-add: This property is used as correction timer for
|
||||
wrong prediction by lpm prediction algorithm for cluster predictions.
|
||||
This value should be between 100 to 1500. Higher values would mean
|
||||
longer time staying in shallower state before waking up to select a
|
||||
deeper state in case of wrong prediction.
|
||||
qcom,pm-cluster contains qcom,pm-cluster-level nodes which identify
|
||||
the various low power modes that the cluster can enter. The
|
||||
qcom,pm-cluster node should also include another cluster node or a cpu
|
||||
|
|
@ -77,8 +85,22 @@ that share the parameters.It contains the following properties.
|
|||
- qcom,pm-cpu-levels: The different low power modes that a CPU could
|
||||
enter. The following section explains the required properties of this
|
||||
node.
|
||||
-qcom,use-prediction: This optional property is used to indicate the
|
||||
the LPM governor is to apply sleep prediction to this cluster.
|
||||
|
||||
Optional properties:
|
||||
- qcom,disable-prediction: This property is used to indicate the
|
||||
LPM governor is to disable sleep prediction to this cpu.
|
||||
- qcom,ref-stddev: This property is used as reference standard deviation
|
||||
in lpm prediction algorithm. This value should be between 100 to 1000.
|
||||
Higher value would result in more predictions and thereby resulting in
|
||||
shallower low power modes.
|
||||
- qcom,tmr-add: This property is used as correction timer for wrong
|
||||
prediction by lpm prediction algorithm. This value should be between
|
||||
100 to 1500. Higher values would mean longer time staying in shallower
|
||||
state before waking up to select a deeper state in case of wrong prediction.
|
||||
- qcom,ref-premature-cnt: This property is used as reference premature
|
||||
count to predict next sleep state by the prediction algorithm. This value
|
||||
should be between 1 to 5. Higher value for this parameter would result in
|
||||
less predictions to disallow deeper low power modes.
|
||||
|
||||
[Node bindings for qcom,pm-cpu-levels]
|
||||
Required properties:
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
|
@ -178,8 +178,8 @@ static ssize_t lpm_latency_show(struct kobject *kobj,
|
|||
struct kernel_param kp;
|
||||
struct lpm_level_avail *avail = get_avail_ptr(kobj, attr);
|
||||
|
||||
if (!avail)
|
||||
pr_info("Error\n");
|
||||
if (WARN_ON(!avail))
|
||||
return -EINVAL;
|
||||
|
||||
kp.arg = &avail->latency_us;
|
||||
|
||||
|
|
@ -197,8 +197,15 @@ ssize_t lpm_enable_show(struct kobject *kobj, struct kobj_attribute *attr,
|
|||
{
|
||||
int ret = 0;
|
||||
struct kernel_param kp;
|
||||
struct lpm_level_avail *avail = get_avail_ptr(kobj, attr);
|
||||
|
||||
if (WARN_ON(!avail))
|
||||
return -EINVAL;
|
||||
|
||||
kp.arg = get_enabled_ptr(attr, avail);
|
||||
if (WARN_ON(!kp.arg))
|
||||
return -EINVAL;
|
||||
|
||||
kp.arg = get_enabled_ptr(attr, get_avail_ptr(kobj, attr));
|
||||
ret = param_get_bool(buf, &kp);
|
||||
if (ret > 0) {
|
||||
strlcat(buf, "\n", PAGE_SIZE);
|
||||
|
|
@ -453,6 +460,17 @@ static int parse_cluster_params(struct device_node *node,
|
|||
if (ret)
|
||||
goto fail;
|
||||
|
||||
key = "qcom,disable-prediction";
|
||||
c->lpm_prediction = !(of_property_read_bool(node, key));
|
||||
|
||||
if (c->lpm_prediction) {
|
||||
key = "qcom,clstr-tmr-add";
|
||||
ret = of_property_read_u32(node, key, &c->tmr_add);
|
||||
if (ret || c->tmr_add < TIMER_ADD_LOW ||
|
||||
c->tmr_add > TIMER_ADD_HIGH)
|
||||
c->tmr_add = DEFAULT_TIMER_ADD;
|
||||
}
|
||||
|
||||
/* Set default_level to 0 as default */
|
||||
c->default_level = 0;
|
||||
|
||||
|
|
@ -713,8 +731,28 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
|
|||
if (ret)
|
||||
goto failed_parse_params;
|
||||
|
||||
key = "qcom,use-prediction";
|
||||
cpu->lpm_prediction = of_property_read_bool(node, key);
|
||||
key = "qcom,disable-prediction";
|
||||
cpu->lpm_prediction = !(of_property_read_bool(node, key));
|
||||
|
||||
if (cpu->lpm_prediction) {
|
||||
key = "qcom,ref-stddev";
|
||||
ret = of_property_read_u32(node, key, &cpu->ref_stddev);
|
||||
if (ret || cpu->ref_stddev < STDDEV_LOW ||
|
||||
cpu->ref_stddev > STDDEV_HIGH)
|
||||
cpu->ref_stddev = DEFAULT_STDDEV;
|
||||
|
||||
key = "qcom,tmr-add";
|
||||
ret = of_property_read_u32(node, key, &cpu->tmr_add);
|
||||
if (ret || cpu->tmr_add < TIMER_ADD_LOW ||
|
||||
cpu->tmr_add > TIMER_ADD_HIGH)
|
||||
cpu->tmr_add = DEFAULT_TIMER_ADD;
|
||||
|
||||
key = "qcom,ref-premature-cnt";
|
||||
ret = of_property_read_u32(node, key, &cpu->ref_premature_cnt);
|
||||
if (ret || cpu->ref_premature_cnt < PREMATURE_CNT_LOW ||
|
||||
cpu->ref_premature_cnt > PREMATURE_CNT_HIGH)
|
||||
cpu->ref_premature_cnt = DEFAULT_PREMATURE_CNT;
|
||||
}
|
||||
|
||||
key = "parse_cpu";
|
||||
ret = parse_cpu(node, cpu);
|
||||
|
|
|
|||
|
|
@ -92,15 +92,6 @@ struct lpm_cluster *lpm_root_node;
|
|||
static bool lpm_prediction = true;
|
||||
module_param_named(lpm_prediction, lpm_prediction, bool, 0664);
|
||||
|
||||
static uint32_t ref_stddev = 500;
|
||||
module_param_named(ref_stddev, ref_stddev, uint, 0664);
|
||||
|
||||
static uint32_t tmr_add = 1000;
|
||||
module_param_named(tmr_add, tmr_add, uint, 0664);
|
||||
|
||||
static uint32_t ref_premature_cnt = 1;
|
||||
module_param_named(ref_premature_cnt, ref_premature_cnt, uint, 0664);
|
||||
|
||||
static uint32_t bias_hyst;
|
||||
module_param_named(bias_hyst, bias_hyst, uint, 0664);
|
||||
|
||||
|
|
@ -496,7 +487,7 @@ again:
|
|||
* ignore one maximum sample and retry
|
||||
*/
|
||||
if (((avg > stddev * 6) && (divisor >= (MAXSAMPLES - 1)))
|
||||
|| stddev <= ref_stddev) {
|
||||
|| stddev <= cpu->ref_stddev) {
|
||||
history->stime = ktime_to_us(ktime_get()) + avg;
|
||||
return avg;
|
||||
} else if (divisor > (MAXSAMPLES - 1)) {
|
||||
|
|
@ -521,7 +512,7 @@ again:
|
|||
total += history->resi[i];
|
||||
}
|
||||
}
|
||||
if (failed >= ref_premature_cnt) {
|
||||
if (failed >= cpu->ref_premature_cnt) {
|
||||
*idx_restrict = j;
|
||||
do_div(total, failed);
|
||||
for (i = 0; i < j; i++) {
|
||||
|
|
@ -545,8 +536,9 @@ again:
|
|||
static inline void invalidate_predict_history(struct cpuidle_device *dev)
|
||||
{
|
||||
struct lpm_history *history = &per_cpu(hist, dev->cpu);
|
||||
struct lpm_cpu *lpm_cpu = per_cpu(cpu_lpm, dev->cpu);
|
||||
|
||||
if (!lpm_prediction)
|
||||
if (!lpm_prediction || !lpm_cpu->lpm_prediction)
|
||||
return;
|
||||
|
||||
if (history->hinvalid) {
|
||||
|
|
@ -561,8 +553,9 @@ static void clear_predict_history(void)
|
|||
struct lpm_history *history;
|
||||
int i;
|
||||
unsigned int cpu;
|
||||
struct lpm_cpu *lpm_cpu = per_cpu(cpu_lpm, raw_smp_processor_id());
|
||||
|
||||
if (!lpm_prediction)
|
||||
if (!lpm_prediction || !lpm_cpu->lpm_prediction)
|
||||
return;
|
||||
|
||||
for_each_possible_cpu(cpu) {
|
||||
|
|
@ -681,8 +674,8 @@ static int cpu_power_select(struct cpuidle_device *dev,
|
|||
if ((predicted || (idx_restrict != (cpu->nlevels + 1)))
|
||||
&& ((best_level >= 0)
|
||||
&& (best_level < (cpu->nlevels-1)))) {
|
||||
htime = predicted + tmr_add;
|
||||
if (htime == tmr_add)
|
||||
htime = predicted + cpu->tmr_add;
|
||||
if (htime == cpu->tmr_add)
|
||||
htime = idx_restrict_time;
|
||||
else if (htime > max_residency[best_level])
|
||||
htime = max_residency[best_level];
|
||||
|
|
@ -746,14 +739,14 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
|
|||
next_event.tv64 = next_event_c->tv64;
|
||||
}
|
||||
|
||||
if (from_idle && lpm_prediction) {
|
||||
if (from_idle && lpm_prediction && cluster->lpm_prediction) {
|
||||
history = &per_cpu(hist, cpu);
|
||||
if (history->stime && (history->stime < prediction))
|
||||
prediction = history->stime;
|
||||
}
|
||||
}
|
||||
|
||||
if (from_idle && lpm_prediction) {
|
||||
if (from_idle && lpm_prediction && cluster->lpm_prediction) {
|
||||
if (prediction > ktime_to_us(ktime_get()))
|
||||
*pred_time = prediction - ktime_to_us(ktime_get());
|
||||
}
|
||||
|
|
@ -772,7 +765,7 @@ static int cluster_predict(struct lpm_cluster *cluster,
|
|||
struct cluster_history *history = &cluster->history;
|
||||
int64_t cur_time = ktime_to_us(ktime_get());
|
||||
|
||||
if (!lpm_prediction)
|
||||
if (!lpm_prediction || !cluster->lpm_prediction)
|
||||
return 0;
|
||||
|
||||
if (history->hinvalid) {
|
||||
|
|
@ -847,7 +840,7 @@ static void update_cluster_history(struct cluster_history *history, int idx)
|
|||
struct lpm_cluster *cluster =
|
||||
container_of(history, struct lpm_cluster, history);
|
||||
|
||||
if (!lpm_prediction)
|
||||
if (!lpm_prediction || !cluster->lpm_prediction)
|
||||
return;
|
||||
|
||||
if ((history->entry_idx == -1) || (history->entry_idx == idx)) {
|
||||
|
|
@ -908,7 +901,7 @@ static void clear_cl_predict_history(void)
|
|||
struct lpm_cluster *cluster = lpm_root_node;
|
||||
struct list_head *list;
|
||||
|
||||
if (!lpm_prediction)
|
||||
if (!lpm_prediction || !cluster->lpm_prediction)
|
||||
return;
|
||||
|
||||
clear_cl_history_each(&cluster->history);
|
||||
|
|
@ -1034,7 +1027,7 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
|
|||
cluster->child_cpus.bits[0], from_idle);
|
||||
lpm_stats_cluster_enter(cluster->stats, idx);
|
||||
|
||||
if (from_idle && lpm_prediction)
|
||||
if (from_idle && lpm_prediction && cluster->lpm_prediction)
|
||||
update_cluster_history_time(&cluster->history, idx,
|
||||
ktime_to_us(ktime_get()));
|
||||
}
|
||||
|
|
@ -1066,7 +1059,8 @@ static int cluster_configure(struct lpm_cluster *cluster, int idx,
|
|||
if (predicted && (idx < (cluster->nlevels - 1))) {
|
||||
struct power_params *pwr_params = &cluster->levels[idx].pwr;
|
||||
|
||||
clusttimer_start(cluster, pwr_params->max_residency + tmr_add);
|
||||
clusttimer_start(cluster, pwr_params->max_residency +
|
||||
cluster->tmr_add);
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -1120,7 +1114,8 @@ static void cluster_prepare(struct lpm_cluster *cluster,
|
|||
&cluster->levels[0].pwr;
|
||||
|
||||
clusttimer_start(cluster,
|
||||
pwr_params->max_residency + tmr_add);
|
||||
pwr_params->max_residency +
|
||||
cluster->tmr_add);
|
||||
|
||||
goto failed;
|
||||
}
|
||||
|
|
@ -1335,8 +1330,9 @@ static void update_history(struct cpuidle_device *dev, int idx)
|
|||
{
|
||||
struct lpm_history *history = &per_cpu(hist, dev->cpu);
|
||||
uint32_t tmr = 0;
|
||||
struct lpm_cpu *lpm_cpu = per_cpu(cpu_lpm, dev->cpu);
|
||||
|
||||
if (!lpm_prediction)
|
||||
if (!lpm_prediction || !lpm_cpu->lpm_prediction)
|
||||
return;
|
||||
|
||||
if (history->htmr_wkup) {
|
||||
|
|
@ -1394,7 +1390,7 @@ exit:
|
|||
update_history(dev, idx);
|
||||
trace_cpu_idle_exit(idx, success);
|
||||
local_irq_enable();
|
||||
if (lpm_prediction) {
|
||||
if (lpm_prediction && cpu->lpm_prediction) {
|
||||
histtimer_cancel();
|
||||
clusttimer_cancel();
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
/* Copyright (c) 2014-2017, The Linux Foundation. All rights reserved.
|
||||
/* Copyright (c) 2014-2018, The Linux Foundation. All rights reserved.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 and
|
||||
|
|
@ -16,6 +16,15 @@
|
|||
#define NR_LPM_LEVELS 8
|
||||
#define MAXSAMPLES 5
|
||||
#define CLUST_SMPL_INVLD_TIME 40000
|
||||
#define DEFAULT_PREMATURE_CNT 3
|
||||
#define DEFAULT_STDDEV 100
|
||||
#define DEFAULT_TIMER_ADD 100
|
||||
#define TIMER_ADD_LOW 100
|
||||
#define TIMER_ADD_HIGH 1500
|
||||
#define STDDEV_LOW 100
|
||||
#define STDDEV_HIGH 1000
|
||||
#define PREMATURE_CNT_LOW 1
|
||||
#define PREMATURE_CNT_HIGH 5
|
||||
|
||||
struct power_params {
|
||||
uint32_t latency_us; /* Enter + Exit latency */
|
||||
|
|
@ -43,6 +52,9 @@ struct lpm_cpu {
|
|||
int nlevels;
|
||||
unsigned int psci_mode_shift;
|
||||
unsigned int psci_mode_mask;
|
||||
uint32_t ref_stddev;
|
||||
uint32_t ref_premature_cnt;
|
||||
uint32_t tmr_add;
|
||||
bool lpm_prediction;
|
||||
struct cpuidle_driver *drv;
|
||||
struct lpm_cluster *parent;
|
||||
|
|
@ -97,6 +109,8 @@ struct lpm_cluster {
|
|||
int min_child_level;
|
||||
int default_level;
|
||||
int last_level;
|
||||
uint32_t tmr_add;
|
||||
bool lpm_prediction;
|
||||
struct list_head cpu;
|
||||
spinlock_t sync_lock;
|
||||
struct cpumask child_cpus;
|
||||
|
|
|
|||
Loading…
Reference in a new issue