1 // SPDX-License-Identifier: GPL-2.0-or-later
3 /* Platform profile sysfs interface */
5 #include <linux/acpi.h>
6 #include <linux/bits.h>
7 #include <linux/init.h>
8 #include <linux/mutex.h>
9 #include <linux/platform_profile.h>
10 #include <linux/sysfs.h>
12 static struct platform_profile_handler *cur_profile;
13 static DEFINE_MUTEX(profile_lock);
15 static const char * const profile_names[] = {
16 [PLATFORM_PROFILE_LOW_POWER] = "low-power",
17 [PLATFORM_PROFILE_COOL] = "cool",
18 [PLATFORM_PROFILE_QUIET] = "quiet",
19 [PLATFORM_PROFILE_BALANCED] = "balanced",
20 [PLATFORM_PROFILE_PERFORMANCE] = "performance",
22 static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
24 static ssize_t platform_profile_choices_show(struct device *dev,
25 struct device_attribute *attr,
31 err = mutex_lock_interruptible(&profile_lock);
36 mutex_unlock(&profile_lock);
40 for_each_set_bit(i, cur_profile->choices, PLATFORM_PROFILE_LAST) {
42 len += sysfs_emit_at(buf, len, "%s", profile_names[i]);
44 len += sysfs_emit_at(buf, len, " %s", profile_names[i]);
46 len += sysfs_emit_at(buf, len, "\n");
47 mutex_unlock(&profile_lock);
51 static ssize_t platform_profile_show(struct device *dev,
52 struct device_attribute *attr,
55 enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
58 err = mutex_lock_interruptible(&profile_lock);
63 mutex_unlock(&profile_lock);
67 err = cur_profile->profile_get(cur_profile, &profile);
68 mutex_unlock(&profile_lock);
72 /* Check that profile is valid index */
73 if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
76 return sysfs_emit(buf, "%s\n", profile_names[profile]);
79 static ssize_t platform_profile_store(struct device *dev,
80 struct device_attribute *attr,
81 const char *buf, size_t count)
85 err = mutex_lock_interruptible(&profile_lock);
90 mutex_unlock(&profile_lock);
94 /* Scan for a matching profile */
95 i = sysfs_match_string(profile_names, buf);
97 mutex_unlock(&profile_lock);
101 /* Check that platform supports this profile choice */
102 if (!test_bit(i, cur_profile->choices)) {
103 mutex_unlock(&profile_lock);
107 err = cur_profile->profile_set(cur_profile, i);
108 mutex_unlock(&profile_lock);
114 static DEVICE_ATTR_RO(platform_profile_choices);
115 static DEVICE_ATTR_RW(platform_profile);
117 static struct attribute *platform_profile_attrs[] = {
118 &dev_attr_platform_profile_choices.attr,
119 &dev_attr_platform_profile.attr,
123 static const struct attribute_group platform_profile_group = {
124 .attrs = platform_profile_attrs
127 void platform_profile_notify(void)
131 sysfs_notify(acpi_kobj, NULL, "platform_profile");
133 EXPORT_SYMBOL_GPL(platform_profile_notify);
135 int platform_profile_register(struct platform_profile_handler *pprof)
139 mutex_lock(&profile_lock);
140 /* We can only have one active profile */
142 mutex_unlock(&profile_lock);
146 /* Sanity check the profile handler field are set */
147 if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) ||
148 !pprof->profile_set || !pprof->profile_get) {
149 mutex_unlock(&profile_lock);
153 err = sysfs_create_group(acpi_kobj, &platform_profile_group);
155 mutex_unlock(&profile_lock);
160 mutex_unlock(&profile_lock);
163 EXPORT_SYMBOL_GPL(platform_profile_register);
165 int platform_profile_remove(void)
167 sysfs_remove_group(acpi_kobj, &platform_profile_group);
169 mutex_lock(&profile_lock);
171 mutex_unlock(&profile_lock);
174 EXPORT_SYMBOL_GPL(platform_profile_remove);
176 MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
177 MODULE_LICENSE("GPL");