From b1c3b0ab60002bc875d45118a33254c8d29a32f0 Mon Sep 17 00:00:00 2001 From: Park Bumgyu Date: Thu, 12 Apr 2018 13:30:20 +0900 Subject: [PATCH] soc: samsung: cpupm: support sysfs to enable/disable power mode Change-Id: I8f0384bb511a8d9c4aaa57329808e65766ce8b03 Signed-off-by: Park Bumgyu --- drivers/soc/samsung/exynos-cpupm.c | 111 ++++++++++++++++++++++++++++- 1 file changed, 110 insertions(+), 1 deletion(-) diff --git a/drivers/soc/samsung/exynos-cpupm.c b/drivers/soc/samsung/exynos-cpupm.c index c72c36f22e42..fc2c7affab08 100644 --- a/drivers/soc/samsung/exynos-cpupm.c +++ b/drivers/soc/samsung/exynos-cpupm.c @@ -396,6 +396,15 @@ struct power_mode { * depending on system idle state */ bool system_idle; + + /* + * kobject attribute for sysfs, + * it supports for enabling or disabling this power mode + */ + struct kobj_attribute attr; + + /* user's request for enabling/disabling power mode */ + bool user_request; }; /* Maximum number of power modes manageable per cpu */ @@ -684,6 +693,87 @@ void exynos_cpu_pm_exit(int cpu, int cancel) spin_unlock(&cpupm_lock); } +/****************************************************************************** + * sysfs interface * + ******************************************************************************/ +static ssize_t show_power_mode(struct kobject *kobj, + struct kobj_attribute *attr, char *buf) +{ + struct power_mode *mode = container_of(attr, struct power_mode, attr); + + return sprintf(buf, "%s\n", + atomic_read(&mode->disable) > 0 ? "disabled" : "enabled"); +} + +static ssize_t store_power_mode(struct kobject *kobj, + struct kobj_attribute *attr, const char *buf, + size_t count) +{ + unsigned int val; + int cpu, type; + struct power_mode *mode = container_of(attr, struct power_mode, attr); + + if (!sscanf(buf, "%u", &val)) + return -EINVAL; + + cpu = cpumask_any(&mode->siblings); + type = mode->type; + + val = !!val; + if (mode->user_request == val) + return count; + + mode->user_request = val; + if (val) + enable_power_mode(cpu, type); + else + disable_power_mode(cpu, type); + + return count; +} + +/* + * attr_pool is used to create sysfs node at initialization time. Saving the + * initiailized attr to attr_pool, and it creates nodes of each attr at the + * time of sysfs creation. 10 is the appropriate value for the size of + * attr_pool. + */ +static struct attribute *attr_pool[10]; + +static struct kobject *cpupm_kobj; +static struct attribute_group attr_group; + +static void cpupm_sysfs_node_init(int attr_count) +{ + attr_group.attrs = kcalloc(attr_count + 1, + sizeof(struct attribute *), GFP_KERNEL); + if (!attr_group.attrs) + return; + + memcpy(attr_group.attrs, attr_pool, + sizeof(struct attribute *) * attr_count); + + cpupm_kobj = kobject_create_and_add("cpupm", power_kobj); + if (!cpupm_kobj) + goto out; + + if (sysfs_create_group(cpupm_kobj, &attr_group)) + goto out; + + return; + +out: + kfree(attr_group.attrs); +} + +#define cpupm_attr_init(_attr, _name, _index) \ + sysfs_attr_init(&_attr.attr); \ + _attr.attr.name = _name; \ + _attr.attr.mode = VERIFY_OCTAL_PERMISSIONS(0644); \ + _attr.show = show_power_mode; \ + _attr.store = store_power_mode; \ + attr_pool[_index] = &_attr.attr; + /****************************************************************************** * Initialization * ******************************************************************************/ @@ -708,7 +798,7 @@ static int __init cpu_power_mode_init(void) struct device_node *dn = NULL; struct power_mode *mode; const char *buf; - int id = 0; + int id = 0, attr_count = 0; while ((dn = of_find_node_by_type(dn, "cpupm"))) { int cpu; @@ -741,6 +831,22 @@ static int __init cpu_power_mode_init(void) atomic_set(&mode->disable, 0); + /* + * The users' request is set to enable since initialization state of + * power mode is enabled. + */ + mode->user_request = true; + + /* + * Initialize attribute for sysfs. + * The absence of entry allowed cpu is equivalent to this power + * mode being disabled. In this case, no attribute is created. + */ + if (!cpumask_empty(&mode->entry_allowed)) { + cpupm_attr_init(mode->attr, mode->name, attr_count); + attr_count++; + } + /* Connect power mode to the cpus in the power domain */ for_each_cpu(cpu, &mode->siblings) add_mode(per_cpu(cpupm, cpu).modes, mode); @@ -748,6 +854,9 @@ static int __init cpu_power_mode_init(void) cpuidle_profile_group_idle_register(mode->id, mode->name); } + if (attr_count) + cpupm_sysfs_node_init(attr_count); + return 0; } -- 2.20.1