lpm-levels: Use minimum residency values for mode selection

The low power mode selection logic has problems while
selecting best mode both in cpu and cluster selection.
When latency requirement is not met, the level selection
loop breaks without selecting another possible shallow
power mode that meets both latency and residency
requirements. The residencies are initialized to ~0
(0xFFFFFFFF) when a particular mode is disabled in idle
context. This results in wrong selection of shallower
mode in suspend context instead of selecting best possible
deepest low power mode (assuming the mode is enabled in
suspend context but disabled in idle context). This can
have impact on power.

The change addresses this by selecting a possible
shallower power mode that can meet the latency criteria.
It also initializes the residencies to 0 when a mode is
not allowed in idle context. This results in selection
of deepest low power mode in suspend context and
saves power.

Change-Id: I90a3496026f92522544a0abd7ffeeeb88f1732f6
Signed-off-by: Raju P.L.S.S.S.N <rplsssn@codeaurora.org>
This commit is contained in:
Raju P.L.S.S.S.N 2016-06-15 20:14:02 +05:30 committed by Srinivas Rao L
parent 285cdfb61b
commit cec5c130c6
3 changed files with 68 additions and 17 deletions

View File

@ -39,6 +39,7 @@ static const struct lpm_type_str lpm_types[] = {
};
static DEFINE_PER_CPU(uint32_t *, max_residency);
static DEFINE_PER_CPU(uint32_t *, min_residency);
static struct lpm_level_avail *cpu_level_available[NR_CPUS];
static struct platform_device *lpm_pdev;
@ -75,20 +76,37 @@ static void set_optimum_cpu_residency(struct lpm_cpu *cpu, int cpu_id,
{
int i, j;
bool mode_avail;
uint32_t *residency = per_cpu(max_residency, cpu_id);
uint32_t *maximum_residency = per_cpu(max_residency, cpu_id);
uint32_t *minimum_residency = per_cpu(min_residency, cpu_id);
for (i = 0; i < cpu->nlevels; i++) {
struct power_params *pwr = &cpu->levels[i].pwr;
residency[i] = ~0;
mode_avail = probe_time ||
lpm_cpu_mode_allow(cpu_id, i, true);
if(!mode_avail) {
maximum_residency[i] = 0;
minimum_residency[i] = 0;
continue;
}
maximum_residency[i] = ~0;
for (j = i + 1; j < cpu->nlevels; j++) {
mode_avail = probe_time ||
lpm_cpu_mode_allow(cpu_id, j, true);
if (mode_avail &&
(residency[i] > pwr->residencies[j]) &&
(maximum_residency[i] > pwr->residencies[j]) &&
(pwr->residencies[j] != 0))
residency[i] = pwr->residencies[j];
maximum_residency[i] = pwr->residencies[j];
}
minimum_residency[i] = pwr->time_overhead_us;
for(j = i-1; j >= 0; j--) {
if(probe_time || lpm_cpu_mode_allow(cpu_id, j, true)) {
minimum_residency[i] = maximum_residency[j] + 1;
break;
}
}
}
}
@ -102,17 +120,32 @@ static void set_optimum_cluster_residency(struct lpm_cluster *cluster,
for (i = 0; i < cluster->nlevels; i++) {
struct power_params *pwr = &cluster->levels[i].pwr;
mode_avail = probe_time ||
lpm_cluster_mode_allow(cluster, i, true);
if(!mode_avail) {
pwr->max_residency = 0;
pwr->min_residency = 0;
continue;
}
pwr->max_residency = ~0;
for (j = 0; j < cluster->nlevels; j++) {
if (i >= j)
for (j = i + 1; j < cluster->nlevels; j++) {
mode_avail = probe_time ||
lpm_cluster_mode_allow(cluster, i,
lpm_cluster_mode_allow(cluster, j,
true);
if (mode_avail &&
(pwr->max_residency > pwr->residencies[j]) &&
(pwr->residencies[j] != 0))
pwr->max_residency = pwr->residencies[j];
}
pwr->min_residency = pwr->time_overhead_us;
for(j = i-1; j >= 0; j--) {
if(probe_time || lpm_cluster_mode_allow(cluster, j, true)) {
pwr->min_residency = cluster->levels[j].pwr.max_residency + 1;
break;
}
}
}
}
@ -121,6 +154,11 @@ uint32_t *get_per_cpu_max_residency(int cpu)
return per_cpu(max_residency, cpu);
}
uint32_t *get_per_cpu_min_residency(int cpu)
{
return per_cpu(min_residency, cpu);
}
ssize_t lpm_enable_show(struct kobject *kobj, struct kobj_attribute *attr,
char *buf)
{
@ -886,6 +924,12 @@ struct lpm_cluster *parse_cluster(struct device_node *node,
GFP_KERNEL);
if (!per_cpu(max_residency, i))
return ERR_PTR(-ENOMEM);
per_cpu(min_residency, i) = devm_kzalloc(
&lpm_pdev->dev,
sizeof(uint32_t) * c->cpu->nlevels,
GFP_KERNEL);
if (!per_cpu(min_residency, i))
return ERR_PTR(-ENOMEM);
set_optimum_cpu_residency(c->cpu, i, true);
}
}

View File

@ -311,7 +311,7 @@ static int cpu_power_select(struct cpuidle_device *dev,
uint32_t next_event_us = 0;
int i;
uint32_t lvl_latency_us = 0;
uint32_t *residency = get_per_cpu_max_residency(dev->cpu);
uint32_t *min_residency = get_per_cpu_min_residency(dev->cpu);
if (!cpu)
return -EINVAL;
@ -343,14 +343,19 @@ static int cpu_power_select(struct cpuidle_device *dev,
if (next_event_us) {
if (next_event_us < lvl_latency_us)
continue;
break;
if (((next_event_us - lvl_latency_us) < sleep_us) ||
(next_event_us < sleep_us))
next_wakeup_us = next_event_us - lvl_latency_us;
}
if (next_wakeup_us <= residency[i]) {
/*
* min_residency is max_residency of previous level+1
* if none of the previous levels are enabled,
* min_residency is time overhead for current level
*/
if (next_wakeup_us >= min_residency[i]) {
best_level = i;
if (next_event_us && next_event_us < sleep_us &&
(mode != MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT))
@ -358,7 +363,6 @@ static int cpu_power_select(struct cpuidle_device *dev,
= next_event_us - lvl_latency_us;
else
modified_time_us = 0;
break;
}
}
@ -460,10 +464,7 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle)
continue;
if (from_idle && latency_us < pwr_params->latency_us)
continue;
if (sleep_us < pwr_params->time_overhead_us)
continue;
break;
if (suspend_in_progress && from_idle && level->notify_rpm)
continue;
@ -471,9 +472,13 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle)
if (level->notify_rpm && msm_rpm_waiting_for_ack())
continue;
if (sleep_us <= pwr_params->max_residency) {
/*
* min_residency is max_residency of previous level+1
* if none of the previous levels are enabled,
* min_residency is time overhead for current level
*/
if (sleep_us >= pwr_params->min_residency) {
best_level = i;
break;
}
}

View File

@ -28,6 +28,7 @@ struct power_params {
uint32_t energy_overhead; /* Enter + exit over head */
uint32_t time_overhead_us; /* Enter + exit overhead */
uint32_t residencies[NR_LPM_LEVELS];
uint32_t min_residency;
uint32_t max_residency;
};
@ -121,6 +122,7 @@ bool lpm_cpu_mode_allow(unsigned int cpu,
bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
unsigned int mode, bool from_idle);
uint32_t *get_per_cpu_max_residency(int cpu);
uint32_t *get_per_cpu_min_residency(int cpu);
extern struct lpm_cluster *lpm_root_node;
#ifdef CONFIG_SMP