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:
parent
285cdfb61b
commit
cec5c130c6
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue