Merge "lpm_levels_legacy: Rectified compilation errors"

This commit is contained in:
qctecmdr 2021-03-17 02:02:47 -07:00 • committed by Gerrit - the friendly Code Review server
commit 2fd974a64c
10 changed files with 2939 additions and 5 deletions

View file

@ -22,8 +22,12 @@ obj-$(CONFIG_ARM_AT91_CPUIDLE) += cpuidle-at91.o
obj-$(CONFIG_ARM_EXYNOS_CPUIDLE) += cpuidle-exynos.o
obj-$(CONFIG_ARM_CPUIDLE) += cpuidle-arm.o
obj-$(CONFIG_ARM_PSCI_CPUIDLE) += cpuidle-psci.o
ifeq ($(CONFIG_MSM_PM_LEGACY), y)
obj-y += lpm-levels-legacy.o lpm-levels-of-legacy.o
else
obj-$(CONFIG_ARM_QCOM_LPM_CPUIDLE) += lpm-levels.o
obj-$(CONFIG_ARM_QCOM_LPM_CPUIDLE) += lpm-levels-of.o
endif
###############################################################################
# MIPS drivers

File diff suppressed because it is too large Load diff

View file

@ -0,0 +1,127 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2014-2021 The Linux Foundation. All rights reserved.
*/
#include <soc/qcom/pm-legacy.h>
#include <soc/qcom/spm.h>
#define NR_LPM_LEVELS 8
extern bool use_psci;
struct lpm_lookup_table {
uint32_t modes;
const char *mode_name;
};
struct power_params {
uint32_t latency_us; /* Enter + Exit latency */
uint32_t ss_power; /* Steady state power */
uint32_t energy_overhead; /* Enter + exit over head */
uint32_t time_overhead_us; /* Enter + exit overhead */
uint32_t residencies[NR_LPM_LEVELS];
uint32_t max_residency;
};
struct lpm_cpu_level {
const char *name;
enum msm_pm_sleep_mode mode;
bool use_bc_timer;
struct power_params pwr;
unsigned int psci_id;
bool is_reset;
bool jtag_save_restore;
bool hyp_psci;
int reset_level;
};
struct lpm_cpu {
struct lpm_cpu_level levels[NR_LPM_LEVELS];
int nlevels;
unsigned int psci_mode_shift;
unsigned int psci_mode_mask;
struct lpm_cluster *parent;
};
struct lpm_level_avail {
bool idle_enabled;
bool suspend_enabled;
struct kobject *kobj;
struct kobj_attribute idle_enabled_attr;
struct kobj_attribute suspend_enabled_attr;
void *data;
int idx;
bool cpu_node;
};
struct lpm_cluster_level {
const char *level_name;
int *mode; /* SPM mode to enter */
int min_child_level;
struct cpumask num_cpu_votes;
struct power_params pwr;
bool notify_rpm;
bool disable_dynamic_routing;
bool sync_level;
bool last_core_only;
struct lpm_level_avail available;
unsigned int psci_id;
bool is_reset;
int reset_level;
bool no_cache_flush;
};
struct low_power_ops {
struct msm_spm_device *spm;
int (*set_mode)(struct low_power_ops *ops, int mode,
struct lpm_cluster_level *level);
enum msm_pm_l2_scm_flag tz_flag;
};
struct lpm_cluster {
struct list_head list;
struct list_head child;
const char *cluster_name;
const char **name;
unsigned long aff_level; /* Affinity level of the node */
struct low_power_ops *lpm_dev;
int ndevices;
struct lpm_cluster_level levels[NR_LPM_LEVELS];
int nlevels;
enum msm_pm_l2_scm_flag l2_flag;
int min_child_level;
int default_level;
int last_level;
struct lpm_cpu *cpu;
struct cpuidle_driver *drv;
spinlock_t sync_lock;
struct cpumask child_cpus;
struct cpumask num_children_in_sync;
struct lpm_cluster *parent;
struct lpm_stats *stats;
unsigned int psci_mode_shift;
unsigned int psci_mode_mask;
bool no_saw_devices;
};
int set_l2_mode(struct low_power_ops *ops, int mode,
struct lpm_cluster_level *level);
int set_system_mode(struct low_power_ops *ops, int mode,
struct lpm_cluster_level *level);
int set_l3_mode(struct low_power_ops *ops, int mode,
struct lpm_cluster_level *level);
void lpm_suspend_wake_time(uint64_t wakeup_time);
struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev);
void free_cluster_node(struct lpm_cluster *cluster);
void cluster_dt_walkthrough(struct lpm_cluster *cluster);
int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj);
bool lpm_cpu_mode_allow(unsigned int cpu,
unsigned int mode, bool from_idle);
bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
unsigned int mode, bool from_idle);
uint32_t *get_per_cpu_max_residency(int cpu);
extern struct lpm_cluster *lpm_root_node;

View file

