]> git.ipfire.org Git - thirdparty/linux.git/commitdiff
ACPI: platform_profile: Create class for ACPI platform profile
authorMario Limonciello <mario.limonciello@amd.com>
Fri, 6 Dec 2024 03:19:06 +0000 (21:19 -0600)
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>
Tue, 10 Dec 2024 17:18:01 +0000 (19:18 +0200)
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>
drivers/acpi/platform_profile.c
include/linux/platform_profile.h

index a1f0378f15e625d28e0022be5c1ac8878afe6820..11eb60b09bac45057a69e0f7ee5a133a35ba0c4a 100644 (file)
@@ -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");
index 8ec0b8da56db5a043c6bf279149080981f45b7a1..a888fd085c5130b8f4a2bc984a5523cf472264ac 100644 (file)
@@ -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);