summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIlpo Järvinen <ilpo.jarvinen@linux.intel.com>2024-12-12 16:07:26 +0200
committerIlpo Järvinen <ilpo.jarvinen@linux.intel.com>2024-12-12 16:07:26 +0200
commit18131e9f1682bd6082ec6c194f8ed3243f0fc230 (patch)
tree8f6d7bac52881d71e67a7c7e831d90ac037fa615
parent3b6f9c6505a34450a4dd5d9bbbe5cfec4718adb2 (diff)
parent0056b0852c326cf21201661f68ab41002ae9a845 (diff)
downloadlinux-next-18131e9f1682bd6082ec6c194f8ed3243f0fc230.tar.gz
linux-next-18131e9f1682bd6082ec6c194f8ed3243f0fc230.zip
Merge branch 'platform-drivers-x86-platform-profile' into for-next
-rw-r--r--Documentation/ABI/testing/sysfs-platform_profile5
-rw-r--r--Documentation/userspace-api/sysfs-platform_profile.rst38
-rw-r--r--drivers/acpi/platform_profile.c534
-rw-r--r--drivers/platform/surface/surface_platform_profile.c8
-rw-r--r--drivers/platform/x86/acer-wmi.c12
-rw-r--r--drivers/platform/x86/amd/pmf/Makefile2
-rw-r--r--drivers/platform/x86/amd/pmf/core.c1
-rw-r--r--drivers/platform/x86/amd/pmf/pmf-quirks.c66
-rw-r--r--drivers/platform/x86/amd/pmf/pmf.h3
-rw-r--r--drivers/platform/x86/amd/pmf/sps.c4
-rw-r--r--drivers/platform/x86/asus-wmi.c8
-rw-r--r--drivers/platform/x86/dell/alienware-wmi.c8
-rw-r--r--drivers/platform/x86/dell/dell-pc.c38
-rw-r--r--drivers/platform/x86/hp/hp-wmi.c8
-rw-r--r--drivers/platform/x86/ideapad-laptop.c6
-rw-r--r--drivers/platform/x86/inspur_platform_profile.c7
-rw-r--r--drivers/platform/x86/thinkpad_acpi.c16
-rw-r--r--include/linux/platform_profile.h9
18 files changed, 559 insertions, 214 deletions
diff --git a/Documentation/ABI/testing/sysfs-platform_profile b/Documentation/ABI/testing/sysfs-platform_profile
index baf1d125f9f8..125324ab53a9 100644
--- a/Documentation/ABI/testing/sysfs-platform_profile
+++ b/Documentation/ABI/testing/sysfs-platform_profile
@@ -33,3 +33,8 @@ Description: Reading this file gives the current selected profile for this
source such as e.g. a hotkey triggered profile change handled
either directly by the embedded-controller or fully handled
inside the kernel.
+
+ This file may also emit the string 'custom' to indicate
+ that multiple platform profiles drivers are in use but
+ have different values. This string can not be written to
+ this interface and is solely for informational purposes.
diff --git a/Documentation/userspace-api/sysfs-platform_profile.rst b/Documentation/userspace-api/sysfs-platform_profile.rst
index 4fccde2e4563..7f013356118a 100644
--- a/Documentation/userspace-api/sysfs-platform_profile.rst
+++ b/Documentation/userspace-api/sysfs-platform_profile.rst
@@ -40,3 +40,41 @@ added. Drivers which wish to introduce new profile names must:
1. Explain why the existing profile names cannot be used.
2. Add the new profile name, along with a clear description of the
expected behaviour, to the sysfs-platform_profile ABI documentation.
+
+"Custom" profile support
+========================
+The platform_profile class also supports profiles advertising a "custom"
+profile. This is intended to be set by drivers when the setttings in the
+driver have been modified in a way that a standard profile doesn't represent
+the current state.
+
+Multiple driver support
+=======================
+When multiple drivers on a system advertise a platform profile handler, the
+platform profile handler core will only advertise the profiles that are
+common between all drivers to the ``/sys/firmware/acpi`` interfaces.
+
+This is to ensure there is no ambiguity on what the profile names mean when
+all handlers don't support a profile.
+
+Individual drivers will register a 'platform_profile' class device that has
+similar semantics as the ``/sys/firmware/acpi/platform_profile`` interface.
+
+To discover which driver is associated with a platform profile handler the
+user can read the ``name`` attribute of the class device.
+
+To discover available profiles from the class interface the user can read the
+``choices`` attribute.
+
+If a user wants to select a profile for a specific driver, they can do so
+by writing to the ``profile`` attribute of the driver's class device.
+
+This will allow users to set different profiles for different drivers on the
+same system. If the selected profile by individual drivers differs the
+platform profile handler core will display the profile 'custom' to indicate
+that the profiles are not the same.
+
+While the ``platform_profile`` attribute has the value ``custom``, writing a
+common profile from ``platform_profile_choices`` to the platform_profile
+attribute of the platform profile handler core will set the profile for all
+drivers.
diff --git a/drivers/acpi/platform_profile.c b/drivers/acpi/platform_profile.c
index d2f7fd7743a1..75a1415190ac 100644
--- a/drivers/acpi/platform_profile.c
+++ b/drivers/acpi/platform_profile.c
@@ -5,11 +5,11 @@
#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>
-static struct platform_profile_handler *cur_profile;
static DEFINE_MUTEX(profile_lock);
static const char * const profile_names[] = {
@@ -19,99 +19,365 @@ static const char * const profile_names[] = {
[PLATFORM_PROFILE_BALANCED] = "balanced",
[PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance",
[PLATFORM_PROFILE_PERFORMANCE] = "performance",
+ [PLATFORM_PROFILE_CUSTOM] = "custom",
};
static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);
-static ssize_t platform_profile_choices_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
-{
- int len = 0;
- int err, i;
-
- err = mutex_lock_interruptible(&profile_lock);
- if (err)
- return err;
+static DEFINE_IDA(platform_profile_ida);
- if (!cur_profile) {
- mutex_unlock(&profile_lock);
- return -ENODEV;
- }
+/**
+ * _commmon_choices_show - Show the available profile choices
+ * @choices: The available profile choices
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t _commmon_choices_show(unsigned long *choices, char *buf)
+{
+ int i, len = 0;
- for_each_set_bit(i, cur_profile->choices, PLATFORM_PROFILE_LAST) {
+ for_each_set_bit(i, choices, PLATFORM_PROFILE_LAST) {
if (len == 0)
len += sysfs_emit_at(buf, len, "%s", profile_names[i]);
else
len += sysfs_emit_at(buf, len, " %s", profile_names[i]);
}
len += sysfs_emit_at(buf, len, "\n");
- mutex_unlock(&profile_lock);
+
return len;
}
-static ssize_t platform_profile_show(struct device *dev,
- struct device_attribute *attr,
- char *buf)
+/**
+ * _store_class_profile - Set the profile for a class device
+ * @dev: The class device
+ * @data: The profile to set
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _store_class_profile(struct device *dev, void *data)
+{
+ struct platform_profile_handler *handler;
+ int *bit = (int *)data;
+
+ lockdep_assert_held(&profile_lock);
+ handler = dev_get_drvdata(dev);
+ if (!test_bit(*bit, handler->choices))
+ return -EOPNOTSUPP;
+
+ return handler->profile_set(handler, *bit);
+}
+
+/**
+ * _notify_class_profile - Notify the class device of a profile change
+ * @dev: The class device
+ * @data: Unused
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _notify_class_profile(struct device *dev, void *data)
{
- enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
+ struct platform_profile_handler *handler = dev_get_drvdata(dev);
+
+ lockdep_assert_held(&profile_lock);
+ sysfs_notify(&handler->class_dev->kobj, NULL, "profile");
+ kobject_uevent(&handler->class_dev->kobj, KOBJ_CHANGE);
+
+ return 0;
+}
+
+/**
+ * get_class_profile - Show the current profile for a class device
+ * @dev: The class device
+ * @profile: The profile to return
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int get_class_profile(struct device *dev,
+ enum platform_profile_option *profile)
+{
+ struct platform_profile_handler *handler;
+ enum platform_profile_option val;
int err;
- err = mutex_lock_interruptible(&profile_lock);
- if (err)
+ lockdep_assert_held(&profile_lock);
+ handler = dev_get_drvdata(dev);
+ err = handler->profile_get(handler, &val);
+ if (err) {
+ pr_err("Failed to get profile for handler %s\n", handler->name);
return err;
+ }
+
+ if (WARN_ON(val >= PLATFORM_PROFILE_LAST))
+ return -EINVAL;
+ *profile = val;
+
+ return 0;
+}
+
+/**
+ * name_show - Show the name of the profile handler
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t name_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct platform_profile_handler *handler = dev_get_drvdata(dev);
+
+ return sysfs_emit(buf, "%s\n", handler->name);
+}
+static DEVICE_ATTR_RO(name);
+
+/**
+ * choices_show - Show the available profile choices
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t choices_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ struct platform_profile_handler *handler = dev_get_drvdata(dev);
+
+ return _commmon_choices_show(handler->choices, buf);
+}
+static DEVICE_ATTR_RO(choices);
+
+/**
+ * profile_show - Show the current profile for a class device
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t profile_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
+ int err;
+
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ err = get_class_profile(dev, &profile);
+ if (err)
+ return err;
+ }
+
+ return sysfs_emit(buf, "%s\n", profile_names[profile]);
+}
+
+/**
+ * profile_store - Set the profile for a class device
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to read from
+ * @count: The number of bytes to read
+ *
+ * Return: The number of bytes read
+ */
+static ssize_t profile_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ int index, ret;
+
+ index = sysfs_match_string(profile_names, buf);
+ if (index < 0)
+ return -EINVAL;
- if (!cur_profile) {
- mutex_unlock(&profile_lock);
- return -ENODEV;
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ ret = _store_class_profile(dev, &index);
+ if (ret)
+ return ret;
}
- err = cur_profile->profile_get(cur_profile, &profile);
- mutex_unlock(&profile_lock);
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
+
+ return count;
+}
+static DEVICE_ATTR_RW(profile);
+
+static struct attribute *profile_attrs[] = {
+ &dev_attr_name.attr,
+ &dev_attr_choices.attr,
+ &dev_attr_profile.attr,
+ NULL
+};
+ATTRIBUTE_GROUPS(profile);
+
+static const struct class platform_profile_class = {
+ .name = "platform-profile",
+ .dev_groups = profile_groups,
+};
+
+/**
+ * _aggregate_choices - Aggregate the available profile choices
+ * @dev: The device
+ * @data: The available profile choices
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _aggregate_choices(struct device *dev, void *data)
+{
+ struct platform_profile_handler *handler;
+ unsigned long *aggregate = data;
+
+ lockdep_assert_held(&profile_lock);
+ handler = dev_get_drvdata(dev);
+ if (test_bit(PLATFORM_PROFILE_LAST, aggregate))
+ bitmap_copy(aggregate, handler->choices, PLATFORM_PROFILE_LAST);
+ else
+ bitmap_and(aggregate, handler->choices, aggregate, PLATFORM_PROFILE_LAST);
+
+ return 0;
+}
+
+/**
+ * platform_profile_choices_show - Show the available profile choices for legacy sysfs interface
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t platform_profile_choices_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
+ int err;
+
+ set_bit(PLATFORM_PROFILE_LAST, aggregate);
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ err = class_for_each_device(&platform_profile_class, NULL,
+ aggregate, _aggregate_choices);
+ if (err)
+ return err;
+ }
+
+ /* no profile handler registered any more */
+ if (bitmap_empty(aggregate, PLATFORM_PROFILE_LAST))
+ return -EINVAL;
+
+ return _commmon_choices_show(aggregate, buf);
+}
+
+/**
+ * _aggregate_profiles - Aggregate the profiles for legacy sysfs interface
+ * @dev: The device
+ * @data: The profile to return
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _aggregate_profiles(struct device *dev, void *data)
+{
+ enum platform_profile_option *profile = data;
+ enum platform_profile_option val;
+ int err;
+
+ err = get_class_profile(dev, &val);
if (err)
return err;
- /* Check that profile is valid index */
- if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
- return -EIO;
+ if (*profile != PLATFORM_PROFILE_LAST && *profile != val)
+ *profile = PLATFORM_PROFILE_CUSTOM;
+ else
+ *profile = val;
- return sysfs_emit(buf, "%s\n", profile_names[profile]);
+ return 0;
}
-static ssize_t platform_profile_store(struct device *dev,
- struct device_attribute *attr,
- const char *buf, size_t count)
+/**
+ * _store_and_notify - Store and notify a class from legacy sysfs interface
+ * @dev: The device
+ * @data: The profile to return
+ *
+ * Return: 0 on success, -errno on failure
+ */
+static int _store_and_notify(struct device *dev, void *data)
{
- int err, i;
+ enum platform_profile_option *profile = data;
+ int err;
- err = mutex_lock_interruptible(&profile_lock);
+ err = _store_class_profile(dev, profile);
if (err)
return err;
+ return _notify_class_profile(dev, NULL);
+}
+
+/**
+ * platform_profile_show - Show the current profile for legacy sysfs interface
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to write to
+ *
+ * Return: The number of bytes written
+ */
+static ssize_t platform_profile_show(struct device *dev,
+ struct device_attribute *attr,
+ char *buf)
+{
+ enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
+ int err;
- if (!cur_profile) {
- mutex_unlock(&profile_lock);
- return -ENODEV;
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ err = class_for_each_device(&platform_profile_class, NULL,
+ &profile, _aggregate_profiles);
+ if (err)
+ return err;
}
+ /* no profile handler registered any more */
+ if (profile == PLATFORM_PROFILE_LAST)
+ return -EINVAL;
+
+ return sysfs_emit(buf, "%s\n", profile_names[profile]);
+}
+
+/**
+ * platform_profile_store - Set the profile for legacy sysfs interface
+ * @dev: The device
+ * @attr: The attribute
+ * @buf: The buffer to read from
+ * @count: The number of bytes to read
+ *
+ * Return: The number of bytes read
+ */
+static ssize_t platform_profile_store(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
+ int ret;
+ int i;
+
/* Scan for a matching profile */
i = sysfs_match_string(profile_names, buf);
- if (i < 0) {
- mutex_unlock(&profile_lock);
+ if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
return -EINVAL;
+ set_bit(PLATFORM_PROFILE_LAST, choices);
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ ret = class_for_each_device(&platform_profile_class, NULL,
+ choices, _aggregate_choices);
+ if (ret)
+ return ret;
+ if (!test_bit(i, choices))
+ return -EOPNOTSUPP;
+
+ ret = class_for_each_device(&platform_profile_class, NULL, &i,
+ _store_and_notify);
+ if (ret)
+ return ret;
}
- /* Check that platform supports this profile choice */
- if (!test_bit(i, cur_profile->choices)) {
- mutex_unlock(&profile_lock);
- return -EOPNOTSUPP;
- }
-
- err = cur_profile->profile_set(cur_profile, i);
- if (!err)
- sysfs_notify(acpi_kobj, NULL, "platform_profile");
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
- mutex_unlock(&profile_lock);
- if (err)
- return err;
return count;
}
@@ -124,54 +390,72 @@ 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(void)
+void platform_profile_notify(struct platform_profile_handler *pprof)
{
- if (!cur_profile)
- return;
+ scoped_cond_guard(mutex_intr, return, &profile_lock) {
+ _notify_class_profile(pprof->class_dev, NULL);
+ }
sysfs_notify(acpi_kobj, NULL, "platform_profile");
}
EXPORT_SYMBOL_GPL(platform_profile_notify);
int platform_profile_cycle(void)
{
- enum platform_profile_option profile;
- enum platform_profile_option next;
+ enum platform_profile_option next = PLATFORM_PROFILE_LAST;
+ enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
+ unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
int err;
- err = mutex_lock_interruptible(&profile_lock);
- if (err)
- return err;
+ set_bit(PLATFORM_PROFILE_LAST, choices);
+ scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
+ err = class_for_each_device(&platform_profile_class, NULL,
+ &profile, _aggregate_profiles);
+ if (err)
+ return err;
- if (!cur_profile) {
- mutex_unlock(&profile_lock);
- return -ENODEV;
- }
+ if (profile == PLATFORM_PROFILE_CUSTOM ||
+ profile == PLATFORM_PROFILE_LAST)
+ return -EINVAL;
- err = cur_profile->profile_get(cur_profile, &profile);
- if (err) {
- mutex_unlock(&profile_lock);
- return err;
- }
+ err = class_for_each_device(&platform_profile_class, NULL,
+ choices, _aggregate_choices);
+ if (err)
+ return err;
- next = find_next_bit_wrap(cur_profile->choices, PLATFORM_PROFILE_LAST,
- profile + 1);
+ /* never iterate into a custom if all drivers supported it */
+ clear_bit(PLATFORM_PROFILE_CUSTOM, choices);
- if (WARN_ON(next == PLATFORM_PROFILE_LAST)) {
- mutex_unlock(&profile_lock);
- return -EINVAL;
- }
+ next = find_next_bit_wrap(choices,
+ PLATFORM_PROFILE_LAST,
+ profile + 1);
- err = cur_profile->profile_set(cur_profile, next);
- mutex_unlock(&profile_lock);
+ err = class_for_each_device(&platform_profile_class, NULL, &next,
+ _store_and_notify);
- if (!err)
- sysfs_notify(acpi_kobj, NULL, "platform_profile");
+ if (err)
+ return err;
+ }
- return err;
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
+
+ return 0;
}
EXPORT_SYMBOL_GPL(platform_profile_cycle);
@@ -179,43 +463,85 @@ int platform_profile_register(struct platform_profile_handler *pprof)
{
int err;
- mutex_lock(&profile_lock);
- /* We can only have one active profile */
- if (cur_profile) {
- mutex_unlock(&profile_lock);
- return -EEXIST;
- }
-
- /* Sanity check the profile handler field are set */
+ /* Sanity check the profile handler */
if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) ||
- !pprof->profile_set || !pprof->profile_get) {
- mutex_unlock(&profile_lock);
+ !pprof->profile_set || !pprof->profile_get) {
+ pr_err("platform_profile: handler is invalid\n");
return -EINVAL;
}
- err = sysfs_create_group(acpi_kobj, &platform_profile_group);
- if (err) {
- mutex_unlock(&profile_lock);
- return err;
+ guard(mutex)(&profile_lock);
+
+ /* 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;
- mutex_unlock(&profile_lock);
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
+
+ err = sysfs_update_group(acpi_kobj, &platform_profile_group);
+ if (err)
+ goto cleanup_cur;
+
return 0;
+
+cleanup_cur:
+ 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(void)
+int platform_profile_remove(struct platform_profile_handler *pprof)
{
- sysfs_remove_group(acpi_kobj, &platform_profile_group);
+ int id;
+ guard(mutex)(&profile_lock);
+
+ id = pprof->minor;
+ device_unregister(pprof->class_dev);
+ ida_free(&platform_profile_ida, id);
+
+ sysfs_notify(acpi_kobj, NULL, "platform_profile");
+
+ sysfs_update_group(acpi_kobj, &platform_profile_group);
- mutex_lock(&profile_lock);
- cur_profile = NULL;
- mutex_unlock(&profile_lock);
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");
diff --git a/drivers/platform/surface/surface_platform_profile.c b/drivers/platform/surface/surface_platform_profile.c
index 08db878f1d7d..6c87e982bfc8 100644
--- a/drivers/platform/surface/surface_platform_profile.c
+++ b/drivers/platform/surface/surface_platform_profile.c
@@ -210,7 +210,10 @@ static int surface_platform_profile_probe(struct ssam_device *sdev)
return -ENOMEM;
tpd->sdev = sdev;
+ ssam_device_set_drvdata(sdev, tpd);
+ tpd->handler.name = "Surface Platform Profile";
+ tpd->handler.dev = &sdev->dev;
tpd->handler.profile_get = ssam_platform_profile_get;
tpd->handler.profile_set = ssam_platform_profile_set;
@@ -226,7 +229,10 @@ static int surface_platform_profile_probe(struct ssam_device *sdev)
static void surface_platform_profile_remove(struct ssam_device *sdev)
{
- platform_profile_remove();
+ struct ssam_platform_profile_device *tpd;
+
+ tpd = ssam_device_get_drvdata(sdev);
+ platform_profile_remove(&tpd->handler);
}
static const struct ssam_device_id ssam_platform_profile_match[] = {
diff --git a/drivers/platform/x86/acer-wmi.c b/drivers/platform/x86/acer-wmi.c
index aad8eb0ddae5..b3043d78a7b3 100644
--- a/drivers/platform/x86/acer-wmi.c
+++ b/drivers/platform/x86/acer-wmi.c
@@ -1900,11 +1900,13 @@ acer_predator_v4_platform_profile_set(struct platform_profile_handler *pprof,
return 0;
}
-static int acer_platform_profile_setup(void)
+static int acer_platform_profile_setup(struct platform_device *device)
{
if (quirks->predator_v4) {
int err;
+ platform_profile_handler.name = "acer-wmi";
+ platform_profile_handler.dev = &device->dev;
platform_profile_handler.profile_get =
acer_predator_v4_platform_profile_get;
platform_profile_handler.profile_set =
@@ -2010,7 +2012,7 @@ static int acer_thermal_profile_change(void)
if (tp != ACER_PREDATOR_V4_THERMAL_PROFILE_TURBO_WMI)
last_non_turbo_profile = tp;
- platform_profile_notify();
+ platform_profile_notify(&platform_profile_handler);
}
return 0;
@@ -2554,7 +2556,7 @@ static int acer_platform_probe(struct platform_device *device)
goto error_rfkill;
if (has_cap(ACER_CAP_PLATFORM_PROFILE)) {
- err = acer_platform_profile_setup();
+ err = acer_platform_profile_setup(device);
if (err)
goto error_platform_profile;
}
@@ -2569,7 +2571,7 @@ static int acer_platform_probe(struct platform_device *device)
error_hwmon:
if (platform_profile_support)
- platform_profile_remove();
+ platform_profile_remove(&platform_profile_handler);
error_platform_profile:
acer_rfkill_exit();
error_rfkill:
@@ -2592,7 +2594,7 @@ static void acer_platform_remove(struct platform_device *device)
acer_rfkill_exit();
if (platform_profile_support)
- platform_profile_remove();
+ platform_profile_remove(&platform_profile_handler);
}
#ifdef CONFIG_PM_SLEEP
diff --git a/drivers/platform/x86/amd/pmf/Makefile b/drivers/platform/x86/amd/pmf/Makefile
index 7d6079b02589..6b26e48ce8ad 100644
--- a/drivers/platform/x86/amd/pmf/Makefile
+++ b/drivers/platform/x86/amd/pmf/Makefile
@@ -7,4 +7,4 @@
obj-$(CONFIG_AMD_PMF) += amd-pmf.o
amd-pmf-objs := core.o acpi.o sps.o \
auto-mode.o cnqf.o \
- tee-if.o spc.o pmf-quirks.o
+ tee-if.o spc.o
diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c
index 06a97c533cb8..01eb9ee1eccd 100644
--- a/drivers/platform/x86/amd/pmf/core.c
+++ b/drivers/platform/x86/amd/pmf/core.c
@@ -456,7 +456,6 @@ static int amd_pmf_probe(struct platform_device *pdev)
mutex_init(&dev->lock);
mutex_init(&dev->update_mutex);
- amd_pmf_quirks_init(dev);
apmf_acpi_init(dev);
platform_set_drvdata(pdev, dev);
amd_pmf_dbgfs_register(dev);
diff --git a/drivers/platform/x86/amd/pmf/pmf-quirks.c b/drivers/platform/x86/amd/pmf/pmf-quirks.c
deleted file mode 100644
index 7cde5733b9ca..000000000000
--- a/drivers/platform/x86/amd/pmf/pmf-quirks.c
+++ /dev/null
@@ -1,66 +0,0 @@
-// SPDX-License-Identifier: GPL-2.0-or-later
-/*
- * AMD Platform Management Framework Driver Quirks
- *
- * Copyright (c) 2024, Advanced Micro Devices, Inc.
- * All Rights Reserved.
- *
- * Author: Mario Limonciello <mario.limonciello@amd.com>
- */
-
-#include <linux/dmi.h>
-
-#include "pmf.h"
-
-struct quirk_entry {
- u32 supported_func;
-};
-
-static struct quirk_entry quirk_no_sps_bug = {
- .supported_func = 0x4003,
-};
-
-static const struct dmi_system_id fwbug_list[] = {
- {
- .ident = "ROG Zephyrus G14",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "GA403U"),
- },
- .driver_data = &quirk_no_sps_bug,
- },
- {
- .ident = "ROG Ally X",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "RC72LA"),
- },
- .driver_data = &quirk_no_sps_bug,
- },
- {
- .ident = "ASUS TUF Gaming A14",
- .matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
- DMI_MATCH(DMI_PRODUCT_NAME, "FA401W"),
- },
- .driver_data = &quirk_no_sps_bug,
- },
- {}
-};
-
-void amd_pmf_quirks_init(struct amd_pmf_dev *dev)
-{
- const struct dmi_system_id *dmi_id;
- struct quirk_entry *quirks;
-
- dmi_id = dmi_first_match(fwbug_list);
- if (!dmi_id)
- return;
-
- quirks = dmi_id->driver_data;
- if (quirks->supported_func) {
- dev->supported_func = quirks->supported_func;
- pr_info("Using supported funcs quirk to avoid %s platform firmware bug\n",
- dmi_id->ident);
- }
-}
diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h
index a79808fda1d8..64ab532550ac 100644
--- a/drivers/platform/x86/amd/pmf/pmf.h
+++ b/drivers/platform/x86/amd/pmf/pmf.h
@@ -797,7 +797,4 @@ int amd_pmf_smartpc_apply_bios_output(struct amd_pmf_dev *dev, u32 val, u32 preq
void amd_pmf_populate_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
void amd_pmf_dump_ta_inputs(struct amd_pmf_dev *dev, struct ta_pmf_enact_table *in);
-/* Quirk infrastructure */
-void amd_pmf_quirks_init(struct amd_pmf_dev *dev);
-
#endif /* PMF_H */
diff --git a/drivers/platform/x86/amd/pmf/sps.c b/drivers/platform/x86/amd/pmf/sps.c
index 92f7fb22277d..bd2bd6cfc39a 100644
--- a/drivers/platform/x86/amd/pmf/sps.c
+++ b/drivers/platform/x86/amd/pmf/sps.c
@@ -405,6 +405,8 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev)
amd_pmf_set_sps_power_limits(dev);
}
+ dev->pprof.name = "amd-pmf";
+ dev->pprof.dev = dev->dev;
dev->pprof.profile_get = amd_pmf_profile_get;
dev->pprof.profile_set = amd_pmf_profile_set;
@@ -424,5 +426,5 @@ int amd_pmf_init_sps(struct amd_pmf_dev *dev)
void amd_pmf_deinit_sps(struct amd_pmf_dev *dev)
{
- platform_profile_remove();
+ platform_profile_remove(&dev->pprof);
}
diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c
index ba8b6d028f9f..fdeebab96fc0 100644
--- a/drivers/platform/x86/asus-wmi.c
+++ b/drivers/platform/x86/asus-wmi.c
@@ -3789,7 +3789,7 @@ static ssize_t throttle_thermal_policy_store(struct device *dev,
* Ensure that platform_profile updates userspace with the change to ensure
* that platform_profile and throttle_thermal_policy_mode are in sync.
*/
- platform_profile_notify();
+ platform_profile_notify(&asus->platform_profile_handler);
return count;
}
@@ -3876,6 +3876,8 @@ static int platform_profile_setup(struct asus_wmi *asus)
dev_info(dev, "Using throttle_thermal_policy for platform_profile support\n");
+ asus->platform_profile_handler.name = "asus-wmi";
+ asus->platform_profile_handler.dev = dev;
asus->platform_profile_handler.profile_get = asus_wmi_platform_profile_get;
asus->platform_profile_handler.profile_set = asus_wmi_platform_profile_set;
@@ -4850,7 +4852,7 @@ fail_sysfs:
fail_custom_fan_curve:
fail_platform_profile_setup:
if (asus->platform_profile_support)
- platform_profile_remove();
+ platform_profile_remove(&asus->platform_profile_handler);
fail_fan_boost_mode:
fail_platform:
kfree(asus);
@@ -4877,7 +4879,7 @@ static void asus_wmi_remove(struct platform_device *device)
asus_wmi_battery_exit(asus);
if (asus->platform_profile_support)
- platform_profile_remove();
+ platform_profile_remove(&asus->platform_profile_handler);
kfree(asus);
}
diff --git a/drivers/platform/x86/dell/alienware-wmi.c b/drivers/platform/x86/dell/alienware-wmi.c
index 77465ed9b449..e95d22c7b60c 100644
--- a/drivers/platform/x86/dell/alienware-wmi.c
+++ b/drivers/platform/x86/dell/alienware-wmi.c
@@ -1108,7 +1108,7 @@ static int thermal_profile_set(struct platform_profile_handler *pprof,
return wmax_thermal_control(supported_thermal_profiles[profile]);
}
-static int create_thermal_profile(void)
+static int create_thermal_profile(struct platform_device *platform_device)
{
u32 out_data;
u8 sys_desc[4];
@@ -1156,6 +1156,8 @@ static int create_thermal_profile(void)
pp_handler.profile_get = thermal_profile_get;
pp_handler.profile_set = thermal_profile_set;
+ pp_handler.name = "alienware-wmi";
+ pp_handler.dev = &platform_device->dev;
return platform_profile_register(&pp_handler);
}
@@ -1163,7 +1165,7 @@ static int create_thermal_profile(void)
static void remove_thermal_profile(void)
{
if (quirks->thermal)
- platform_profile_remove();
+ platform_profile_remove(&pp_handler);
}
static int __init alienware_wmi_init(void)
@@ -1224,7 +1226,7 @@ static int __init alienware_wmi_init(void)
}
if (quirks->thermal) {
- ret = create_thermal_profile();
+ ret = create_thermal_profile(platform_device);
if (ret)
goto fail_prep_thermal_profile;
}
diff --git a/drivers/platform/x86/dell/dell-pc.c b/drivers/platform/x86/dell/dell-pc.c
index 972385ca1990..3797a5721dbd 100644
--- a/drivers/platform/x86/dell/dell-pc.c
+++ b/drivers/platform/x86/dell/dell-pc.c
@@ -18,10 +18,13 @@
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/platform_profile.h>
+#include <linux/platform_device.h>
#include <linux/slab.h>
#include "dell-smbios.h"
+static struct platform_device *platform_device;
+
static const struct dmi_system_id dell_device_table[] __initconst = {
{
.ident = "Dell Inc.",
@@ -244,9 +247,17 @@ static int thermal_init(void)
if (!supported_modes)
return 0;
- thermal_handler = kzalloc(sizeof(*thermal_handler), GFP_KERNEL);
- if (!thermal_handler)
- return -ENOMEM;
+ platform_device = platform_device_register_simple("dell-pc", PLATFORM_DEVID_NONE, NULL, 0);
+ if (IS_ERR(platform_device))
+ return PTR_ERR(platform_device);
+
+ thermal_handler = devm_kzalloc(&platform_device->dev, sizeof(*thermal_handler), GFP_KERNEL);
+ if (!thermal_handler) {
+ ret = -ENOMEM;
+ goto cleanup_platform_device;
+ }
+ thermal_handler->name = "dell-pc";
+ thermal_handler->dev = &platform_device->dev;
thermal_handler->profile_get = thermal_platform_profile_get;
thermal_handler->profile_set = thermal_platform_profile_set;
@@ -261,20 +272,25 @@ static int thermal_init(void)
/* Clean up if failed */
ret = platform_profile_register(thermal_handler);
- if (ret) {
- kfree(thermal_handler);
- thermal_handler = NULL;
- }
+ if (ret)
+ goto cleanup_thermal_handler;
+
+ return 0;
+
+cleanup_thermal_handler:
+ thermal_handler = NULL;
+
+cleanup_platform_device:
+ platform_device_unregister(platform_device);
return ret;
}
static void thermal_cleanup(void)
{
- if (thermal_handler) {
- platform_profile_remove();
- kfree(thermal_handler);
- }
+ if (thermal_handler)
+ platform_profile_remove(thermal_handler);
+ platform_device_unregister(platform_device);
}
static int __init dell_init(void)
diff --git a/drivers/platform/x86/hp/hp-wmi.c b/drivers/platform/x86/hp/hp-wmi.c
index 81ccc96ffe40..6d6e13a0c6e2 100644
--- a/drivers/platform/x86/hp/hp-wmi.c
+++ b/drivers/platform/x86/hp/hp-wmi.c
@@ -1565,7 +1565,7 @@ static inline void omen_unregister_powersource_event_handler(void)
unregister_acpi_notifier(&platform_power_source_nb);
}
-static int thermal_profile_setup(void)
+static int thermal_profile_setup(struct platform_device *device)
{
int err, tp;
@@ -1624,6 +1624,8 @@ static int thermal_profile_setup(void)
set_bit(PLATFORM_PROFILE_COOL, platform_profile_handler.choices);
}
+ platform_profile_handler.name = "hp-wmi";
+ platform_profile_handler.dev = &device->dev;
set_bit(PLATFORM_PROFILE_BALANCED, platform_profile_handler.choices);
set_bit(PLATFORM_PROFILE_PERFORMANCE, platform_profile_handler.choices);
@@ -1663,7 +1665,7 @@ static int __init hp_wmi_bios_setup(struct platform_device *device)
if (err < 0)
return err;
- thermal_profile_setup();
+ thermal_profile_setup(device);
return 0;
}
@@ -1691,7 +1693,7 @@ static void __exit hp_wmi_bios_remove(struct platform_device *device)
}
if (platform_profile_support)
- platform_profile_remove();
+ platform_profile_remove(&platform_profile_handler);
}
static int hp_wmi_resume_handler(struct device *device)
diff --git a/drivers/platform/x86/ideapad-laptop.c b/drivers/platform/x86/ideapad-laptop.c
index e980dd18e5f6..dc98f862a06d 100644
--- a/drivers/platform/x86/ideapad-laptop.c
+++ b/drivers/platform/x86/ideapad-laptop.c
@@ -1041,7 +1041,7 @@ static void dytc_profile_refresh(struct ideapad_private *priv)
if (profile != priv->dytc->current_profile) {
priv->dytc->current_profile = profile;
- platform_profile_notify();
+ platform_profile_notify(&priv->dytc->pprof);
}
}
@@ -1102,6 +1102,8 @@ static int ideapad_dytc_profile_init(struct ideapad_private *priv)
mutex_init(&priv->dytc->mutex);
+ priv->dytc->pprof.name = "ideapad-laptop";
+ priv->dytc->pprof.dev = &priv->platform_device->dev;
priv->dytc->priv = priv;
priv->dytc->pprof.profile_get = dytc_profile_get;
priv->dytc->pprof.profile_set = dytc_profile_set;
@@ -1134,7 +1136,7 @@ static void ideapad_dytc_profile_exit(struct ideapad_private *priv)
if (!priv->dytc)
return;
- platform_profile_remove();
+ platform_profile_remove(&priv->dytc->pprof);
mutex_destroy(&priv->dytc->mutex);
kfree(priv->dytc);
diff --git a/drivers/platform/x86/inspur_platform_profile.c b/drivers/platform/x86/inspur_platform_profile.c
index 8440defa6788..53af73a7fbf7 100644
--- a/drivers/platform/x86/inspur_platform_profile.c
+++ b/drivers/platform/x86/inspur_platform_profile.c
@@ -177,6 +177,8 @@ static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
priv->wdev = wdev;
dev_set_drvdata(&wdev->dev, priv);
+ priv->handler.name = "inspur-wmi";
+ priv->handler.dev = &wdev->dev;
priv->handler.profile_get = inspur_platform_profile_get;
priv->handler.profile_set = inspur_platform_profile_set;
@@ -189,7 +191,10 @@ static int inspur_wmi_probe(struct wmi_device *wdev, const void *context)
static void inspur_wmi_remove(struct wmi_device *wdev)
{
- platform_profile_remove();
+ struct inspur_wmi_priv *priv;
+
+ priv = dev_get_drvdata(&wdev->dev);
+ platform_profile_remove(&priv->handler);
}
static const struct wmi_device_id inspur_wmi_id_table[] = {
diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c
index 6371a9f765c1..f51662861738 100644
--- a/drivers/platform/x86/thinkpad_acpi.c
+++ b/drivers/platform/x86/thinkpad_acpi.c
@@ -10538,6 +10538,12 @@ unlock:
return err;
}
+static struct platform_profile_handler dytc_profile = {
+ .name = "thinkpad-acpi",
+ .profile_get = dytc_profile_get,
+ .profile_set = dytc_profile_set,
+};
+
static void dytc_profile_refresh(void)
{
enum platform_profile_option profile;
@@ -10566,15 +10572,10 @@ static void dytc_profile_refresh(void)
err = convert_dytc_to_profile(funcmode, perfmode, &profile);
if (!err && profile != dytc_current_profile) {
dytc_current_profile = profile;
- platform_profile_notify();
+ platform_profile_notify(&dytc_profile);
}
}
-static struct platform_profile_handler dytc_profile = {
- .profile_get = dytc_profile_get,
- .profile_set = dytc_profile_set,
-};
-
static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
{
int err, output;
@@ -10637,6 +10638,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
dbg_printk(TPACPI_DBG_INIT,
"DYTC version %d: thermal mode available\n", dytc_version);
+ dytc_profile.dev = &tpacpi_pdev->dev;
/* Create platform_profile structure and register */
err = platform_profile_register(&dytc_profile);
/*
@@ -10658,7 +10660,7 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
static void dytc_profile_exit(void)
{
- platform_profile_remove();
+ platform_profile_remove(&dytc_profile);
}
static struct ibm_struct dytc_profile_driver_data = {
diff --git a/include/linux/platform_profile.h b/include/linux/platform_profile.h
index f5492ed413f3..0682bb4c57e5 100644
--- a/include/linux/platform_profile.h
+++ b/include/linux/platform_profile.h
@@ -23,10 +23,15 @@ enum platform_profile_option {
PLATFORM_PROFILE_BALANCED,
PLATFORM_PROFILE_BALANCED_PERFORMANCE,
PLATFORM_PROFILE_PERFORMANCE,
+ PLATFORM_PROFILE_CUSTOM,
PLATFORM_PROFILE_LAST, /*must always be last */
};
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);
@@ -35,8 +40,8 @@ struct platform_profile_handler {
};
int platform_profile_register(struct platform_profile_handler *pprof);
-int platform_profile_remove(void);
+int platform_profile_remove(struct platform_profile_handler *pprof);
int platform_profile_cycle(void);
-void platform_profile_notify(void);
+void platform_profile_notify(struct platform_profile_handler *pprof);
#endif /*_PLATFORM_PROFILE_H_*/