diff --git a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt index 55d06b25b2de..1a357b10418d 100644 --- a/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt +++ b/Documentation/devicetree/bindings/arm/msm/lpm-levels.txt @@ -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: diff --git a/drivers/cpuidle/lpm-levels-of.c b/drivers/cpuidle/lpm-levels-of.c index 030aacb7cba8..80d89e9d2083 100644 --- a/drivers/cpuidle/lpm-levels-of.c +++ b/drivers/cpuidle/lpm-levels-of.c @@ -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); diff --git a/drivers/cpuidle/lpm-levels.c b/drivers/cpuidle/lpm-levels.c index b9ab79165369..bb4dd7891138 100644 --- a/drivers/cpuidle/lpm-levels.c +++ b/drivers/cpuidle/lpm-levels.c @@ -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(); } diff --git a/drivers/cpuidle/lpm-levels.h b/drivers/cpuidle/lpm-levels.h index 8c66bd73402c..a6c7c5b5986c 100644 --- a/drivers/cpuidle/lpm-levels.h +++ b/drivers/cpuidle/lpm-levels.h @@ -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;