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>
tirimbino
Raghavendra Kakarla 7 years ago committed by Gerrit - the friendly Code Review server
parent f6328cb773
commit c8b50d264c
  1. 26
      Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
  2. 48
      drivers/cpuidle/lpm-levels-of.c
  3. 46
      drivers/cpuidle/lpm-levels.c
  4. 14
      drivers/cpuidle/lpm-levels.h

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

@ -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);
@ -450,6 +457,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;
@ -711,8 +729,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);

@ -89,15 +89,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);
@ -493,7 +484,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)) {
@ -518,7 +509,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++) {
@ -542,8 +533,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) {
@ -558,8 +550,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) {
@ -678,8 +671,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];
@ -742,14 +735,14 @@ static uint64_t get_cluster_sleep_time(struct lpm_cluster *cluster,
if (*next_event_c < next_event)
next_event = *next_event_c;
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());
}
@ -768,7 +761,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) {
@ -843,7 +836,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)) {
@ -904,7 +897,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);
@ -1041,7 +1034,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()));
}
@ -1063,7 +1056,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;
@ -1117,7 +1111,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;
}
@ -1332,8 +1327,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) {
@ -1391,7 +1387,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();
}

@ -15,6 +15,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 */
@ -42,6 +51,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;
@ -96,6 +108,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…
Cancel
Save