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 #include #include +#include #include #include #include -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) +static DEFINE_IDA(platform_profile_ida); + +/** + * _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 len = 0; - int err, i; + int i, len = 0; - err = mutex_lock_interruptible(&profile_lock); - if (err) - return err; - - if (!cur_profile) { - mutex_unlock(&profile_lock); - return -ENODEV; - } - - 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) { - enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED; + 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) +{ + 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 (!cur_profile) { - mutex_unlock(&profile_lock); - return -ENODEV; } - err = cur_profile->profile_get(cur_profile, &profile); - mutex_unlock(&profile_lock); - if (err) - return err; + if (WARN_ON(val >= PLATFORM_PROFILE_LAST)) + return -EINVAL; + *profile = val; - /* Check that profile is valid index */ - if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names)))) - return -EIO; + 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]); } -static ssize_t platform_profile_store(struct device *dev, - struct device_attribute *attr, - const char *buf, size_t count) +/** + * 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 err, i; + int index, ret; - err = mutex_lock_interruptible(&profile_lock); + index = sysfs_match_string(profile_names, buf); + if (index < 0) + return -EINVAL; + + scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) { + ret = _store_class_profile(dev, &index); + if (ret) + return ret; + } + + 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; - if (!cur_profile) { - mutex_unlock(&profile_lock); - return -ENODEV; + if (*profile != PLATFORM_PROFILE_LAST && *profile != val) + *profile = PLATFORM_PROFILE_CUSTOM; + else + *profile = val; + + return 0; +} + +/** + * _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) +{ + enum platform_profile_option *profile = data; + int err; + + 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; + + 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; - } + sysfs_notify(acpi_kobj, NULL, "platform_profile"); - err = cur_profile->profile_set(cur_profile, i); - if (!err) - 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 = class_for_each_device(&platform_profile_class, NULL, + choices, _aggregate_choices); + if (err) + return err; + + /* never iterate into a custom if all drivers supported it */ + clear_bit(PLATFORM_PROFILE_CUSTOM, choices); + + next = find_next_bit_wrap(choices, + PLATFORM_PROFILE_LAST, + profile + 1); + + err = class_for_each_device(&platform_profile_class, NULL, &next, + _store_and_notify); + + if (err) + return err; } - err = cur_profile->profile_get(cur_profile, &profile); - if (err) { - mutex_unlock(&profile_lock); - return err; - } + sysfs_notify(acpi_kobj, NULL, "platform_profile"); - next = find_next_bit_wrap(cur_profile->choices, PLATFORM_PROFILE_LAST, - profile + 1); - - if (WARN_ON(next == PLATFORM_PROFILE_LAST)) { - mutex_unlock(&profile_lock); - return -EINVAL; - } - - err = cur_profile->profile_set(cur_profile, next); - mutex_unlock(&profile_lock); - - if (!err) - sysfs_notify(acpi_kobj, NULL, "platform_profile"); - - return err; + 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 "); 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 - */ - -#include - -#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 #include #include +#include #include #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_*/