@ -0,0 +1,999 @@
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2011-2021, The Linux Foundation. All rights reserved.
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/of.h>
#include <linux/err.h>
#include <linux/sysfs.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/moduleparam.h>
#include "lpm-levels-legacy.h"
bool use_psci;
enum lpm_type {
IDLE = 0,
SUSPEND,
LPM_TYPE_NR
};
struct lpm_type_str {
enum lpm_type type;
char *str;
};
static const struct lpm_type_str lpm_types[] = {
{IDLE, "idle_enabled"},
{SUSPEND, "suspend_enabled"},
};
static DEFINE_PER_CPU(uint32_t *, max_residency);
static struct lpm_level_avail *cpu_level_available[NR_CPUS];
static struct platform_device *lpm_pdev;
static void *get_enabled_ptr(struct kobj_attribute *attr,
struct lpm_level_avail *avail)
{
void *arg = NULL;
if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
arg = (void *) &avail->idle_enabled;
else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
arg = (void *) &avail->suspend_enabled;
return arg;
}
static struct lpm_level_avail *get_avail_ptr(struct kobject *kobj,
struct kobj_attribute *attr)
{
struct lpm_level_avail *avail = NULL;
if (!strcmp(attr->attr.name, lpm_types[IDLE].str))
avail = container_of(attr, struct lpm_level_avail,
idle_enabled_attr);
else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str))
avail = container_of(attr, struct lpm_level_avail,
suspend_enabled_attr);
return avail;
}
static void set_optimum_cpu_residency(struct lpm_cpu *cpu, int cpu_id,
bool probe_time)
{
int i, j;
bool mode_avail;
uint32_t *residency = per_cpu(max_residency, cpu_id);
for (i = 0; i < cpu->nlevels; i++) {
struct power_params *pwr = &cpu->levels[i].pwr;
mode_avail = probe_time ||
lpm_cpu_mode_allow(cpu_id, i, true);
if (!mode_avail) {
residency[i] = 0;
continue;
}
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]) &&
(pwr->residencies[j] != 0))
residency[i] = pwr->residencies[j];
}
}
}
static void set_optimum_cluster_residency(struct lpm_cluster *cluster,
bool probe_time)
{
int i, j;
bool mode_avail;
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;
continue;
}
pwr->max_residency = ~0;
for (j = i+1; j < cluster->nlevels; j++) {
mode_avail = probe_time ||
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];
}
}
}
uint32_t *get_per_cpu_max_residency(int cpu)
{
return per_cpu(max_residency, cpu);
}
static ssize_t lpm_enable_show(struct kobject *kobj,
struct kobj_attribute *attr, char *buf)
{
int ret = 0;
struct kernel_param kp;
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);
ret++;
}
return ret;
}
static ssize_t lpm_enable_store(struct kobject *kobj,
struct kobj_attribute *attr, const char *buf, size_t len)
{
int ret = 0;
struct kernel_param kp;
struct lpm_level_avail *avail;
avail = get_avail_ptr(kobj, attr);
if (WARN_ON(!avail))
return -EINVAL;
kp.arg = get_enabled_ptr(attr, avail);
ret = param_set_bool(buf, &kp);
if (avail->cpu_node)
set_optimum_cpu_residency(avail->data, avail->idx, false);
else
set_optimum_cluster_residency(avail->data, false);
return ret ? ret : len;
}
static int create_lvl_avail_nodes(const char *name,
struct kobject *parent, struct lpm_level_avail *avail,
void *data, int index, bool cpu_node)
{
struct attribute_group *attr_group = NULL;
struct attribute **attr = NULL;
struct kobject *kobj = NULL;
int ret = 0;
kobj = kobject_create_and_add(name, parent);
if (!kobj)
return -ENOMEM;
attr_group = devm_kzalloc(&lpm_pdev->dev, sizeof(*attr_group),
GFP_KERNEL);
if (!attr_group) {
ret = -ENOMEM;
goto failed;
}
attr = devm_kzalloc(&lpm_pdev->dev,
sizeof(*attr) * (LPM_TYPE_NR + 1), GFP_KERNEL);
if (!attr) {
ret = -ENOMEM;
goto failed;
}
sysfs_attr_init(&avail->idle_enabled_attr.attr);
avail->idle_enabled_attr.attr.name = lpm_types[IDLE].str;
avail->idle_enabled_attr.attr.mode = 0644;
avail->idle_enabled_attr.show = lpm_enable_show;
avail->idle_enabled_attr.store = lpm_enable_store;
sysfs_attr_init(&avail->suspend_enabled_attr.attr);
avail->suspend_enabled_attr.attr.name = lpm_types[SUSPEND].str;
avail->suspend_enabled_attr.attr.mode = 0644;
avail->suspend_enabled_attr.show = lpm_enable_show;
avail->suspend_enabled_attr.store = lpm_enable_store;
attr[0] = &avail->idle_enabled_attr.attr;
attr[1] = &avail->suspend_enabled_attr.attr;
attr[2] = NULL;
attr_group->attrs = attr;
ret = sysfs_create_group(kobj, attr_group);
if (ret) {
ret = -ENOMEM;
goto failed;
}
avail->idle_enabled = true;
avail->suspend_enabled = true;
avail->kobj = kobj;
avail->data = data;
avail->idx = index;
avail->cpu_node = cpu_node;
return ret;
failed:
kobject_put(kobj);
return ret;
}
static int create_cpu_lvl_nodes(struct lpm_cluster *p, struct kobject *parent)
{
int cpu;
int i, cpu_idx;
struct kobject **cpu_kobj = NULL;
struct lpm_level_avail *level_list = NULL;
char cpu_name[20] = {0};
int ret = 0;
cpu_kobj = devm_kzalloc(&lpm_pdev->dev, sizeof(*cpu_kobj) *
cpumask_weight(&p->child_cpus), GFP_KERNEL);
if (!cpu_kobj)
return -ENOMEM;
cpu_idx = 0;
for_each_cpu(cpu, &p->child_cpus) {
snprintf(cpu_name, sizeof(cpu_name), "cpu%d", cpu);
cpu_kobj[cpu_idx] = kobject_create_and_add(cpu_name, parent);
if (!cpu_kobj[cpu_idx]) {
ret = -ENOMEM;
goto release_kobj;
}
level_list = devm_kzalloc(&lpm_pdev->dev,
p->cpu->nlevels * sizeof(*level_list),
GFP_KERNEL);
if (!level_list) {
ret = -ENOMEM;
goto release_kobj;
}
for (i = 0; i < p->cpu->nlevels; i++) {
ret = create_lvl_avail_nodes(p->cpu->levels[i].name,
cpu_kobj[cpu_idx], &level_list[i],
(void *)p->cpu, cpu, true);
if (ret)
goto release_kobj;
}
cpu_level_available[cpu] = level_list;
cpu_idx++;
}
return ret;
release_kobj:
for (i = 0; i < cpumask_weight(&p->child_cpus); i++)
kobject_put(cpu_kobj[i]);
return ret;
}
int create_cluster_lvl_nodes(struct lpm_cluster *p, struct kobject *kobj)
{
int ret = 0;
struct lpm_cluster *child = NULL;
int i;
struct kobject *cluster_kobj = NULL;
if (!p)
return -ENODEV;
cluster_kobj = kobject_create_and_add(p->cluster_name, kobj);
if (!cluster_kobj)
return -ENOMEM;
for (i = 0; i < p->nlevels; i++) {
ret = create_lvl_avail_nodes(p->levels[i].level_name,
cluster_kobj, &p->levels[i].available,
(void *)p, 0, false);
if (ret)
return ret;
}
list_for_each_entry(child, &p->child, list) {
ret = create_cluster_lvl_nodes(child, cluster_kobj);
if (ret)
return ret;
}
if (p->cpu) {
ret = create_cpu_lvl_nodes(p, cluster_kobj);
if (ret)
return ret;
}
return 0;
}
bool lpm_cpu_mode_allow(unsigned int cpu,
unsigned int index, bool from_idle)
{
struct lpm_level_avail *avail = cpu_level_available[cpu];
if (!lpm_pdev || !avail)
return !from_idle;
return !!(from_idle ? avail[index].idle_enabled :
avail[index].suspend_enabled);
}
bool lpm_cluster_mode_allow(struct lpm_cluster *cluster,
unsigned int mode, bool from_idle)
{
struct lpm_level_avail *avail = &cluster->levels[mode].available;
if (!lpm_pdev || !avail)
return false;
return !!(from_idle ? avail->idle_enabled :
avail->suspend_enabled);
}
static int parse_legacy_cluster_params(struct device_node *node,
struct lpm_cluster *c)
{
int i;
char *key;
int ret;
struct lpm_match {
char *devname;
int (*set_mode)(struct low_power_ops *ops, int mode,
struct lpm_cluster_level *level);
};
struct lpm_match match_tbl[] = {
{"l2", set_l2_mode},
{"cci", set_system_mode},
{"l3", set_l3_mode},
{"cbf", set_system_mode},
};
key = "qcom,spm-device-names";
c->ndevices = of_property_count_strings(node, key);
if (c->ndevices < 0) {
pr_info("%s(): Ignoring cluster params\n", __func__);
c->no_saw_devices = true;
c->ndevices = 0;
return 0;
}
c->name = devm_kzalloc(&lpm_pdev->dev, c->ndevices * sizeof(*c->name),
GFP_KERNEL);
c->lpm_dev = devm_kzalloc(&lpm_pdev->dev,
c->ndevices * sizeof(*c->lpm_dev),
GFP_KERNEL);
if (!c->name || !c->lpm_dev) {
ret = -ENOMEM;
goto failed;
}
for (i = 0; i < c->ndevices; i++) {
char device_name[20];
int j;
ret = of_property_read_string_index(node, key, i, &c->name[i]);
if (ret)
goto failed;
snprintf(device_name, sizeof(device_name), "%s-%s",
c->cluster_name, c->name[i]);
c->lpm_dev[i].spm = msm_spm_get_device_by_name(device_name);
if (IS_ERR_OR_NULL(c->lpm_dev[i].spm)) {
pr_err("Failed to get spm device by name:%s\n",
device_name);
ret = PTR_ERR(c->lpm_dev[i].spm);
goto failed;
}
for (j = 0; j < ARRAY_SIZE(match_tbl); j++) {
if (!strcmp(c->name[i], match_tbl[j].devname))
c->lpm_dev[i].set_mode = match_tbl[j].set_mode;
}
if (!c->lpm_dev[i].set_mode) {
ret = -ENODEV;
goto failed;
}
}
key = "qcom,default-level";
if (of_property_read_u32(node, key, &c->default_level))
c->default_level = 0;
return 0;
failed:
pr_err("%s(): Failed reading %s\n", __func__, key);
return ret;
}
static int parse_cluster_params(struct device_node *node,
struct lpm_cluster *c)
{
char *key;
int ret;
key = "label";
ret = of_property_read_string(node, key, &c->cluster_name);
if (ret) {
pr_err("%s(): Cannot read required param %s\n", __func__, key);
return ret;
}
if (use_psci) {
key = "qcom,psci-mode-shift";
ret = of_property_read_u32(node, key,
&c->psci_mode_shift);
if (ret) {
pr_err("%s(): Failed to read param: %s\n",
__func__, key);
return ret;
}
key = "qcom,psci-mode-mask";
ret = of_property_read_u32(node, key,
&c->psci_mode_mask);
if (ret) {
pr_err("%s(): Failed to read param: %s\n",
__func__, key);
return ret;
}
/* Set ndevice to 1 as default */
c->ndevices = 1;
return 0;
} else
return parse_legacy_cluster_params(node, c);
}
static int parse_lpm_mode(const char *str)
{
int i;
struct lpm_lookup_table mode_lookup[] = {
{MSM_SPM_MODE_POWER_COLLAPSE, "pc"},
{MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE, "spc"},
{MSM_SPM_MODE_FASTPC, "fpc"},
{MSM_SPM_MODE_GDHS, "gdhs"},
{MSM_SPM_MODE_RETENTION, "retention"},
{MSM_SPM_MODE_CLOCK_GATING, "wfi"},
{MSM_SPM_MODE_DISABLED, "active"}
};
for (i = 0; i < ARRAY_SIZE(mode_lookup); i++)
if (!strcmp(str, mode_lookup[i].mode_name))
return mode_lookup[i].modes;
return -EINVAL;
}
static int parse_power_params(struct device_node *node,
struct power_params *pwr)
{
char *key;
int ret;
key = "qcom,latency-us";
ret = of_property_read_u32(node, key, &pwr->latency_us);
if (ret)
goto fail;
key = "qcom,ss-power";
ret = of_property_read_u32(node, key, &pwr->ss_power);
if (ret)
goto fail;
key = "qcom,energy-overhead";
ret = of_property_read_u32(node, key, &pwr->energy_overhead);
if (ret)
goto fail;
key = "qcom,time-overhead";
ret = of_property_read_u32(node, key, &pwr->time_overhead_us);
if (ret)
goto fail;
fail:
if (ret)
pr_err("%s(): %s Error reading %s\n", __func__, node->name,
key);
return ret;
}
static int parse_cluster_level(struct device_node *node,
struct lpm_cluster *cluster)
{
int i = 0;
struct lpm_cluster_level *level = &cluster->levels[cluster->nlevels];
int ret = -ENOMEM;
char *key;
key = "label";
ret = of_property_read_string(node, key, &level->level_name);
if (ret)
goto failed;
if (use_psci) {
char *k = "qcom,psci-mode";
ret = of_property_read_u32(node, k, &level->psci_id);
if (ret)
goto failed;
level->is_reset = of_property_read_bool(node, "qcom,is-reset");
} else if (!cluster->no_saw_devices) {
key = "no saw-devices";
level->mode = devm_kzalloc(&lpm_pdev->dev,
cluster->ndevices * sizeof(*level->mode),
GFP_KERNEL);
if (!level->mode) {
pr_err("Memory allocation failed\n");
goto failed;
}
for (i = 0; i < cluster->ndevices; i++) {
const char *spm_mode;
char key[25] = {0};
snprintf(key, 25, "qcom,spm-%s-mode", cluster->name[i]);
ret = of_property_read_string(node, key, &spm_mode);
if (ret)
goto failed;
level->mode[i] = parse_lpm_mode(spm_mode);
if (level->mode[i] < 0)
goto failed;
if (level->mode[i] == MSM_SPM_MODE_POWER_COLLAPSE
|| level->mode[i] ==
MSM_SPM_MODE_STANDALONE_POWER_COLLAPSE)
level->is_reset |= true;
}
}
key = "label";
ret = of_property_read_string(node, key, &level->level_name);
if (ret)
goto failed;
if (cluster->nlevels != cluster->default_level) {
key = "min child idx";
ret = of_property_read_u32(node, "qcom,min-child-idx",
&level->min_child_level);
if (ret)
goto failed;
if (cluster->min_child_level > level->min_child_level)
cluster->min_child_level = level->min_child_level;
}
level->notify_rpm = of_property_read_bool(node, "qcom,notify-rpm");
level->disable_dynamic_routing = of_property_read_bool(node,
"qcom,disable-dynamic-int-routing");
level->last_core_only = of_property_read_bool(node,
"qcom,last-core-only");
level->no_cache_flush = of_property_read_bool(node,
"qcom,no-cache-flush");
key = "parse_power_params";
ret = parse_power_params(node, &level->pwr);
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:
pr_err("Failed %s() key = %s ret = %d\n", __func__, key, ret);
return ret;
}
static int parse_cpu_spm_mode(const char *mode_name)
{
struct lpm_lookup_table pm_sm_lookup[] = {
{MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
"wfi"},
{MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
"standalone_pc"},
{MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
"pc"},
{MSM_PM_SLEEP_MODE_RETENTION,
"retention"},
{MSM_PM_SLEEP_MODE_FASTPC,
"fpc"},
};
int i;
int ret = -EINVAL;
for (i = 0; i < ARRAY_SIZE(pm_sm_lookup); i++) {
if (!strcmp(mode_name, pm_sm_lookup[i].mode_name)) {
ret = pm_sm_lookup[i].modes;
break;
}
}
return ret;
}
static int parse_cpu_mode(struct device_node *n, struct lpm_cpu_level *l)
{
char *key;
int ret;
key = "qcom,spm-cpu-mode";
ret = of_property_read_string(n, key, &l->name);
if (ret) {
pr_err("Failed %s %d\n", n->name, __LINE__);
return ret;
}
if (use_psci) {
key = "qcom,psci-cpu-mode";
ret = of_property_read_u32(n, key, &l->psci_id);
if (ret) {
pr_err("Failed reading %s on device %s\n", key,
n->name);
return ret;
}
key = "qcom,hyp-psci";
l->hyp_psci = of_property_read_bool(n, key);
} else {
l->mode = parse_cpu_spm_mode(l->name);
if (l->mode < 0)
return l->mode;
}
return 0;
}
static int get_cpumask_for_node(struct device_node *node, struct cpumask *mask)
{
struct device_node *cpu_node;
int cpu;
int idx = 0;
cpu_node = of_parse_phandle(node, "qcom,cpu", idx++);
if (!cpu_node) {
pr_info("%s: No CPU phandle, assuming single cluster\n",
node->full_name);
/*
* Not all targets have the cpu node populated in the device
* tree. If cpu node is not populated assume all possible
* nodes belong to this cluster
*/
cpumask_copy(mask, cpu_possible_mask);
return 0;
}
while (cpu_node) {
for_each_possible_cpu(cpu) {
if (of_get_cpu_node(cpu, NULL) == cpu_node) {
cpumask_set_cpu(cpu, mask);
break;
}
}
of_node_put(cpu_node);
cpu_node = of_parse_phandle(node, "qcom,cpu", idx++);
}
return 0;
}
static int calculate_residency(struct power_params *base_pwr,
struct power_params *next_pwr)
{
int32_t residency = (int32_t)(next_pwr->energy_overhead -
base_pwr->energy_overhead) -
((int32_t)(next_pwr->ss_power * next_pwr->time_overhead_us)
- (int32_t)(base_pwr->ss_power * base_pwr->time_overhead_us));
residency /= (int32_t)(base_pwr->ss_power - next_pwr->ss_power);
if (residency < 0) {
pr_err("%s: residency < 0 for LPM\n",
__func__);
return next_pwr->time_overhead_us;
}
return residency < next_pwr->time_overhead_us ?
next_pwr->time_overhead_us : residency;
}
static int parse_cpu_levels(struct device_node *node, struct lpm_cluster *c)
{
struct device_node *n;
int ret = -ENOMEM;
int i, j;
char *key;
c->cpu = devm_kzalloc(&lpm_pdev->dev, sizeof(*c->cpu), GFP_KERNEL);
if (!c->cpu)
return ret;
c->cpu->parent = c;
if (use_psci) {
key = "qcom,psci-mode-shift";
ret = of_property_read_u32(node, key, &c->cpu->psci_mode_shift);
if (ret) {
pr_err("Failed reading %s on device %s\n", key,
node->name);
return ret;
}
key = "qcom,psci-mode-mask";
ret = of_property_read_u32(node, key, &c->cpu->psci_mode_mask);
if (ret) {
pr_err("Failed reading %s on device %s\n", key,
node->name);
return ret;
}
}
for_each_child_of_node(node, n) {
struct lpm_cpu_level *l = &c->cpu->levels[c->cpu->nlevels];
c->cpu->nlevels++;
ret = parse_cpu_mode(n, l);
if (ret < 0) {
pr_info("Failed %s\n", l->name);
goto failed;
}
ret = parse_power_params(n, &l->pwr);
if (ret)
goto failed;
key = "qcom,use-broadcast-timer";
l->use_bc_timer = of_property_read_bool(n, key);
l->is_reset = of_property_read_bool(n, "qcom,is-reset");
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;
}
for (i = 0; i < c->cpu->nlevels; i++) {
for (j = 0; j < c->cpu->nlevels; j++) {
if (i >= j) {
c->cpu->levels[i].pwr.residencies[j] = 0;
continue;
}
c->cpu->levels[i].pwr.residencies[j] =
calculate_residency(&c->cpu->levels[i].pwr,
&c->cpu->levels[j].pwr);
pr_err("%s: idx %d %u\n", __func__, j,
c->cpu->levels[i].pwr.residencies[j]);
}
}
return 0;
failed:
of_node_put(n);
pr_err("%s(): Failed with error code:%d\n", __func__, ret);
return ret;
}
void free_cluster_node(struct lpm_cluster *cluster)
{
struct lpm_cluster *cl, *m;
list_for_each_entry_safe(cl, m, &cluster->child, list) {
list_del(&cl->list);
free_cluster_node(cl);
}
cluster->ndevices = 0;
}
/*
* TODO:
* Expects a CPU or a cluster only. This ensures that affinity
* level of a cluster is consistent with reference to its
* child nodes.
*/
static struct lpm_cluster *parse_cluster(struct device_node *node,
struct lpm_cluster *parent)
{
struct lpm_cluster *c;
struct device_node *n;
char *key;
int ret = 0;
int i, j;
c = devm_kzalloc(&lpm_pdev->dev, sizeof(*c), GFP_KERNEL);
if (!c)
return ERR_PTR(-ENOMEM);
ret = parse_cluster_params(node, c);
if (ret)
goto failed_parse_params;
INIT_LIST_HEAD(&c->child);
c->parent = parent;
spin_lock_init(&c->sync_lock);
c->min_child_level = NR_LPM_LEVELS;
for_each_child_of_node(node, n) {
if (!n->name)
continue;
key = "qcom,pm-cluster-level";
if (!of_node_cmp(n->name, key)) {
if (parse_cluster_level(n, c))
goto failed_parse_cluster;
continue;
}
key = "qcom,pm-cluster";
if (!of_node_cmp(n->name, key)) {
struct lpm_cluster *child;
if (c->no_saw_devices)
pr_info("%s: SAW device not provided.\n",
__func__);
child = parse_cluster(n, c);
if (!child)
goto failed_parse_cluster;
list_add(&child->list, &c->child);
cpumask_or(&c->child_cpus, &c->child_cpus,
&child->child_cpus);
c->aff_level = child->aff_level + 1;
continue;
}
key = "qcom,pm-cpu";
if (!of_node_cmp(n->name, key)) {
/*
* Parse the cpu node only if a pm-cpu node
* is available, though the mask is defined @ the
* cluster level
*/
if (get_cpumask_for_node(node, &c->child_cpus))
goto failed_parse_cluster;
if (parse_cpu_levels(n, c))
goto failed_parse_cluster;
c->aff_level = 1;
for_each_cpu(i, &c->child_cpus) {
per_cpu(max_residency, i) = devm_kzalloc(
&lpm_pdev->dev,
sizeof(uint32_t) * c->cpu->nlevels,
GFP_KERNEL);
if (!per_cpu(max_residency, i))
return ERR_PTR(-ENOMEM);
set_optimum_cpu_residency(c->cpu, i, true);
}
}
}
if (cpumask_intersects(&c->child_cpus, cpu_online_mask))
c->last_level = c->default_level;
else
c->last_level = c->nlevels-1;
for (i = 0; i < c->nlevels; i++) {
for (j = 0; j < c->nlevels; j++) {
if (i >= j) {
c->levels[i].pwr.residencies[j] = 0;
continue;
}
c->levels[i].pwr.residencies[j] = calculate_residency(
&c->levels[i].pwr, &c->levels[j].pwr);
}
}
set_optimum_cluster_residency(c, true);
return c;
failed_parse_cluster:
pr_err("Failed parse cluster:%s\n", key);
of_node_put(n);
if (parent)
list_del(&c->list);
free_cluster_node(c);
failed_parse_params:
pr_err("Failed parse params\n");
return NULL;
}
struct lpm_cluster *lpm_of_parse_cluster(struct platform_device *pdev)
{
struct device_node *top = NULL;
struct lpm_cluster *c;
use_psci = of_property_read_bool(pdev->dev.of_node, "qcom,use-psci");
top = of_find_node_by_name(pdev->dev.of_node, "qcom,pm-cluster");
if (!top) {
pr_err("Failed to find root node\n");
return ERR_PTR(-ENODEV);
}
lpm_pdev = pdev;
c = parse_cluster(top, NULL);
of_node_put(top);
return c;
}
void cluster_dt_walkthrough(struct lpm_cluster *cluster)
{
struct list_head *list;
int i, j;
static int id;
char str[10] = {0};
if (!cluster)
return;
for (i = 0; i < id; i++)
snprintf(str+i, 10 - i, "\t");
pr_info("%d\n", __LINE__);
for (i = 0; i < cluster->nlevels; i++) {
struct lpm_cluster_level *l = &cluster->levels[i];
pr_info("%d ndevices:%d\n", __LINE__, cluster->ndevices);
for (j = 0; j < cluster->ndevices; j++)
pr_info("%sDevice: %pK id:%pK\n", str,
&cluster->name[j], &l->mode[i]);
}
if (cluster->cpu) {
pr_info("%d\n", __LINE__);
for (j = 0; j < cluster->cpu->nlevels; j++)
pr_info("%s\tCPU mode: %s id:%d\n", str,
cluster->cpu->levels[j].name,
cluster->cpu->levels[j].mode);
}
id++;
list_for_each(list, &cluster->child) {
struct lpm_cluster *n;
pr_info("%d\n", __LINE__);
n = list_entry(list, typeof(*n), list);
cluster_dt_walkthrough(n);
}
id--;
}

