Merge branch 'platform-drivers-x86-platform-profile' into for-next
This commit is contained in:
@@ -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.
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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 */
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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);
|
||||
|
||||
|
||||
@@ -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[] = {
|
||||
|
||||
@@ -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 = {
|
||||
|
||||
@@ -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_*/
|
||||
|
||||
Reference in New Issue
Block a user