Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit ecf1cb6c authored by Linux Build Service Account's avatar Linux Build Service Account Committed by Gerrit - the friendly Code Review server
Browse files

Merge "msm: lpm_levels: Allow enable/disable LPM for cpus and clusters"

parents 57aaaddd dfbc6bef
Loading
Loading
Loading
Loading
+233 −1
Original line number Diff line number Diff line
@@ -15,12 +15,245 @@
#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.h"

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 struct lpm_level_avail *cpu_level_available[NR_CPUS];
static struct platform_device *lpm_pdev;

static void *get_avail_val(struct kobject *kobj, struct kobj_attribute *attr)
{
	void *arg = NULL;
	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);
		arg = (void *) &avail->idle_enabled;
	} else if (!strcmp(attr->attr.name, lpm_types[SUSPEND].str)) {
		avail = container_of(attr, struct lpm_level_avail,
					suspend_enabled_attr);
		arg = (void *) &avail->suspend_enabled;
	}

	return arg;
}

ssize_t lpm_enable_show(struct kobject *kobj, struct kobj_attribute *attr,
				char *buf)
{
	int ret = 0;
	struct kernel_param kp;

	kp.arg = get_avail_val(kobj, attr);
	ret = param_get_bool(buf, &kp);
	if (ret > 0) {
		strlcat(buf, "\n", PAGE_SIZE);
		ret++;
	}

	return ret;
}

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;

	kp.arg = get_avail_val(kobj, attr);
	ret = param_set_bool(buf, &kp);

	return ret ? ret : len;
}