View file

@ -835,6 +835,17 @@ config QTI_SYSTEM_PM
config QTI_SYSTEM_PM_RPM
bool
config MSM_PM_LEGACY
depends on PM
select MSM_IDLE_STATS if DEBUG_FS
select CPU_IDLE_MULTIPLE_DRIVERS
bool "Qualcomm Technologies, Inc. (QTI) platform specific Legacy PM driver"
help
Platform specific legacy power driver to manage
cores and l2 low power modes. It interface with
various system driver and put the cores into
low power modes.
config QTI_PLH_SCMI_CLIENT
tristate "Qualcomm Technologies Inc. SCMI client driver for PLH"
depends on QTI_PLH
@ -895,7 +906,7 @@ config QTI_HW_MEMLAT_LOG
This driver register with IPC_Logging framework, to have dedicated
buffer for memlat hw device.
if MSM_PM
if (MSM_PM || MSM_PM_LEGACY)
menuconfig MSM_IDLE_STATS
bool "Collect idle statistics"
help
@ -928,7 +939,7 @@ config MSM_SUSPEND_STATS_FIRST_BUCKET
histogram. This is for collecting statistics on suspend.
endif # MSM_IDLE_STATS
endif # MSM_PM
endif # MSM_PM || MSM_PM_LEGACY
config QTI_RPM_STATS_LOG
tristate "Qualcomm Technologies RPM Stats Driver"

