Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit fbcacdf2 authored by Raghavendra Kakarla's avatar Raghavendra Kakarla
Browse files

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: default avatarRaghavendra Kakarla <rkakarla@codeaurora.org>
parent 6657c2cf
Loading
Loading
Loading
Loading
+24 −2
Original line number Diff line number Diff line
@@ -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:
+44 −6
Original line number Diff line number Diff line
/* 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);
+21 −25
Original line number Diff line number Diff line
@@ -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 @@ static uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev,
	 * 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 @@ static uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev,
					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 uint64_t lpm_cpuidle_predict(struct cpuidle_device *dev,
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 @@ static int lpm_cpuidle_enter(struct cpuidle_device *dev,
	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 −1
Original line number Diff line number Diff line
/* 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;