static int create_lvl_avail_nodes(const char *name,
			struct kobject *parent, struct lpm_level_avail *avail)
{
	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;
	}

	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;

	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;

	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, j;
	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;

	for_each_cpu(cpu, &p->child_cpus) {
		snprintf(cpu_name, sizeof(cpu_name), "cpu%d", cpu);
		cpu_kobj[cpu] = kobject_create_and_add(cpu_name, parent);
		if (!cpu_kobj[cpu])
			return -ENOMEM;

		level_list = devm_kzalloc(&lpm_pdev->dev,
				MSM_PM_SLEEP_MODE_NR * sizeof(*level_list),
				GFP_KERNEL);
		if (!level_list)
			return -ENOMEM;

		for (i = 0; i < MSM_PM_SLEEP_MODE_NR; i++) {
			for (j = 0; j < p->cpu->nlevels; j++)
				if (p->cpu->levels[j].mode == i)
					break;
			if (j == p->cpu->nlevels) {
				/* Level not defined in DT */
				level_list[i].idle_enabled = false;
				level_list[i].suspend_enabled = false;
				continue;
			}

			ret = create_lvl_avail_nodes(p->cpu->levels[j].name,
						cpu_kobj[cpu], &level_list[i]);
			if (ret)
				return ret;
		}

		cpu_level_available[cpu] = level_list;
	}

	return 0;

}

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);
		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 mode, bool from_idle)
{
	struct lpm_level_avail *avail = cpu_level_available[cpu];

	if (!lpm_pdev || !avail)
		return !from_idle;

	return !!(from_idle ? avail[mode].idle_enabled :
				avail[mode].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_cluster_params(struct device_node *node, struct lpm_cluster *c)
{
	int i;
@@ -194,7 +427,6 @@ static int parse_cluster_level(struct device_node *node,
			cluster->min_child_level = level->min_child_level;
	}

	level->available = true;
	level->notify_rpm = of_property_read_bool(node, "qcom,notify-rpm");
	level->last_core_only = of_property_read_bool(node,
					"qcom,last-core-only");
+24 −6
Original line number Diff line number Diff line
@@ -31,6 +31,7 @@
#include <linux/msm_remote_spinlock.h>
#include <linux/dma-mapping.h>
#include <linux/coresight-cti.h>
#include <linux/moduleparam.h>
#include <soc/qcom/spm.h>
#include <soc/qcom/pm.h>
#include <soc/qcom/rpm-notifier.h>
@@ -68,7 +69,8 @@ struct lpm_debug {
	uint32_t arg4;
};

static struct lpm_cluster *lpm_root_node;
struct lpm_cluster *lpm_root_node;

static DEFINE_PER_CPU(struct lpm_cluster*, cpu_cluster);
static bool suspend_in_progress;
static struct hrtimer lpm_hrtimer;
@@ -253,7 +255,7 @@ static int cpu_power_select(struct cpuidle_device *dev,
		enum msm_pm_sleep_mode mode = level->mode;
		bool allow;

		allow = msm_pm_sleep_mode_allow(dev->cpu, mode, true);
		allow = lpm_cpu_mode_allow(dev->cpu, mode, true);

		if (!allow)
			continue;
@@ -374,7 +376,7 @@ static int cluster_select(struct lpm_cluster *cluster, bool from_idle)
		struct lpm_cluster_level *level = &cluster->levels[i];
		struct power_params *pwr_params = &level->pwr;

		if (!level->available)
		if (!lpm_cluster_mode_allow(cluster, i, from_idle))
			continue;

		if (level->last_core_only &&
@@ -801,7 +803,7 @@ static int lpm_suspend_enter(suspend_state_t state)
	for (idx = lpm_cpu->nlevels - 1; idx >= 0; idx--) {
		struct lpm_cpu_level *level = &lpm_cpu->levels[idx];

		if (msm_pm_sleep_mode_allow(cpu, level->mode, false))
		if (lpm_cpu_mode_allow(cpu, level->mode, false))
			break;
	}
	if (idx < 0) {
@@ -829,6 +831,7 @@ static int lpm_probe(struct platform_device *pdev)
	int size;
	struct lpm_cluster *p = NULL;
	int cpu;
	struct kobject *module_kobj = NULL;

	lpm_root_node = lpm_of_parse_cluster(pdev);

@@ -884,6 +887,21 @@ static int lpm_probe(struct platform_device *pdev)
		goto failed;
	}

	module_kobj = kset_find_obj(module_kset, KBUILD_MODNAME);
	if (!module_kobj) {
		pr_err("%s: cannot find kobject for module %s\n",
			__func__, KBUILD_MODNAME);
		ret = -ENOENT;
		goto failed;
	}

	ret = create_cluster_lvl_nodes(lpm_root_node, module_kobj);
	if (ret) {
		pr_err("%s(): Failed to create cluster level nodes\n",
				__func__);
		goto failed;
	}

	return 0;
failed:
	free_cluster_node(lpm_root_node);
@@ -972,10 +990,10 @@ void lpm_cpu_hotplug_enter(unsigned int cpu)
	int i;
	int idx = -1;

	if (msm_pm_sleep_mode_allow(cpu, MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
	if (lpm_cpu_mode_allow(cpu, MSM_PM_SLEEP_MODE_POWER_COLLAPSE,
				false))
		mode = MSM_PM_SLEEP_MODE_POWER_COLLAPSE;
	else if (msm_pm_sleep_mode_allow(cpu,
	else if (lpm_cpu_mode_allow(cpu,
			MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE, false))
		mode = MSM_PM_SLEEP_MODE_POWER_COLLAPSE_STANDALONE;
	else
+17 −1
Original line number Diff line number Diff line
@@ -40,6 +40,14 @@ struct lpm_cpu {
	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;
};

struct lpm_cluster_level {
	const char *level_name;
	int *mode;			/* SPM mode to enter */
@@ -47,9 +55,9 @@ struct lpm_cluster_level {
	struct cpumask num_cpu_votes;
	struct power_params pwr;
	bool notify_rpm;
	bool available;
	bool sync_level;
	bool last_core_only;
	struct lpm_level_avail available;
};

struct low_power_ops {
@@ -87,3 +95,11 @@ int set_cci_mode(struct low_power_ops *ops, int mode, bool notify_rpm);
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);

extern struct lpm_cluster *lpm_root_node;
+0 −1
Original line number Diff line number Diff line
@@ -839,7 +839,6 @@ static int msm_cpu_pm_probe(struct platform_device *pdev)
		}
	}

	msm_pm_mode_sysfs_add(KBUILD_MODNAME);
	if (pdev->dev.of_node)
		of_platform_populate(pdev->dev.of_node, NULL, NULL, &pdev->dev);

+0 −2
Original line number Diff line number Diff line
@@ -61,8 +61,6 @@ struct msm_pm_sleep_status_data {
	uint32_t mask;
};

int msm_pm_mode_sysfs_add(const char *);

/**
 * lpm_cpu_pre_pc_cb(): API to get the L2 flag to pass to TZ
 *