View file

@ -113,3 +113,4 @@ obj-$(CONFIG_QCOM_ADSP_MANUAL_VOTE) += adsp_vote_qmi.o adsp_lpm_voting_v01.o
obj-$(CONFIG_MSM_SLATECOM) += slatecom_spi.o
obj-$(CONFIG_MSM_SLATECOM_INTERFACE) += slatecom_interface.o
obj-$(CONFIG_QCOM_AOP_SET_DDR) += aop-set-ddr.o
obj-$(CONFIG_CPU_V7) += idle-v7.o

View file

@ -0,0 +1,57 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2007 Google, Inc.
* Copyright (c) 2007-2009, 2011-2014, 2019, 2021 The Linux Foundation. All rights reserved.
*/
#include <linux/linkage.h>
#include <linux/threads.h>
#include <asm/assembler.h>
.arm
ENTRY(msm_pm_boot_entry)
THUMB( adr r9, BSYM(2f) ) /* Kernel is always entered in ARM. */
THUMB( bx r9 ) /* If this is a Thumb-2 kernel, */
THUMB( .thumb ) /* switch to Thumb now. */
THUMB(2: )
mrc p15, 0, r0, c0, c0, 5 /* MPIDR */
bic r0, #0xff000000 /* what CPU am I */
bic r0, #0x00ff0000 /* clear bits 31-16 */
adr r3, 3f
ldr r1, [r3]
sub r3, r1, r3 /* r3 holds the virt to phy offset */
ldr r1, =msm_pc_debug_counters_phys /* phys addr for IMEM reg */
sub r1, r1, r3 /* translate virt to phys */
ldr r1,[r1]
cmp r1, #0
beq skip_pc_debug3
mov r2, r0, lsr #8 /* get cluster num */
add r1, r1, r2, lsl #6 /* get offset of cluster */
and r2, r0, #0xff
add r1, r1, r2, lsl #4 /* get offset for the cpu */
add r1, #4 /* warmboot entry counter */
ldr r2, [r1]
add r2, #1
str r2, [r1]
skip_pc_debug3:
ldr r1, =msm_pm_boot_vector
sub r1, r1, r3 /* translate virt to phys */
mov r2, r0, lsr #8 /* get cluster num */
add r1, r1, r2, lsl #4 /* Get offset for the cluster */
and r0, r0, #0xff /* cpu id */
add r1, r1, r0, lsl #2 /* Get offset for the cpu */
ldr pc, [r1] /* jump */
ENDPROC(msm_pm_boot_entry)
3: .long .
.data
.globl msm_pm_boot_vector
msm_pm_boot_vector:
.space 4 * 4 * 4
.globl msm_pc_debug_counters_phys
msm_pc_debug_counters_phys:
.long 0x0

