mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-07 20:33:58 -04:00
Merge "lpm_levels_legacy: Rectified compilation errors"
This commit is contained in:
commit
2fd974a64c
10 changed files with 2939 additions and 5 deletions
|
|
@ -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
|
||||
|
|
|
|||
1497
drivers/cpuidle/lpm-levels-legacy.c
Normal file
1497
drivers/cpuidle/lpm-levels-legacy.c
Normal file
File diff suppressed because it is too large
Load diff
127
drivers/cpuidle/lpm-levels-legacy.h
Normal file
127
drivers/cpuidle/lpm-levels-legacy.h
Normal 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;
|
||||
999
drivers/cpuidle/lpm-levels-of-legacy.c
Normal file
999
drivers/cpuidle/lpm-levels-of-legacy.c
Normal 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--;
|
||||
}
|
||||
|
|
@ -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"
|
||||
|
|
|
|||
|
|
@ -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
|
||||
|
|
|
|||
57
drivers/soc/qcom/idle-v7.S
Normal file
57
drivers/soc/qcom/idle-v7.S
Normal 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
|
||||
|
|
@ -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
|
||||
|
|
|
|||
219
include/soc/qcom/pm-legacy.h
Normal file
219
include/soc/qcom/pm-legacy.h
Normal 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 */
|
||||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue