1 // SPDX-License-Identifier: GPL-2.0-or-later
2 
3 /* Platform profile sysfs interface */
4 
5 #include <linux/acpi.h>
6 #include <linux/bits.h>
7 #include <linux/init.h>
8 #include <linux/kdev_t.h>
9 #include <linux/mutex.h>
10 #include <linux/platform_profile.h>
11 #include <linux/sysfs.h>
12 
13 static DEFINE_MUTEX(profile_lock);
14 
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",
22 	[PLATFORM_PROFILE_CUSTOM] = "custom",
23 };
24 static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
25 
26 static DEFINE_IDA(platform_profile_ida);
27 
28 /**
29  * _commmon_choices_show - Show the available profile choices
30  * @choices: The available profile choices
31  * @buf: The buffer to write to
32  *
33  * Return: The number of bytes written
34  */
35 static ssize_t _commmon_choices_show(unsigned long *choices, char *buf)
36 {
37 	int i, len = 0;
38 
39 	for_each_set_bit(i, choices, PLATFORM_PROFILE_LAST) {
40 		if (len == 0)
41 			len += sysfs_emit_at(buf, len, "%s", profile_names[i]);
42 		else
43 			len += sysfs_emit_at(buf, len, " %s", profile_names[i]);
44 	}
45 	len += sysfs_emit_at(buf, len, "\n");
46 
47 	return len;
48 }
49 
50 /**
51  * _store_class_profile - Set the profile for a class device
52  * @dev: The class device
53  * @data: The profile to set
54  *
55  * Return: 0 on success, -errno on failure
56  */
57 static int _store_class_profile(struct device *dev, void *data)
58 {
59 	struct platform_profile_handler *handler;
60 	int *bit = (int *)data;
61 
62 	lockdep_assert_held(&profile_lock);
63 	handler = dev_get_drvdata(dev);
64 	if (!test_bit(*bit, handler->choices))
65 		return -EOPNOTSUPP;
66 
67 	return handler->profile_set(handler, *bit);
68 }
69 
70 /**
71  * _notify_class_profile - Notify the class device of a profile change
72  * @dev: The class device
73  * @data: Unused
74  *
75  * Return: 0 on success, -errno on failure
76  */
77 static int _notify_class_profile(struct device *dev, void *data)
78 {
79 	struct platform_profile_handler *handler = dev_get_drvdata(dev);
80 
81 	lockdep_assert_held(&profile_lock);
82 	sysfs_notify(&handler->class_dev->kobj, NULL, "profile");
83 	kobject_uevent(&handler->class_dev->kobj, KOBJ_CHANGE);
84 
85 	return 0;
86 }
87 
88 /**
89  * get_class_profile - Show the current profile for a class device
90  * @dev: The class device
91  * @profile: The profile to return
92  *
93  * Return: 0 on success, -errno on failure
94  */
95 static int get_class_profile(struct device *dev,
96 			     enum platform_profile_option *profile)
97 {
98 	struct platform_profile_handler *handler;
99 	enum platform_profile_option val;
100 	int err;
101 
102 	lockdep_assert_held(&profile_lock);
103 	handler = dev_get_drvdata(dev);
104 	err = handler->profile_get(handler, &val);
105 	if (err) {
106 		pr_err("Failed to get profile for handler %s\n", handler->name);
107 		return err;
108 	}
109 
110 	if (WARN_ON(val >= PLATFORM_PROFILE_LAST))
111 		return -EINVAL;
112 	*profile = val;
113 
114 	return 0;
115 }
116 
117 /**
118  * name_show - Show the name of the profile handler
119  * @dev: The device
120  * @attr: The attribute
121  * @buf: The buffer to write to
122  *
123  * Return: The number of bytes written
124  */
125 static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf)
126 {
127 	struct platform_profile_handler *handler = dev_get_drvdata(dev);
128 
129 	return sysfs_emit(buf, "%s\n", handler->name);
130 }
131 static DEVICE_ATTR_RO(name);
132 
133 /**
134  * choices_show - Show the available profile choices
135  * @dev: The device
136  * @attr: The attribute
137  * @buf: The buffer to write to
138  *
139  * Return: The number of bytes written
140  */
141 static ssize_t choices_show(struct device *dev,
142 			    struct device_attribute *attr,
143 			    char *buf)
144 {
145 	struct platform_profile_handler *handler = dev_get_drvdata(dev);
146 
147 	return _commmon_choices_show(handler->choices, buf);
148 }
149 static DEVICE_ATTR_RO(choices);
150 
151 /**
152  * profile_show - Show the current profile for a class device
153  * @dev: The device
154  * @attr: The attribute
155  * @buf: The buffer to write to
156  *
157  * Return: The number of bytes written
158  */
159 static ssize_t profile_show(struct device *dev,
160 			    struct device_attribute *attr,
161 			    char *buf)
162 {
163 	enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
164 	int err;
165 
166 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
167 		err = get_class_profile(dev, &profile);
168 		if (err)
169 			return err;
170 	}
171 
172 	return sysfs_emit(buf, "%s\n", profile_names[profile]);
173 }
174 
175 /**
176  * profile_store - Set the profile for a class device
177  * @dev: The device
178  * @attr: The attribute
179  * @buf: The buffer to read from
180  * @count: The number of bytes to read
181  *
182  * Return: The number of bytes read
183  */
184 static ssize_t profile_store(struct device *dev,
185 			     struct device_attribute *attr,
186 			     const char *buf, size_t count)
187 {
188 	int index, ret;
189 
190 	index = sysfs_match_string(profile_names, buf);
191 	if (index < 0)
192 		return -EINVAL;
193 
194 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
195 		ret = _store_class_profile(dev, &index);
196 		if (ret)
197 			return ret;
198 	}
199 
200 	sysfs_notify(acpi_kobj, NULL, "platform_profile");
201 
202 	return count;
203 }
204 static DEVICE_ATTR_RW(profile);
205 
206 static struct attribute *profile_attrs[] = {
207 	&dev_attr_name.attr,
208 	&dev_attr_choices.attr,
209 	&dev_attr_profile.attr,
210 	NULL
211 };
212 ATTRIBUTE_GROUPS(profile);
213 
214 static const struct class platform_profile_class = {
215 	.name = "platform-profile",
216 	.dev_groups = profile_groups,
217 };
218 
219 /**
220  * _aggregate_choices - Aggregate the available profile choices
221  * @dev: The device
222  * @data: The available profile choices
223  *
224  * Return: 0 on success, -errno on failure
225  */
226 static int _aggregate_choices(struct device *dev, void *data)
227 {
228 	struct platform_profile_handler *handler;
229 	unsigned long *aggregate = data;
230 
231 	lockdep_assert_held(&profile_lock);
232 	handler = dev_get_drvdata(dev);
233 	if (test_bit(PLATFORM_PROFILE_LAST, aggregate))
234 		bitmap_copy(aggregate, handler->choices, PLATFORM_PROFILE_LAST);
235 	else
236 		bitmap_and(aggregate, handler->choices, aggregate, PLATFORM_PROFILE_LAST);
237 
238 	return 0;
239 }
240 
241 /**
242  * platform_profile_choices_show - Show the available profile choices for legacy sysfs interface
243  * @dev: The device
244  * @attr: The attribute
245  * @buf: The buffer to write to
246  *
247  * Return: The number of bytes written
248  */
249 static ssize_t platform_profile_choices_show(struct device *dev,
250 					     struct device_attribute *attr,
251 					     char *buf)
252 {
253 	unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
254 	int err;
255 
256 	set_bit(PLATFORM_PROFILE_LAST, aggregate);
257 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
258 		err = class_for_each_device(&platform_profile_class, NULL,
259 					    aggregate, _aggregate_choices);
260 		if (err)
261 			return err;
262 	}
263 
264 	/* no profile handler registered any more */
265 	if (bitmap_empty(aggregate, PLATFORM_PROFILE_LAST))
266 		return -EINVAL;
267 
268 	return _commmon_choices_show(aggregate, buf);
269 }
270 
271 /**
272  * _aggregate_profiles - Aggregate the profiles for legacy sysfs interface
273  * @dev: The device
274  * @data: The profile to return
275  *
276  * Return: 0 on success, -errno on failure
277  */
278 static int _aggregate_profiles(struct device *dev, void *data)
279 {
280 	enum platform_profile_option *profile = data;
281 	enum platform_profile_option val;
282 	int err;
283 
284 	err = get_class_profile(dev, &val);
285 	if (err)
286 		return err;
287 
288 	if (*profile != PLATFORM_PROFILE_LAST && *profile != val)
289 		*profile = PLATFORM_PROFILE_CUSTOM;
290 	else
291 		*profile = val;
292 
293 	return 0;
294 }
295 
296 /**
297  * _store_and_notify - Store and notify a class from legacy sysfs interface
298  * @dev: The device
299  * @data: The profile to return
300  *
301  * Return: 0 on success, -errno on failure
302  */
303 static int _store_and_notify(struct device *dev, void *data)
304 {
305 	enum platform_profile_option *profile = data;
306 	int err;
307 
308 	err = _store_class_profile(dev, profile);
309 	if (err)
310 		return err;
311 	return _notify_class_profile(dev, NULL);
312 }
313 
314 /**
315  * platform_profile_show - Show the current profile for legacy sysfs interface
316  * @dev: The device
317  * @attr: The attribute
318  * @buf: The buffer to write to
319  *
320  * Return: The number of bytes written
321  */
322 static ssize_t platform_profile_show(struct device *dev,
323 				     struct device_attribute *attr,
324 				     char *buf)
325 {
326 	enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
327 	int err;
328 
329 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
330 		err = class_for_each_device(&platform_profile_class, NULL,
331 					    &profile, _aggregate_profiles);
332 		if (err)
333 			return err;
334 	}
335 
336 	/* no profile handler registered any more */
337 	if (profile == PLATFORM_PROFILE_LAST)
338 		return -EINVAL;
339 
340 	return sysfs_emit(buf, "%s\n", profile_names[profile]);
341 }
342 
343 /**
344  * platform_profile_store - Set the profile for legacy sysfs interface
345  * @dev: The device
346  * @attr: The attribute
347  * @buf: The buffer to read from
348  * @count: The number of bytes to read
349  *
350  * Return: The number of bytes read
351  */
352 static ssize_t platform_profile_store(struct device *dev,
353 				      struct device_attribute *attr,
354 				      const char *buf, size_t count)
355 {
356 	unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
357 	int ret;
358 	int i;
359 
360 	/* Scan for a matching profile */
361 	i = sysfs_match_string(profile_names, buf);
362 	if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
363 		return -EINVAL;
364 	set_bit(PLATFORM_PROFILE_LAST, choices);
365 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
366 		ret = class_for_each_device(&platform_profile_class, NULL,
367 					    choices, _aggregate_choices);
368 		if (ret)
369 			return ret;
370 		if (!test_bit(i, choices))
371 			return -EOPNOTSUPP;
372 
373 		ret = class_for_each_device(&platform_profile_class, NULL, &i,
374 					    _store_and_notify);
375 		if (ret)
376 			return ret;
377 	}
378 
379 	sysfs_notify(acpi_kobj, NULL, "platform_profile");
380 
381 	return count;
382 }
383 
384 static DEVICE_ATTR_RO(platform_profile_choices);
385 static DEVICE_ATTR_RW(platform_profile);
386 
387 static struct attribute *platform_profile_attrs[] = {
388 	&dev_attr_platform_profile_choices.attr,
389 	&dev_attr_platform_profile.attr,
390 	NULL
391 };
392 
393 static int profile_class_registered(struct device *dev, const void *data)
394 {
395 	return 1;
396 }
397 
398 static umode_t profile_class_is_visible(struct kobject *kobj, struct attribute *attr, int idx)
399 {
400 	if (!class_find_device(&platform_profile_class, NULL, NULL, profile_class_registered))
401 		return 0;
402 	return attr->mode;
403 }
404 
405 static const struct attribute_group platform_profile_group = {
406 	.attrs = platform_profile_attrs,
407 	.is_visible = profile_class_is_visible,
408 };
409 
410 void platform_profile_notify(struct platform_profile_handler *pprof)
411 {
412 	scoped_cond_guard(mutex_intr, return, &profile_lock) {
413 		_notify_class_profile(pprof->class_dev, NULL);
414 	}
415 	sysfs_notify(acpi_kobj, NULL, "platform_profile");
416 }
417 EXPORT_SYMBOL_GPL(platform_profile_notify);
418 
419 int platform_profile_cycle(void)
420 {
421 	enum platform_profile_option next = PLATFORM_PROFILE_LAST;
422 	enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
423 	unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
424 	int err;
425 
426 	set_bit(PLATFORM_PROFILE_LAST, choices);
427 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
428 		err = class_for_each_device(&platform_profile_class, NULL,
429 					    &profile, _aggregate_profiles);
430 		if (err)
431 			return err;
432 
433 		if (profile == PLATFORM_PROFILE_CUSTOM ||
434 		    profile == PLATFORM_PROFILE_LAST)
435 			return -EINVAL;
436 
437 		err = class_for_each_device(&platform_profile_class, NULL,
438 					    choices, _aggregate_choices);
439 		if (err)
440 			return err;
441 
442 		/* never iterate into a custom if all drivers supported it */
443 		clear_bit(PLATFORM_PROFILE_CUSTOM, choices);
444 
445 		next = find_next_bit_wrap(choices,
446 					  PLATFORM_PROFILE_LAST,
447 					  profile + 1);
448 
449 		err = class_for_each_device(&platform_profile_class, NULL, &next,
450 					    _store_and_notify);
451 
452 		if (err)
453 			return err;
454 	}
455 
456 	sysfs_notify(acpi_kobj, NULL, "platform_profile");
457 
458 	return 0;
459 }
460 EXPORT_SYMBOL_GPL(platform_profile_cycle);
461 
462 int platform_profile_register(struct platform_profile_handler *pprof)
463 {
464 	int err;
465 
466 	/* Sanity check the profile handler */
467 	if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) ||
468 	    !pprof->profile_set || !pprof->profile_get) {
469 		pr_err("platform_profile: handler is invalid\n");
470 		return -EINVAL;
471 	}
472 
473 	guard(mutex)(&profile_lock);
474 
475 	/* create class interface for individual handler */
476 	pprof->minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
477 	if (pprof->minor < 0)
478 		return pprof->minor;
479 	pprof->class_dev = device_create(&platform_profile_class, pprof->dev,
480 					 MKDEV(0, 0), pprof, "platform-profile-%d",
481 					 pprof->minor);
482 	if (IS_ERR(pprof->class_dev)) {
483 		err = PTR_ERR(pprof->class_dev);
484 		goto cleanup_ida;
485 	}
486 
487 	sysfs_notify(acpi_kobj, NULL, "platform_profile");
488 
489 	err = sysfs_update_group(acpi_kobj, &platform_profile_group);
490 	if (err)
491 		goto cleanup_cur;
492 
493 	return 0;
494 
495 cleanup_cur:
496 	device_unregister(pprof->class_dev);
497 
498 cleanup_ida:
499 	ida_free(&platform_profile_ida, pprof->minor);
500 
501 	return err;
502 }
503 EXPORT_SYMBOL_GPL(platform_profile_register);
504 
505 int platform_profile_remove(struct platform_profile_handler *pprof)
506 {
507 	int id;
508 	guard(mutex)(&profile_lock);
509 
510 	id = pprof->minor;
511 	device_unregister(pprof->class_dev);
512 	ida_free(&platform_profile_ida, id);
513 
514 	sysfs_notify(acpi_kobj, NULL, "platform_profile");
515 
516 	sysfs_update_group(acpi_kobj, &platform_profile_group);
517 
518 	return 0;
519 }
520 EXPORT_SYMBOL_GPL(platform_profile_remove);
521 
522 static int __init platform_profile_init(void)
523 {
524 	int err;
525 
526 	err = class_register(&platform_profile_class);
527 	if (err)
528 		return err;
529 
530 	err = sysfs_create_group(acpi_kobj, &platform_profile_group);
531 	if (err)
532 		class_unregister(&platform_profile_class);
533 
534 	return err;
535 }
536 
537 static void __exit platform_profile_exit(void)
538 {
539 	sysfs_remove_group(acpi_kobj, &platform_profile_group);
540 	class_unregister(&platform_profile_class);
541 }
542 module_init(platform_profile_init);
543 module_exit(platform_profile_exit);
544 
545 MODULE_AUTHOR("Mark Pearson <[email protected]>");
546 MODULE_DESCRIPTION("ACPI platform profile sysfs interface");
547 MODULE_LICENSE("GPL");
548