mirror of
				https://github.com/torvalds/linux.git
				synced 2025-10-31 16:48:26 +02:00 
			
		
		
		
	ACPI: platform_profile: Create class for ACPI platform profile
When registering a platform profile handler create a class device that will allow changing a single platform profile handler. The class and sysfs group are no longer needed when the platform profile core is a module and unloaded, so remove them at that time as well. Reviewed-by: Armin Wolf <W_Armin@gmx.de> Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca> Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca> Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> Link: https://lore.kernel.org/r/20241206031918.1537-11-mario.limonciello@amd.com Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
This commit is contained in:
		
							parent
							
								
									1f3ac55c2e
								
							
						
					
					
						commit
						77be5cacb2
					
				
					 2 changed files with 79 additions and 5 deletions
				
			
		|  | @ -5,6 +5,7 @@ | |||
| #include <linux/acpi.h> | ||||
| #include <linux/bits.h> | ||||
| #include <linux/init.h> | ||||
| #include <linux/kdev_t.h> | ||||
| #include <linux/mutex.h> | ||||
| #include <linux/platform_profile.h> | ||||
| #include <linux/sysfs.h> | ||||
|  | @ -22,6 +23,12 @@ static const char * const profile_names[] = { | |||
| }; | ||||
| static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST); | ||||
| 
 | ||||
| static DEFINE_IDA(platform_profile_ida); | ||||
| 
 | ||||
| static const struct class platform_profile_class = { | ||||
| 	.name = "platform-profile", | ||||
| }; | ||||
| 
 | ||||
| static ssize_t platform_profile_choices_show(struct device *dev, | ||||
| 					struct device_attribute *attr, | ||||
| 					char *buf) | ||||
|  | @ -101,8 +108,21 @@ static struct attribute *platform_profile_attrs[] = { | |||
| 	NULL | ||||
| }; | ||||
| 
 | ||||
| static int profile_class_registered(struct device *dev, const void *data) | ||||
| { | ||||
| 	return 1; | ||||
| } | ||||
| 
 | ||||
| static umode_t profile_class_is_visible(struct kobject *kobj, struct attribute *attr, int idx) | ||||
| { | ||||
| 	if (!class_find_device(&platform_profile_class, NULL, NULL, profile_class_registered)) | ||||
| 		return 0; | ||||
| 	return attr->mode; | ||||
| } | ||||
| 
 | ||||
| static const struct attribute_group platform_profile_group = { | ||||
| 	.attrs = platform_profile_attrs | ||||
| 	.attrs = platform_profile_attrs, | ||||
| 	.is_visible = profile_class_is_visible, | ||||
| }; | ||||
| 
 | ||||
| void platform_profile_notify(struct platform_profile_handler *pprof) | ||||
|  | @ -160,25 +180,77 @@ int platform_profile_register(struct platform_profile_handler *pprof) | |||
| 	if (cur_profile) | ||||
| 		return -EEXIST; | ||||
| 
 | ||||
| 	err = sysfs_create_group(acpi_kobj, &platform_profile_group); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 	/* create class interface for individual handler */ | ||||
| 	pprof->minor = ida_alloc(&platform_profile_ida, GFP_KERNEL); | ||||
| 	if (pprof->minor < 0) | ||||
| 		return pprof->minor; | ||||
| 	pprof->class_dev = device_create(&platform_profile_class, pprof->dev, | ||||
| 					 MKDEV(0, 0), pprof, "platform-profile-%d", | ||||
| 					 pprof->minor); | ||||
| 	if (IS_ERR(pprof->class_dev)) { | ||||
| 		err = PTR_ERR(pprof->class_dev); | ||||
| 		goto cleanup_ida; | ||||
| 	} | ||||
| 
 | ||||
| 	cur_profile = pprof; | ||||
| 
 | ||||
| 	err = sysfs_update_group(acpi_kobj, &platform_profile_group); | ||||
| 	if (err) | ||||
| 		goto cleanup_cur; | ||||
| 
 | ||||
| 	return 0; | ||||
| 
 | ||||
| cleanup_cur: | ||||
| 	cur_profile = NULL; | ||||
| 	device_unregister(pprof->class_dev); | ||||
| 
 | ||||
| cleanup_ida: | ||||
| 	ida_free(&platform_profile_ida, pprof->minor); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(platform_profile_register); | ||||
| 
 | ||||
| int platform_profile_remove(struct platform_profile_handler *pprof) | ||||
| { | ||||
| 	int id; | ||||
| 	guard(mutex)(&profile_lock); | ||||
| 
 | ||||
| 	sysfs_remove_group(acpi_kobj, &platform_profile_group); | ||||
| 	cur_profile = NULL; | ||||
| 
 | ||||
| 	id = pprof->minor; | ||||
| 	device_unregister(pprof->class_dev); | ||||
| 	ida_free(&platform_profile_ida, id); | ||||
| 
 | ||||
| 	sysfs_update_group(acpi_kobj, &platform_profile_group); | ||||
| 
 | ||||
| 	return 0; | ||||
| } | ||||
| EXPORT_SYMBOL_GPL(platform_profile_remove); | ||||
| 
 | ||||
| static int __init platform_profile_init(void) | ||||
| { | ||||
| 	int err; | ||||
| 
 | ||||
| 	err = class_register(&platform_profile_class); | ||||
| 	if (err) | ||||
| 		return err; | ||||
| 
 | ||||
| 	err = sysfs_create_group(acpi_kobj, &platform_profile_group); | ||||
| 	if (err) | ||||
| 		class_unregister(&platform_profile_class); | ||||
| 
 | ||||
| 	return err; | ||||
| } | ||||
| 
 | ||||
| static void __exit platform_profile_exit(void) | ||||
| { | ||||
| 	sysfs_remove_group(acpi_kobj, &platform_profile_group); | ||||
| 	class_unregister(&platform_profile_class); | ||||
| } | ||||
| module_init(platform_profile_init); | ||||
| module_exit(platform_profile_exit); | ||||
| 
 | ||||
| MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>"); | ||||
| MODULE_DESCRIPTION("ACPI platform profile sysfs interface"); | ||||
| MODULE_LICENSE("GPL"); | ||||
|  |  | |||
|  | @ -29,6 +29,8 @@ enum platform_profile_option { | |||
| struct platform_profile_handler { | ||||
| 	const char *name; | ||||
| 	struct device *dev; | ||||
| 	struct device *class_dev; | ||||
| 	int minor; | ||||
| 	unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)]; | ||||
| 	int (*profile_get)(struct platform_profile_handler *pprof, | ||||
| 				enum platform_profile_option *profile); | ||||
|  |  | |||
		Loading…
	
		Reference in a new issue
	
	 Mario Limonciello
						Mario Limonciello