[PATCH v3 20/22] ACPI: platform_profile: Register class device for platform profile handlers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



The "platform_profile" class device has the exact same semantics as the
platform profile files in /sys/firmware/acpi/ but it reflects values only
present for a single platform profile handler.

The expectation is that legacy userspace can change the profile for all
handlers in /sys/firmware/acpi/platform_profile and can change it for
individual handlers by /sys/class/platform_profile/*.

Signed-off-by: Mario Limonciello <mario.limonciello@xxxxxxx>
---
 drivers/acpi/platform_profile.c  | 93 ++++++++++++++++++++++++++++----
 include/linux/platform_profile.h |  2 +
 2 files changed, 85 insertions(+), 10 deletions(-)

diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
index 9b681884ae324..1cc8182930dde 100644
--- a/drivers/acpi/platform_profile.c
+++ b/drivers/acpi/platform_profile.c
@@ -24,13 +24,24 @@ static const char * const profile_names[] = {
 };
 static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
 
+static DEFINE_IDR(platform_profile_minor_idr);
+
+static const struct class platform_profile_class = {
+	.name = "platform-profile",
+};
+
 static bool platform_profile_is_registered(void)
 {
 	lockdep_assert_held(&profile_lock);
 	return !list_empty(&platform_profile_handler_list);
 }
 
-static unsigned long platform_profile_get_choices(void)
+static bool platform_profile_is_class_device(struct device *dev)
+{
+	return dev && dev->class == &platform_profile_class;
+}
+
+static unsigned long platform_profile_get_choices(struct device *dev)
 {
 	struct platform_profile_handler *handler;
 	unsigned long aggregate = 0;
@@ -40,6 +51,9 @@ static unsigned long platform_profile_get_choices(void)
 	list_for_each_entry(handler, &platform_profile_handler_list, list) {
 		unsigned long individual = 0;
 
+		/* if called from a class attribute then only match that one */
+		if (platform_profile_is_class_device(dev) && handler->dev != dev->parent)
+			continue;
 		for_each_set_bit(i, handler->choices, PLATFORM_PROFILE_LAST)
 			individual |= BIT(i);
 		if (!aggregate)
@@ -51,7 +65,7 @@ static unsigned long platform_profile_get_choices(void)
 	return aggregate;
 }
 