View file

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2018-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2020-2021, The Linux Foundation. All rights reserved.
*/
#ifndef __SOC_QCOM_LPM_LEVEL_H__
@ -19,7 +19,7 @@ struct system_pm_ops {
bool (*sleep_allowed)(void);
};
#ifdef CONFIG_MSM_PM
#if defined(CONFIG_MSM_PM) || defined(CONFIG_MSM_PM_LEGACY)
uint32_t register_system_pm_ops(struct system_pm_ops *pm_ops);
void update_ipi_history(int cpu);
#else

View file

@ -0,0 +1,219 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (C) 2007 Google, Inc.
* Copyright (c) 2009-2016, 2018-2021, The Linux Foundation. All rights reserved.
* Author: San Mehat <san@android.com>
*/
#ifndef __ARCH_ARM_MACH_MSM_PM_H
#define __ARCH_ARM_MACH_MSM_PM_H
#include <linux/types.h>
#include <linux/cpuidle.h>
#include <asm/smp_plat.h>
#include <asm/barrier.h>
#if !defined(CONFIG_SMP)
#define msm_secondary_startup NULL
#elif defined(CONFIG_CPU_V7)
#define msm_secondary_startup secondary_startup
#else
#define msm_secondary_startup secondary_holding_pen
#endif
#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
enum msm_pm_sleep_mode {
MSM_PM_SLEEP_MODE_WAIT_FOR_INTERRUPT,
MSM_PM_SLEEP_MODE_RETENTION,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
MSM_PM_SLEEP_MODE_FASTPC,
MSM_PM_SLEEP_MODE_POWER_COLLAPSE_SUSPEND,
MSM_PM_SLEEP_MODE_NR,
MSM_PM_SLEEP_MODE_NOT_SELECTED,
};
enum msm_pm_l2_scm_flag {
MSM_SCM_L2_ON = 0,
MSM_SCM_L2_OFF = 1,
MSM_SCM_L2_GDHS = 3,
MSM_SCM_L3_PC_OFF = 4,
};
#define MSM_PM_MODE(cpu, mode_nr) ((cpu) *MSM_PM_SLEEP_MODE_NR + (mode_nr))
struct msm_pm_time_params {
uint32_t latency_us;
uint32_t sleep_us;
uint32_t next_event_us;
uint32_t modified_time_us;
};
struct msm_pm_sleep_status_data {
void __iomem *base_addr;
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
*
* @cpu: cpuid of the CPU going down.
*
* Returns the l2 flush flag enum that is passed down to TZ during power
* collaps
*/
enum msm_pm_l2_scm_flag lpm_cpu_pre_pc_cb(unsigned int cpu);
/**
* msm_pm_sleep_mode_allow() - API to determine if sleep mode is allowed.
* @cpu: CPU on which to check for the sleep mode.
* @mode: Sleep Mode to check for.
* @idle: Idle or Suspend Sleep Mode.
*
* Helper function to determine if a Idle or Suspend
* Sleep mode is allowed for a specific CPU.
*
* Return: 1 for allowed; 0 if not allowed.
*/
int msm_pm_sleep_mode_allow(unsigned int cpu, unsigned int mode, bool idle);
/**
* msm_pm_sleep_mode_supported() - API to determine if sleep mode is
* supported.
* @cpu: CPU on which to check for the sleep mode.
* @mode: Sleep Mode to check for.
* @idle: Idle or Suspend Sleep Mode.
*
* Helper function to determine if a Idle or Suspend
* Sleep mode is allowed and enabled for a specific CPU.
*
* Return: 1 for supported; 0 if not supported.
*/
int msm_pm_sleep_mode_supported(unsigned int cpu, unsigned int mode, bool idle);
struct msm_pm_cpr_ops {
void (*cpr_suspend)(void);
void (*cpr_resume)(void);
};
void __init msm_pm_set_tz_retention_flag(unsigned int flag);
void msm_pm_enable_retention(bool enable);
bool msm_pm_retention_enabled(void);
bool msm_cpu_pm_enter_sleep(enum msm_pm_sleep_mode mode, bool from_idle);
static inline void msm_arch_idle(void)
{
/* memory barrier */
mb();
wfi();
}
#ifdef CONFIG_MSM_PM_LEGACY
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; }
static inline int msm_pm_sleep_status_init(void) { return 0; };
static inline void lpm_cpu_hotplug_enter(unsigned int cpu)
{
msm_arch_idle();
};
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
void qcom_cpu_die_legacy(unsigned int cpu);
int qcom_cpu_kill_legacy(unsigned int cpu);
int msm_platform_secondary_init(unsigned int cpu);
#else
static inline int msm_platform_secondary_init(unsigned int cpu) { return 0; }
static inline void qcom_cpu_die_legacy(unsigned int cpu) {}
static inline int qcom_cpu_kill_legacy(unsigned int cpu) { return 0; }
#endif
enum msm_pm_time_stats_id {
MSM_PM_STAT_REQUESTED_IDLE = 0,
MSM_PM_STAT_IDLE_SPIN,
MSM_PM_STAT_IDLE_WFI,
MSM_PM_STAT_RETENTION,
MSM_PM_STAT_IDLE_STANDALONE_POWER_COLLAPSE,
MSM_PM_STAT_IDLE_FAILED_STANDALONE_POWER_COLLAPSE,
MSM_PM_STAT_IDLE_POWER_COLLAPSE,
MSM_PM_STAT_IDLE_FAILED_POWER_COLLAPSE,
MSM_PM_STAT_SUSPEND,
MSM_PM_STAT_FAILED_SUSPEND,
MSM_PM_STAT_NOT_IDLE,
MSM_PM_STAT_COUNT
};
#ifdef CONFIG_MSM_IDLE_STATS
void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats, int size);
void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t);
void msm_pm_l2_add_stat(uint32_t id, int64_t t);
#else
static inline void msm_pm_add_stats(enum msm_pm_time_stats_id *enable_stats,
int size) {}
static inline void msm_pm_add_stat(enum msm_pm_time_stats_id id, int64_t t) {}
static inline void msm_pm_l2_add_stat(uint32_t id, int64_t t) {}
#endif
void msm_pm_set_cpr_ops(struct msm_pm_cpr_ops *ops);
extern dma_addr_t msm_pc_debug_counters_phys;
#endif /* __ARCH_ARM_MACH_MSM_PM_H */

View file

@ -1,7 +1,7 @@
/* SPDX-License-Identifier: GPL-2.0-only */
/*
* Copyright (c) 2016-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2016-2021, The Linux Foundation. All rights reserved.
*/
#undef TRACE_SYSTEM
@ -261,6 +261,25 @@ TRACE_EVENT(ipi_wakeup_time,
TP_printk("wakeup:%llu", __entry->wakeup)
);
TRACE_EVENT(pre_pc_cb,
TP_PROTO(int tzflag),
TP_ARGS(tzflag),
TP_STRUCT__entry(
__field(int, tzflag)
),
TP_fast_assign(
__entry->tzflag = tzflag;
),
TP_printk("tzflag:%d",
__entry->tzflag
)
);
#endif
#define TRACE_INCLUDE_FILE trace_msm_low_power
#include <trace/define_trace.h>