cpuidle: lpm-levels: Add API to get low power mode latency
Add API to get latency for a low power mode with particular affinity level and reset level. Reset level is level at which only control logic power collpase happen or both control and memory logic power collapse happen or Retention state. The API returns the minum latency out of all clusters in the particular affinity level and reset level if cluster name is not passed or the latency of the specific cluster for which the cluster name is passed. Change-Id: I2facd9a1fa2dba7e7103d65544537799bd8ba518 Signed-off-by: Srinivas Rao L <lsrao@codeaurora.org> Conflicts: arch/arm/boot/dts/qcom/mdm9607-pm.dtsi arch/arm/boot/dts/qcom/mdm9640-pm.dtsi arch/arm/boot/dts/qcom/mdmcalifornium-pm.dtsi arch/arm/boot/dts/qcom/msm8909-pm8909-pm.dtsi arch/arm/boot/dts/qcom/msm8909-pm8916-pm.dtsi arch/arm/boot/dts/qcom/msm8937-pm.dtsi arch/arm/boot/dts/qcom/msm8952-pm.dtsi arch/arm/boot/dts/qcom/msmgold-pm.dtsi arch/arm/boot/dts/qcom/msmtitanium-pm.dtsi
This commit is contained in:
parent
8804d58937
commit
99d535fc95
7 changed files with 240 additions and 1 deletions
|
@ -92,6 +92,15 @@ Required properties:
|
|||
or not for the drivers to prepare for cluster collapse.
|
||||
- qcom,hyp-psci: This property is used to determine if the cpu
|
||||
enters the low power mode within hypervisor.
|
||||
- qcom,reset-level: This property is used to determine in this
|
||||
low power mode only control logic power collapse happens or memory
|
||||
logic power collapse aswell happens or retention state.
|
||||
The accepted values for this property are:
|
||||
"LPM_RESET_LVL_NONE" - No power collapse
|
||||
"LPM_RESET_LVL_RET" - Retention state
|
||||
"LPM_RESET_LVL_GDHS" - Only control logic power collapse (GDHS)
|
||||
"LPM_RESET_LVL_PC" - Control logic and memory logic
|
||||
power collapse (PC)
|
||||
|
||||
[Node bindings for qcom,pm-cpu]
|
||||
qcom,pm-cpu contains the low power modes that a cpu could enter. Currently it
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <dt-bindings/interrupt-controller/arm-gic.h>
|
||||
#include <dt-bindings/msm/pm.h>
|
||||
|
||||
&soc {
|
||||
qcom,spm@9A10000 {
|
||||
|
@ -63,6 +64,7 @@
|
|||
qcom,energy-overhead = <69000>;
|
||||
qcom,time-overhead = <150>;
|
||||
qcom,min-child-idx = <1>;
|
||||
qcom,reset-level = <LPM_RESET_LVL_RET>;
|
||||
};
|
||||
qcom,pm-cluster-level@2{ /* E4-M3 */
|
||||
reg = <1>;
|
||||
|
@ -77,6 +79,7 @@
|
|||
qcom,min-child-idx = <2>;
|
||||
qcom,notify-rpm;
|
||||
qcom,is-reset;
|
||||
qcom,reset-level = <LPM_RESET_LVL_PC>;
|
||||
};
|
||||
|
||||
qcom,pm-cluster@0{
|
||||
|
@ -109,6 +112,7 @@
|
|||
qcom,energy-overhead = <89070>;
|
||||
qcom,time-overhead = <180>;
|
||||
qcom,min-child-idx = <2>;
|
||||
qcom,reset-level = <LPM_RESET_LVL_GDHS>;
|
||||
};
|
||||
|
||||
qcom,pm-cluster-level@2{ /* D4 */
|
||||
|
@ -121,6 +125,7 @@
|
|||
qcom,time-overhead = <1000>;
|
||||
qcom,min-child-idx = <2>;
|
||||
qcom,is-reset;
|
||||
qcom,reset-level = <LPM_RESET_LVL_PC>;
|
||||
};
|
||||
|
||||
qcom,pm-cpu {
|
||||
|
@ -158,6 +163,8 @@
|
|||
qcom,ss-power = <196>;
|
||||
qcom,energy-overhead = <45300>;
|
||||
qcom,time-overhead = <210>;
|
||||
qcom,reset-level =
|
||||
<LPM_RESET_LVL_PC>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
@ -192,6 +199,8 @@
|
|||
qcom,energy-overhead = <83500>;
|
||||
qcom,time-overhead = <180>;
|
||||
qcom,min-child-idx = <2>;
|
||||
qcom,reset-level =
|
||||
<LPM_RESET_LVL_GDHS>;
|
||||
};
|
||||
|
||||
qcom,pm-cluster-level@2{ /* D4 */
|
||||
|
@ -204,6 +213,8 @@
|
|||
qcom,time-overhead = <1000>;
|
||||
qcom,min-child-idx = <2>;
|
||||
qcom,is-reset;
|
||||
qcom,reset-level =
|
||||
<LPM_RESET_LVL_PC>;
|
||||
};
|
||||
|
||||
qcom,pm-cpu {
|
||||
|
@ -241,6 +252,8 @@
|
|||
qcom,ss-power = <196>;
|
||||
qcom,energy-overhead = <45300>;
|
||||
qcom,time-overhead = <210>;
|
||||
qcom,reset-level =
|
||||
<LPM_RESET_LVL_PC>;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -507,6 +507,13 @@ static int parse_cluster_level(struct device_node *node,
|
|||
if (ret)
|
||||
goto failed;
|
||||
|
||||
key = "qcom,reset-level";
|
||||
ret = of_property_read_u32(node, key, &level->reset_level);
|
||||
if (ret == -EINVAL)
|
||||
level->reset_level = LPM_RESET_LVL_NONE;
|
||||
else if (ret)
|
||||
goto failed;
|
||||
|
||||
cluster->nlevels++;
|
||||
return 0;
|
||||
failed:
|
||||
|
@ -661,6 +668,13 @@ static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
|
|||
|
||||
key = "qcom,jtag-save-restore";
|
||||
l->jtag_save_restore = of_property_read_bool(n, key);
|
||||
|
||||
key = "qcom,reset-level";
|
||||
ret = of_property_read_u32(n, key, &l->reset_level);
|
||||
if (ret == -EINVAL)
|
||||
l->reset_level = LPM_RESET_LVL_NONE;
|
||||
else if (ret)
|
||||
goto failed;
|
||||
}
|
||||
return 0;
|
||||
failed:
|
||||
|
|
|
@ -141,6 +141,140 @@ void lpm_suspend_wake_time(uint64_t wakeup_time)
|
|||
}
|
||||
EXPORT_SYMBOL(lpm_suspend_wake_time);
|
||||
|
||||
static uint32_t least_cluster_latency(struct lpm_cluster *cluster,
|
||||
struct latency_level *lat_level)
|
||||
{
|
||||
struct list_head *list;
|
||||
struct lpm_cluster_level *level;
|
||||
struct lpm_cluster *n;
|
||||
struct power_params *pwr_params;
|
||||
uint32_t latency = 0;
|
||||
int i;
|
||||
|
||||
if (!cluster->list.next) {
|
||||
for (i = 0; i < cluster->nlevels; i++) {
|
||||
level = &cluster->levels[i];
|
||||
pwr_params = &level->pwr;
|
||||
if (lat_level->reset_level == level->reset_level) {
|
||||
if ((latency > pwr_params->latency_us)
|
||||
|| (!latency))
|
||||
latency = pwr_params->latency_us;
|
||||
break;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
list_for_each(list, &cluster->parent->child) {
|
||||
n = list_entry(list, typeof(*n), list);
|
||||
if (lat_level->level_name) {
|
||||
if (strcmp(lat_level->level_name,
|
||||
n->cluster_name))
|
||||
continue;
|
||||
}
|
||||
for (i = 0; i < n->nlevels; i++) {
|
||||
level = &n->levels[i];
|
||||
pwr_params = &level->pwr;
|
||||
if (lat_level->reset_level ==
|
||||
level->reset_level) {
|
||||
if ((latency > pwr_params->latency_us)
|
||||
|| (!latency))
|
||||
latency =
|
||||
pwr_params->latency_us;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return latency;
|
||||
}
|
||||
|
||||
static uint32_t least_cpu_latency(struct list_head *child,
|
||||
struct latency_level *lat_level)
|
||||
{
|
||||
struct list_head *list;
|
||||
struct lpm_cpu_level *level;
|
||||
struct power_params *pwr_params;
|
||||
struct lpm_cpu *cpu;
|
||||
struct lpm_cluster *n;
|
||||
uint32_t latency = 0;
|
||||
int i;
|
||||
|
||||
list_for_each(list, child) {
|
||||
n = list_entry(list, typeof(*n), list);
|
||||
if (lat_level->level_name) {
|
||||
if (strcmp(lat_level->level_name, n->cluster_name))
|
||||
continue;
|
||||
}
|
||||
cpu = n->cpu;
|
||||
for (i = 0; i < cpu->nlevels; i++) {
|
||||
level = &cpu->levels[i];
|
||||
pwr_params = &level->pwr;
|
||||
if (lat_level->reset_level == level->reset_level) {
|
||||
if ((latency > pwr_params->latency_us)
|
||||
|| (!latency))
|
||||
latency = pwr_params->latency_us;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
return latency;
|
||||
}
|
||||
|
||||
static struct lpm_cluster *cluster_aff_match(struct lpm_cluster *cluster,
|
||||
int affinity_level)
|
||||
{
|
||||
struct lpm_cluster *n;
|
||||
|
||||
if ((cluster->aff_level == affinity_level)
|
||||
|| ((cluster->cpu) && (affinity_level == 0)))
|
||||
return cluster;
|
||||
else if (!cluster->cpu) {
|
||||
n = list_entry(cluster->child.next, typeof(*n), list);
|
||||
return cluster_aff_match(n, affinity_level);
|
||||
} else
|
||||
return NULL;
|
||||
}
|
||||
|
||||
int lpm_get_latency(struct latency_level *level, uint32_t *latency)
|
||||
{
|
||||
struct lpm_cluster *cluster;
|
||||
uint32_t val;
|
||||
|
||||
if ((level->affinity_level < 0)
|
||||
|| (level->affinity_level > lpm_root_node->aff_level)
|
||||
|| (level->reset_level < LPM_RESET_LVL_RET)
|
||||
|| (level->reset_level > LPM_RESET_LVL_PC)
|
||||
|| !latency)
|
||||
return -EINVAL;
|
||||
|
||||
if (!lpm_root_node) {
|
||||
pr_err("%s: lpm_probe not completed\n", __func__);
|
||||
return -EAGAIN;
|
||||
}
|
||||
|
||||
cluster = cluster_aff_match(lpm_root_node, level->affinity_level);
|
||||
if (!cluster) {
|
||||
pr_err("%s:No matching cluster found for affinity_level:%d\n",
|
||||
__func__, level->affinity_level);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
if (level->affinity_level == 0)
|
||||
val = least_cpu_latency(&cluster->parent->child, level);
|
||||
else
|
||||
val = least_cluster_latency(cluster, level);
|
||||
|
||||
if (!val) {
|
||||
pr_err("%s:No mode with affinity_level:%d reset_level:%d\n",
|
||||
__func__, level->affinity_level, level->reset_level);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
*latency = val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL(lpm_get_latency);
|
||||
|
||||
static void update_debug_pc_event(enum debug_event event, uint32_t arg1,
|
||||
uint32_t arg2, uint32_t arg3, uint32_t arg4)
|
||||
{
|
||||
|
|
|
@ -38,6 +38,7 @@ struct lpm_cpu_level {
|
|||
bool is_reset;
|
||||
bool jtag_save_restore;
|
||||
bool hyp_psci;
|
||||
int reset_level;
|
||||
};
|
||||
|
||||
struct lpm_cpu {
|
||||
|
@ -69,6 +70,7 @@ struct lpm_cluster_level {
|
|||
struct lpm_level_avail available;
|
||||
unsigned int psci_id;
|
||||
bool is_reset;
|
||||
int reset_level;
|
||||
};
|
||||
|
||||
struct low_power_ops {
|
||||
|
|
25
include/dt-bindings/msm/pm.h
Normal file
25
include/dt-bindings/msm/pm.h
Normal file
|
@ -0,0 +1,25 @@
|
|||
/* Copyright (c) 2016, 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
|
||||
* only version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#ifndef __DT_MSM_PM_H__
|
||||
#define __DT_MSM_PM_H__
|
||||
|
||||
#define LPM_RESET_LVL_NONE 0
|
||||
#define LPM_RESET_LVL_RET 1
|
||||
#define LPM_RESET_LVL_GDHS 2
|
||||
#define LPM_RESET_LVL_PC 3
|
||||
|
||||
#define LPM_AFF_LVL_CPU 0
|
||||
#define LPM_AFF_LVL_L2 1
|
||||
#define LPM_AFF_LVL_CCI 2
|
||||
|
||||
#endif
|
|
@ -1,6 +1,6 @@
|
|||
/*
|
||||
* Copyright (C) 2007 Google, Inc.
|
||||
* Copyright (c) 2009-2015, The Linux Foundation. All rights reserved.
|
||||
* Copyright (c) 2009-2016, The Linux Foundation. All rights reserved.
|
||||
* Author: San Mehat <san@android.com>
|
||||
*
|
||||
* This software is licensed under the terms of the GNU General Public
|
||||
|
@ -21,6 +21,7 @@
|
|||
#include <linux/cpuidle.h>
|
||||
#include <asm/smp_plat.h>
|
||||
#include <asm/barrier.h>
|
||||
#include <dt-bindings/msm/pm.h>
|
||||
|
||||
#if !defined(CONFIG_SMP)
|
||||
#define msm_secondary_startup NULL
|
||||
|
@ -62,6 +63,12 @@ struct msm_pm_sleep_status_data {
|
|||
uint32_t mask;
|
||||
};
|
||||
|
||||
struct latency_level {
|
||||
int affinity_level;
|
||||
int reset_level;
|
||||
const char *level_name;
|
||||
};
|
||||
|
||||
/**
|
||||
* lpm_cpu_pre_pc_cb(): API to get the L2 flag to pass to TZ
|
||||
*
|
||||
|
@ -115,12 +122,41 @@ static inline void msm_arch_idle(void)
|
|||
}
|
||||
|
||||
#ifdef CONFIG_MSM_PM
|
||||
|
||||
void msm_pm_set_rpm_wakeup_irq(unsigned int irq);
|
||||
int msm_pm_wait_cpu_shutdown(unsigned int cpu);
|
||||
int __init msm_pm_sleep_status_init(void);
|
||||
void lpm_cpu_hotplug_enter(unsigned int cpu);
|
||||
s32 msm_cpuidle_get_deep_idle_latency(void);
|
||||
int msm_pm_collapse(unsigned long unused);
|
||||
|
||||
/**
|
||||
* lpm_get_latency() - API to get latency for a low power mode
|
||||
* @latency_level: pointer to structure with below elements
|
||||
* affinity_level: The level (CPU/L2/CCI etc.) for which the
|
||||
* latency is required.
|
||||
* LPM_AFF_LVL_CPU : CPU level
|
||||
* LPM_AFF_LVL_L2 : L2 level
|
||||
* LPM_AFF_LVL_CCI : CCI level
|
||||
* reset_level: Can be passed "LPM_RESET_LVL_GDHS" for
|
||||
* low power mode with control logic power collapse or
|
||||
* "LPM_RESET_LVL_PC" for low power mode with control and
|
||||
* memory logic power collapse or "LPM_RESET_LVL_RET" for
|
||||
* retention mode.
|
||||
* level_name: Pointer to the cluster name for which the latency
|
||||
* is required or NULL if the minimum value out of all the
|
||||
* clusters is to be returned. For CPU level, the name of the
|
||||
* L2 cluster to be passed. For CCI it has no effect.
|
||||
* @latency: address to get the latency value.
|
||||
*
|
||||
* latency value will be for the particular cluster or the minimum
|
||||
* value out of all the clusters at the particular affinity_level
|
||||
* and reset_level.
|
||||
*
|
||||
* Return: 0 for success; Error number for failure.
|
||||
*/
|
||||
int lpm_get_latency(struct latency_level *level, uint32_t *latency);
|
||||
|
||||
#else
|
||||
static inline void msm_pm_set_rpm_wakeup_irq(unsigned int irq) {}
|
||||
static inline int msm_pm_wait_cpu_shutdown(unsigned int cpu) { return 0; }
|
||||
|
@ -133,6 +169,12 @@ static inline void lpm_cpu_hotplug_enter(unsigned int cpu)
|
|||
|
||||
static inline s32 msm_cpuidle_get_deep_idle_latency(void) { return 0; }
|
||||
#define msm_pm_collapse NULL
|
||||
|
||||
static inline int lpm_get_latency(struct latency_level *level,
|
||||
uint32_t *latency)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_HOTPLUG_CPU
|
||||
|
|
Loading…
Add table
Reference in a new issue