-static int platform_profile_get_active(enum platform_profile_option *profile)
+static int platform_profile_get_active(struct device *dev, enum platform_profile_option *profile)
 {
 	struct platform_profile_handler *handler;
 	enum platform_profile_option active = PLATFORM_PROFILE_LAST;
@@ -60,6 +74,8 @@ static int platform_profile_get_active(enum platform_profile_option *profile)
 
 	lockdep_assert_held(&profile_lock);
 	list_for_each_entry(handler, &platform_profile_handler_list, list) {
+		if (platform_profile_is_class_device(dev) && handler->dev != dev->parent)
+			continue;
 		err = handler->profile_get(handler, &val);
 		if (err) {
 			pr_err("Failed to get profile for handler %s\n", handler->name);
@@ -69,6 +85,10 @@ static int platform_profile_get_active(enum platform_profile_option *profile)
 		if (WARN_ON(val >= PLATFORM_PROFILE_LAST))
 			return -EINVAL;
 
+		/*
+		 * If the profiles are different for class devices then this must
+		 * show "custom" to legacy sysfs interface
+		 */
 		if (active != val && active != PLATFORM_PROFILE_LAST) {
 			*profile = PLATFORM_PROFILE_CUSTOM;
 			return 0;
@@ -90,7 +110,7 @@ static ssize_t platform_profile_choices_show(struct device *dev,
 	int i;
 
 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock)
-		choices = platform_profile_get_choices();
+		choices = platform_profile_get_choices(dev);
 
 	for_each_set_bit(i, &choices, PLATFORM_PROFILE_LAST) {
 		if (len == 0)
@@ -113,7 +133,7 @@ static ssize_t platform_profile_show(struct device *dev,
 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
 		if (!platform_profile_is_registered())
 			return -ENODEV;
-		err = platform_profile_get_active(&profile);
+		err = platform_profile_get_active(dev, &profile);
 		if (err)
 			return err;
 	}
@@ -138,12 +158,22 @@ static ssize_t platform_profile_store(struct device *dev,
 		if (!platform_profile_is_registered())
 			return -ENODEV;
 
-		/* Check that all handlers support this profile choice */
-		choices = platform_profile_get_choices();
+		/* don't allow setting custom to legacy sysfs interface */
+		if (!platform_profile_is_class_device(dev) &&
+		     i == PLATFORM_PROFILE_CUSTOM) {
+			pr_warn("Custom profile not supported for legacy sysfs interface\n");
+			return -EINVAL;
+		}
+
+		/* Check that applicable handlers support this profile choice */
+		choices = platform_profile_get_choices(dev);
 		if (!test_bit(i, &choices))
 			return -EOPNOTSUPP;
 
 		list_for_each_entry(handler, &platform_profile_handler_list, list) {
+			if (platform_profile_is_class_device(dev) &&
+			    handler->dev != dev->parent)
+				continue;
 			err = handler->profile_set(handler, i);
 			if (err) {
 				pr_err("Failed to set profile for handler %s\n", handler->name);
@@ -152,6 +182,9 @@ static ssize_t platform_profile_store(struct device *dev,
 		}
 		if (err) {
 			list_for_each_entry_continue_reverse(handler, &platform_profile_handler_list, list) {
+				if (platform_profile_is_class_device(dev) &&
+				    handler->dev != dev->parent)
+					continue;
 				if (handler->profile_set(handler, PLATFORM_PROFILE_BALANCED))
 					pr_err("Failed to revert profile for handler %s\n",
 					       handler->name);
@@ -194,11 +227,11 @@ int platform_profile_cycle(void)
 	int err;
 
 	scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
-		err = platform_profile_get_active(&profile);
+		err = platform_profile_get_active(NULL, &profile);
 		if (err)
 			return err;
 
-		choices = platform_profile_get_choices();
+		choices = platform_profile_get_choices(NULL);
 
 		next = find_next_bit_wrap(&choices,
 					  PLATFORM_PROFILE_LAST,
@@ -228,6 +261,7 @@ EXPORT_SYMBOL_GPL(platform_profile_cycle);
 
 int platform_profile_register(struct platform_profile_handler *pprof)
 {
+	bool registered;
 	int err;
 
 	/* Sanity check the profile handler */
@@ -250,14 +284,49 @@ int platform_profile_register(struct platform_profile_handler *pprof)
 	if (cur_profile)
 		return -EEXIST;
 
-	err = sysfs_create_group(acpi_kobj, &platform_profile_group);
+	registered = platform_profile_is_registered();
+	if (!registered) {
+		/* class for individual handlers */
+		err = class_register(&platform_profile_class);
+		if (err)
+			return err;
+		/* legacy sysfs files */
+		err = sysfs_create_group(acpi_kobj, &platform_profile_group);
+		if (err)
+			goto cleanup_class;
+
+	}
+
+	/* create class interface for individual handler */
+	pprof->minor = idr_alloc(&platform_profile_minor_idr, pprof, 0, 0, GFP_KERNEL);
+	pprof->class_dev = device_create(&platform_profile_class, pprof->dev,
+					 MKDEV(0, pprof->minor), NULL, "platform-profile-%s",
+					 pprof->name);
+	if (IS_ERR(pprof->class_dev)) {
+		err = PTR_ERR(pprof->class_dev);
+		goto cleanup_legacy;
+	}
+	err = sysfs_create_group(&pprof->class_dev->kobj, &platform_profile_group);
 	if (err)
-		return err;
+		goto cleanup_device;
+
 	list_add_tail(&pprof->list, &platform_profile_handler_list);
 	sysfs_notify(acpi_kobj, NULL, "platform_profile");
 
 	cur_profile = pprof;
 	return 0;
+
+cleanup_device:
+	device_destroy(&platform_profile_class, MKDEV(0, pprof->minor));
+
+cleanup_legacy:
+	if (!registered)
+		sysfs_remove_group(acpi_kobj, &platform_profile_group);
+cleanup_class:
+	if (!registered)
+		class_unregister(&platform_profile_class);
+
+	return err;
 }
 EXPORT_SYMBOL_GPL(platform_profile_register);
 
@@ -270,6 +339,10 @@ int platform_profile_remove(struct platform_profile_handler *pprof)
 	cur_profile = NULL;
 
 	sysfs_notify(acpi_kobj, NULL, "platform_profile");
+
+	sysfs_remove_group(&pprof->class_dev->kobj, &platform_profile_group);
+	device_destroy(&platform_profile_class, MKDEV(0, pprof->minor));
+
 	if (!platform_profile_is_registered())
 		sysfs_remove_group(acpi_kobj, &platform_profile_group);
 
diff --git a/include/linux/platform_profile.h b/include/linux/platform_profile.h
index da009c8a402c9..764c4812ef759 100644
--- a/include/linux/platform_profile.h
+++ b/include/linux/platform_profile.h
@@ -30,6 +30,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)];
 	struct list_head list;
 	int (*profile_get)(struct platform_profile_handler *pprof,
-- 
2.43.0





[Index of Archives]     [Linux Kernel Development]     [Linux USB Devel]     [Video for Linux]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]

  Powered by Linux