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_BALANCED_PERFORMANCE
] = "balanced-performance",
21 [PLATFORM_PROFILE_PERFORMANCE
] = "performance",
23 static_assert(ARRAY_SIZE(profile_names
) == PLATFORM_PROFILE_LAST
);
25 static ssize_t
platform_profile_choices_show(struct device
*dev
,
26 struct device_attribute
*attr
,
32 err
= mutex_lock_interruptible(&profile_lock
);
37 mutex_unlock(&profile_lock
);
41 for_each_set_bit(i
, cur_profile
->choices
, PLATFORM_PROFILE_LAST
) {
43 len
+= sysfs_emit_at(buf
, len
, "%s", profile_names
[i
]);
45 len
+= sysfs_emit_at(buf
, len
, " %s", profile_names
[i
]);
47 len
+= sysfs_emit_at(buf
, len
, "\n");
48 mutex_unlock(&profile_lock
);
52 static ssize_t
platform_profile_show(struct device
*dev
,
53 struct device_attribute
*attr
,
56 enum platform_profile_option profile
= PLATFORM_PROFILE_BALANCED
;
59 err
= mutex_lock_interruptible(&profile_lock
);
64 mutex_unlock(&profile_lock
);
68 err
= cur_profile
->profile_get(cur_profile
, &profile
);
69 mutex_unlock(&profile_lock
);
73 /* Check that profile is valid index */
74 if (WARN_ON((profile
< 0) || (profile
>= ARRAY_SIZE(profile_names
))))
77 return sysfs_emit(buf
, "%s\n", profile_names
[profile
]);
80 static ssize_t
platform_profile_store(struct device
*dev
,
81 struct device_attribute
*attr
,
82 const char *buf
, size_t count
)
86 err
= mutex_lock_interruptible(&profile_lock
);
91 mutex_unlock(&profile_lock
);
95 /* Scan for a matching profile */
96 i
= sysfs_match_string(profile_names
, buf
);
98 mutex_unlock(&profile_lock
);
102 /* Check that platform supports this profile choice */
103 if (!test_bit(i
, cur_profile
->choices
)) {
104 mutex_unlock(&profile_lock
);
108 err
= cur_profile
->profile_set(cur_profile
, i
);
110 sysfs_notify(acpi_kobj
, NULL
, "platform_profile");
112 mutex_unlock(&profile_lock
);
118 static DEVICE_ATTR_RO(platform_profile_choices
);
119 static DEVICE_ATTR_RW(platform_profile
);
121 static struct attribute
*platform_profile_attrs
[] = {
122 &dev_attr_platform_profile_choices
.attr
,
123 &dev_attr_platform_profile
.attr
,
127 static const struct attribute_group platform_profile_group
= {
128 .attrs
= platform_profile_attrs
131 void platform_profile_notify(void)
135 sysfs_notify(acpi_kobj
, NULL
, "platform_profile");
137 EXPORT_SYMBOL_GPL(platform_profile_notify
);
139 int platform_profile_cycle(void)
141 enum platform_profile_option profile
;
142 enum platform_profile_option next
;
145 err
= mutex_lock_interruptible(&profile_lock
);
150 mutex_unlock(&profile_lock
);
154 err
= cur_profile
->profile_get(cur_profile
, &profile
);
156 mutex_unlock(&profile_lock
);
160 next
= find_next_bit_wrap(cur_profile
->choices
, PLATFORM_PROFILE_LAST
,
163 if (WARN_ON(next
== PLATFORM_PROFILE_LAST
)) {
164 mutex_unlock(&profile_lock
);
168 err
= cur_profile
->profile_set(cur_profile
, next
);
169 mutex_unlock(&profile_lock
);
172 sysfs_notify(acpi_kobj
, NULL
, "platform_profile");
176 EXPORT_SYMBOL_GPL(platform_profile_cycle
);
178 int platform_profile_register(struct platform_profile_handler
*pprof
)
182 mutex_lock(&profile_lock
);
183 /* We can only have one active profile */
185 mutex_unlock(&profile_lock
);
189 /* Sanity check the profile handler field are set */
190 if (!pprof
|| bitmap_empty(pprof
->choices
, PLATFORM_PROFILE_LAST
) ||
191 !pprof
->profile_set
|| !pprof
->profile_get
) {
192 mutex_unlock(&profile_lock
);
196 err
= sysfs_create_group(acpi_kobj
, &platform_profile_group
);
198 mutex_unlock(&profile_lock
);
203 mutex_unlock(&profile_lock
);
206 EXPORT_SYMBOL_GPL(platform_profile_register
);
208 int platform_profile_remove(void)
210 sysfs_remove_group(acpi_kobj
, &platform_profile_group
);
212 mutex_lock(&profile_lock
);
214 mutex_unlock(&profile_lock
);
217 EXPORT_SYMBOL_GPL(platform_profile_remove
);
219 MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");
220 MODULE_DESCRIPTION("ACPI platform profile sysfs interface");
221 MODULE_LICENSE("GPL");