1
0

Merge branch 'platform-drivers-x86-platform-profile' into for-next

This commit is contained in:
Ilpo Järvinen
2024-12-12 16:07:26 +02:00
18 changed files with 564 additions and 219 deletions

View File

@@ -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.

View File

@@ -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.

View File

@@ -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)
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 <markpearson@lenovo.com>");
MODULE_DESCRIPTION("ACPI platform profile sysfs interface");
MODULE_LICENSE("GPL");

View File

@@ -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[] = {

View File

@@ -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

View File

@@ -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

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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 */

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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;
}

View File

@@ -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)

View File

@@ -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)

View File

@@ -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);

View File

@@ -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[] = {

View File

@@ -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 = {

View File

@@ -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_*/