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>
diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
index 55d06b2..1a357b1 100644
--- a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
+++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt
@@ -28,6 +28,14 @@
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 @@
- 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:
diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c
index 61c0ae8..ad0be45 100644
--- a/drivers/cpuidle/lpm-levels-of.c
+++ b/drivers/cpuidle/lpm-levels-of.c
@@ -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 @@
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 @@
{
int ret = 0;
struct kernel_param kp;
+ struct lpm_level_avail *avail = get_avail_ptr(kobj, attr);
- kp.arg = get_enabled_ptr(attr, 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;
+
ret = param_get_bool(buf, &kp);
if (ret > 0) {
strlcat(buf, "\n", PAGE_SIZE);
@@ -453,6 +460,17 @@
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 @@
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);
diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c
index 1db0c74..69ac505 100644
--- a/drivers/cpuidle/lpm-levels.c
+++ b/drivers/cpuidle/lpm-levels.c
@@ -92,15 +92,6 @@
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 @@
* 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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
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 @@
&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 @@
{
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 @@
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();
}
diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h
index 1ff69c8..0b598c0 100644
--- a/drivers/cpuidle/lpm-levels.h
+++ b/drivers/cpuidle/lpm-levels.h
@@ -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 @@
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 @@
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;