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…
Add table
Reference in a new issue