Compare commits
30 Commits
a3255848e2
...
b2ec0c8f53
Author | SHA1 | Date |
---|---|---|
|
b2ec0c8f53 | |
|
2613207f86 | |
|
0c339d758b | |
|
44996180ca | |
|
b05ba06a51 | |
|
c57e4b77fc | |
|
f6af07a496 | |
|
93d900a094 | |
|
f5e52a6076 | |
|
45f2db52c6 | |
|
f253124ccc | |
|
9c9e50d974 | |
|
3c62a3823f | |
|
fb9f763c1e | |
|
51c2c3eb01 | |
|
8c07f32d52 | |
|
e45261c7ab | |
|
28f4eae68b | |
|
dc049d84dc | |
|
8ead803dab | |
|
460c9520ae | |
|
914c7744d1 | |
|
74416b223d | |
|
8ae1ea21ec | |
|
0bc9ecab4c | |
|
4dfed50f30 | |
|
656189a4f4 | |
|
ed58761c17 | |
|
2e96a5392a | |
|
89f1924b3e |
|
@ -0,0 +1,48 @@
|
|||
What: /sys/class/platform-profile/platform-profile-X/name
|
||||
Date: March 2025
|
||||
KernelVersion: 6.14
|
||||
Description: Name of the class device given by the driver.
|
||||
|
||||
RO
|
||||
|
||||
What: /sys/class/platform-profile/platform-profile-X/choices
|
||||
Date: March 2025
|
||||
KernelVersion: 6.14
|
||||
Description: This file contains a space-separated list of profiles supported
|
||||
for this device.
|
||||
|
||||
Drivers must use the following standard profile-names:
|
||||
|
||||
==================== ========================================
|
||||
low-power Low power consumption
|
||||
cool Cooler operation
|
||||
quiet Quieter operation
|
||||
balanced Balance between low power consumption
|
||||
and performance
|
||||
balanced-performance Balance between performance and low
|
||||
power consumption with a slight bias
|
||||
towards performance
|
||||
performance High performance operation
|
||||
custom Driver defined custom profile
|
||||
==================== ========================================
|
||||
|
||||
RO
|
||||
|
||||
What: /sys/class/platform-profile/platform-profile-X/profile
|
||||
Date: March 2025
|
||||
KernelVersion: 6.14
|
||||
Description: Reading this file gives the current selected profile for this
|
||||
device. Writing this file with one of the strings from
|
||||
platform_profile_choices changes the profile to the new value.
|
||||
|
||||
This file can be monitored for changes by polling for POLLPRI,
|
||||
POLLPRI will be signaled on any changes, independent of those
|
||||
changes coming from a userspace write; or coming from another
|
||||
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 the driver is using a driver defined custom profile.
|
||||
|
||||
RW
|
|
@ -2,17 +2,34 @@
|
|||
|
||||
/* Platform profile sysfs interface */
|
||||
|
||||
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
|
||||
|
||||
#include <linux/acpi.h>
|
||||
#include <linux/bits.h>
|
||||
#include <linux/cleanup.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/mutex.h>
|
||||
#include <linux/platform_profile.h>
|
||||
#include <linux/sysfs.h>
|
||||
|
||||
#define to_pprof_handler(d) (container_of(d, struct platform_profile_handler, class_dev))
|
||||
#define to_pprof_handler(d) (container_of(d, struct platform_profile_handler, dev))
|
||||
|
||||
static DEFINE_MUTEX(profile_lock);
|
||||
|
||||
struct platform_profile_handler {
|
||||
const char *name;
|
||||
struct device dev;
|
||||
int minor;
|
||||
unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
|
||||
unsigned long hidden_choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
|
||||
const struct platform_profile_ops *ops;
|
||||
};
|
||||
|
||||
struct aggregate_choices_data {
|
||||
unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
|
||||
int count;
|
||||
};
|
||||
|
||||
static const char * const profile_names[] = {
|
||||
[PLATFORM_PROFILE_LOW_POWER] = "low-power",
|
||||
[PLATFORM_PROFILE_COOL] = "cool",
|
||||
|
@ -62,7 +79,7 @@ static int _store_class_profile(struct device *dev, void *data)
|
|||
|
||||
lockdep_assert_held(&profile_lock);
|
||||
handler = to_pprof_handler(dev);
|
||||
if (!test_bit(*bit, handler->choices))
|
||||
if (!test_bit(*bit, handler->choices) && !test_bit(*bit, handler->hidden_choices))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
return handler->ops->profile_set(dev, *bit);
|
||||
|
@ -80,8 +97,8 @@ static int _notify_class_profile(struct device *dev, void *data)
|
|||
struct platform_profile_handler *handler = to_pprof_handler(dev);
|
||||
|
||||
lockdep_assert_held(&profile_lock);
|
||||
sysfs_notify(&handler->class_dev.kobj, NULL, "profile");
|
||||
kobject_uevent(&handler->class_dev.kobj, KOBJ_CHANGE);
|
||||
sysfs_notify(&handler->dev.kobj, NULL, "profile");
|
||||
kobject_uevent(&handler->dev.kobj, KOBJ_CHANGE);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -212,61 +229,101 @@ static struct attribute *profile_attrs[] = {
|
|||
};
|
||||
ATTRIBUTE_GROUPS(profile);
|
||||
|
||||
static void pprof_device_release(struct device *dev)
|
||||
{
|
||||
struct platform_profile_handler *pprof = to_pprof_handler(dev);
|
||||
|
||||
kfree(pprof);
|
||||
}
|
||||
|
||||
static const struct class platform_profile_class = {
|
||||
.name = "platform-profile",
|
||||
.dev_groups = profile_groups,
|
||||
.dev_release = pprof_device_release,
|
||||
};
|
||||
|
||||
/**
|
||||
* _aggregate_choices - Aggregate the available profile choices
|
||||
* @dev: The device
|
||||
* @data: The available profile choices
|
||||
* @arg: struct aggregate_choices_data
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
static int _aggregate_choices(struct device *dev, void *data)
|
||||
static int _aggregate_choices(struct device *dev, void *arg)
|
||||
{
|
||||
unsigned long tmp[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
|
||||
struct aggregate_choices_data *data = arg;
|
||||
struct platform_profile_handler *handler;
|
||||
unsigned long *aggregate = data;
|
||||
|
||||
lockdep_assert_held(&profile_lock);
|
||||
handler = to_pprof_handler(dev);
|
||||
if (test_bit(PLATFORM_PROFILE_LAST, aggregate))
|
||||
bitmap_copy(aggregate, handler->choices, PLATFORM_PROFILE_LAST);
|
||||
bitmap_or(tmp, handler->choices, handler->hidden_choices, PLATFORM_PROFILE_LAST);
|
||||
if (test_bit(PLATFORM_PROFILE_LAST, data->aggregate))
|
||||
bitmap_copy(data->aggregate, tmp, PLATFORM_PROFILE_LAST);
|
||||
else
|
||||
bitmap_and(aggregate, handler->choices, aggregate, PLATFORM_PROFILE_LAST);
|
||||
bitmap_and(data->aggregate, tmp, data->aggregate, PLATFORM_PROFILE_LAST);
|
||||
data->count++;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* _remove_hidden_choices - Remove hidden choices from aggregate data
|
||||
* @dev: The device
|
||||
* @arg: struct aggregate_choices_data
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
static int _remove_hidden_choices(struct device *dev, void *arg)
|
||||
{
|
||||
struct aggregate_choices_data *data = arg;
|
||||
struct platform_profile_handler *handler;
|
||||
|
||||
lockdep_assert_held(&profile_lock);
|
||||
handler = to_pprof_handler(dev);
|
||||
bitmap_andnot(data->aggregate, handler->choices,
|
||||
handler->hidden_choices, PLATFORM_PROFILE_LAST);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* platform_profile_choices_show - Show the available profile choices for legacy sysfs interface
|
||||
* @dev: The device
|
||||
* @kobj: The kobject
|
||||
* @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,
|
||||
static ssize_t platform_profile_choices_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
|
||||
struct aggregate_choices_data data = {
|
||||
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
|
||||
.count = 0,
|
||||
};
|
||||
int err;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_LAST, aggregate);
|
||||
set_bit(PLATFORM_PROFILE_LAST, data.aggregate);
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
|
||||
err = class_for_each_device(&platform_profile_class, NULL,
|
||||
aggregate, _aggregate_choices);
|
||||
&data, _aggregate_choices);
|
||||
if (err)
|
||||
return err;
|
||||
if (data.count == 1) {
|
||||
err = class_for_each_device(&platform_profile_class, NULL,
|
||||
&data, _remove_hidden_choices);
|
||||
if (err)
|
||||
return err;
|
||||
}
|
||||
}
|
||||
|
||||
/* no profile handler registered any more */
|
||||
if (bitmap_empty(aggregate, PLATFORM_PROFILE_LAST))
|
||||
if (bitmap_empty(data.aggregate, PLATFORM_PROFILE_LAST))
|
||||
return -EINVAL;
|
||||
|
||||
return _commmon_choices_show(aggregate, buf);
|
||||
return _commmon_choices_show(data.aggregate, buf);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -314,14 +371,14 @@ static int _store_and_notify(struct device *dev, void *data)
|
|||
|
||||
/**
|
||||
* platform_profile_show - Show the current profile for legacy sysfs interface
|
||||
* @dev: The device
|
||||
* @kobj: The kobject
|
||||
* @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,
|
||||
static ssize_t platform_profile_show(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
|
||||
|
@ -343,18 +400,21 @@ static ssize_t platform_profile_show(struct device *dev,
|
|||
|
||||
/**
|
||||
* platform_profile_store - Set the profile for legacy sysfs interface
|
||||
* @dev: The device
|
||||
* @kobj: The kobject
|
||||
* @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,
|
||||
static ssize_t platform_profile_store(struct kobject *kobj,
|
||||
struct kobj_attribute *attr,
|
||||
const char *buf, size_t count)
|
||||
{
|
||||
unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
|
||||
struct aggregate_choices_data data = {
|
||||
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
|
||||
.count = 0,
|
||||
};
|
||||
int ret;
|
||||
int i;
|
||||
|
||||
|
@ -362,13 +422,13 @@ static ssize_t platform_profile_store(struct device *dev,
|
|||
i = sysfs_match_string(profile_names, buf);
|
||||
if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
|
||||
return -EINVAL;
|
||||
set_bit(PLATFORM_PROFILE_LAST, choices);
|
||||
set_bit(PLATFORM_PROFILE_LAST, data.aggregate);
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
|
||||
ret = class_for_each_device(&platform_profile_class, NULL,
|
||||
choices, _aggregate_choices);
|
||||
&data, _aggregate_choices);
|
||||
if (ret)
|
||||
return ret;
|
||||
if (!test_bit(i, choices))
|
||||
if (!test_bit(i, data.aggregate))
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
ret = class_for_each_device(&platform_profile_class, NULL, &i,
|
||||
|
@ -382,12 +442,12 @@ static ssize_t platform_profile_store(struct device *dev,
|
|||
return count;
|
||||
}
|
||||
|
||||
static DEVICE_ATTR_RO(platform_profile_choices);
|
||||
static DEVICE_ATTR_RW(platform_profile);
|
||||
static struct kobj_attribute attr_platform_profile_choices = __ATTR_RO(platform_profile_choices);
|
||||
static struct kobj_attribute attr_platform_profile = __ATTR_RW(platform_profile);
|
||||
|
||||
static struct attribute *platform_profile_attrs[] = {
|
||||
&dev_attr_platform_profile_choices.attr,
|
||||
&dev_attr_platform_profile.attr,
|
||||
&attr_platform_profile_choices.attr,
|
||||
&attr_platform_profile.attr,
|
||||
NULL
|
||||
};
|
||||
|
||||
|
@ -398,8 +458,14 @@ static int profile_class_registered(struct device *dev, const void *data)
|
|||
|
||||
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))
|
||||
struct device *dev;
|
||||
|
||||
dev = class_find_device(&platform_profile_class, NULL, NULL, profile_class_registered);
|
||||
if (!dev)
|
||||
return 0;
|
||||
|
||||
put_device(dev);
|
||||
|
||||
return attr->mode;
|
||||
}
|
||||
|
||||
|
@ -408,23 +474,35 @@ static const struct attribute_group platform_profile_group = {
|
|||
.is_visible = profile_class_is_visible,
|
||||
};
|
||||
|
||||
void platform_profile_notify(struct platform_profile_handler *pprof)
|
||||
/**
|
||||
* platform_profile_notify - Notify class device and legacy sysfs interface
|
||||
* @dev: The class device
|
||||
*/
|
||||
void platform_profile_notify(struct device *dev)
|
||||
{
|
||||
scoped_cond_guard(mutex_intr, return, &profile_lock) {
|
||||
_notify_class_profile(&pprof->class_dev, NULL);
|
||||
_notify_class_profile(dev, NULL);
|
||||
}
|
||||
sysfs_notify(acpi_kobj, NULL, "platform_profile");
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_profile_notify);
|
||||
|
||||
/**
|
||||
* platform_profile_cycle - Cycles profiles available on all registered class devices
|
||||
*
|
||||
* Return: 0 on success, -errno on failure
|
||||
*/
|
||||
int platform_profile_cycle(void)
|
||||
{
|
||||
struct aggregate_choices_data data = {
|
||||
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
|
||||
.count = 0,
|
||||
};
|
||||
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;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_LAST, choices);
|
||||
set_bit(PLATFORM_PROFILE_LAST, data.aggregate);
|
||||
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
|
||||
err = class_for_each_device(&platform_profile_class, NULL,
|
||||
&profile, _aggregate_profiles);
|
||||
|
@ -436,14 +514,14 @@ int platform_profile_cycle(void)
|
|||
return -EINVAL;
|
||||
|
||||
err = class_for_each_device(&platform_profile_class, NULL,
|
||||
choices, _aggregate_choices);
|
||||
&data, _aggregate_choices);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* never iterate into a custom if all drivers supported it */
|
||||
clear_bit(PLATFORM_PROFILE_CUSTOM, choices);
|
||||
clear_bit(PLATFORM_PROFILE_CUSTOM, data.aggregate);
|
||||
|
||||
next = find_next_bit_wrap(choices,
|
||||
next = find_next_bit_wrap(data.aggregate,
|
||||
PLATFORM_PROFILE_LAST,
|
||||
profile + 1);
|
||||
|
||||
|
@ -460,42 +538,71 @@ int platform_profile_cycle(void)
|
|||
}
|
||||
EXPORT_SYMBOL_GPL(platform_profile_cycle);
|
||||
|
||||
int platform_profile_register(struct platform_profile_handler *pprof, void *drvdata)
|
||||
/**
|
||||
* platform_profile_register - Creates and registers a platform profile class device
|
||||
* @dev: Parent device
|
||||
* @name: Name of the class device
|
||||
* @drvdata: Driver data that will be attached to the class device
|
||||
* @ops: Platform profile's mandatory operations
|
||||
*
|
||||
* Return: pointer to the new class device on success, ERR_PTR on failure
|
||||
*/
|
||||
struct device *platform_profile_register(struct device *dev, const char *name,
|
||||
void *drvdata,
|
||||
const struct platform_profile_ops *ops)
|
||||
{
|
||||
struct device *ppdev;
|
||||
int minor;
|
||||
int err;
|
||||
|
||||
/* Sanity check the profile handler */
|
||||
if (!pprof || !pprof->ops->profile_set || !pprof->ops->profile_get ||
|
||||
!pprof->ops->probe) {
|
||||
pr_err("platform_profile: handler is invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
/* Sanity check */
|
||||
if (WARN_ON_ONCE(!dev || !name || !ops || !ops->profile_get ||
|
||||
!ops->profile_set || !ops->probe))
|
||||
return ERR_PTR(-EINVAL);
|
||||
|
||||
err = pprof->ops->probe(drvdata, pprof->choices);
|
||||
struct platform_profile_handler *pprof __free(kfree) = kzalloc(
|
||||
sizeof(*pprof), GFP_KERNEL);
|
||||
if (!pprof)
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
err = ops->probe(drvdata, pprof->choices);
|
||||
if (err) {
|
||||
dev_err(pprof->dev, "platform_profile probe failed\n");
|
||||
return err;
|
||||
dev_err(dev, "platform_profile probe failed\n");
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
|
||||
if (bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST)) {
|
||||
dev_err(pprof->dev, "Failed to register a platform_profile class device with empty choices\n");
|
||||
return -EINVAL;
|
||||
dev_err(dev, "Failed to register platform_profile class device with empty choices\n");
|
||||
return ERR_PTR(-EINVAL);
|
||||
}
|
||||
|
||||
if (ops->hidden_choices) {
|
||||
err = ops->hidden_choices(drvdata, pprof->hidden_choices);
|
||||
if (err) {
|
||||
dev_err(dev, "platform_profile hidden_choices failed\n");
|
||||
return ERR_PTR(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;
|
||||
minor = ida_alloc(&platform_profile_ida, GFP_KERNEL);
|
||||
if (minor < 0)
|
||||
return ERR_PTR(minor);
|
||||
|
||||
pprof->class_dev.class = &platform_profile_class;
|
||||
pprof->class_dev.parent = pprof->dev;
|
||||
dev_set_drvdata(&pprof->class_dev, drvdata);
|
||||
dev_set_name(&pprof->class_dev, "platform-profile-%d", pprof->minor);
|
||||
err = device_register(&pprof->class_dev);
|
||||
pprof->name = name;
|
||||
pprof->ops = ops;
|
||||
pprof->minor = minor;
|
||||
pprof->dev.class = &platform_profile_class;
|
||||
pprof->dev.parent = dev;
|
||||
dev_set_drvdata(&pprof->dev, drvdata);
|
||||
dev_set_name(&pprof->dev, "platform-profile-%d", pprof->minor);
|
||||
/* device_register() takes ownership of pprof/ppdev */
|
||||
ppdev = &no_free_ptr(pprof)->dev;
|
||||
err = device_register(ppdev);
|
||||
if (err) {
|
||||
put_device(&pprof->class_dev);
|
||||
put_device(ppdev);
|
||||
goto cleanup_ida;
|
||||
}
|
||||
|
||||
|
@ -505,61 +612,78 @@ int platform_profile_register(struct platform_profile_handler *pprof, void *drvd
|
|||
if (err)
|
||||
goto cleanup_cur;
|
||||
|
||||
return 0;
|
||||
return ppdev;
|
||||
|
||||
cleanup_cur:
|
||||
device_unregister(&pprof->class_dev);
|
||||
device_unregister(ppdev);
|
||||
|
||||
cleanup_ida:
|
||||
ida_free(&platform_profile_ida, pprof->minor);
|
||||
ida_free(&platform_profile_ida, minor);
|
||||
|
||||
return err;
|
||||
return ERR_PTR(err);
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_profile_register);
|
||||
|
||||
int platform_profile_remove(struct platform_profile_handler *pprof)
|
||||
/**
|
||||
* platform_profile_remove - Unregisters a platform profile class device
|
||||
* @dev: Class device
|
||||
*/
|
||||
void platform_profile_remove(struct device *dev)
|
||||
{
|
||||
int id;
|
||||
struct platform_profile_handler *pprof;
|
||||
|
||||
if (IS_ERR_OR_NULL(dev))
|
||||
return;
|
||||
|
||||
pprof = to_pprof_handler(dev);
|
||||
|
||||
guard(mutex)(&profile_lock);
|
||||
|
||||
id = pprof->minor;
|
||||
device_unregister(&pprof->class_dev);
|
||||
ida_free(&platform_profile_ida, id);
|
||||
ida_free(&platform_profile_ida, pprof->minor);
|
||||
device_unregister(&pprof->dev);
|
||||
|
||||
sysfs_notify(acpi_kobj, NULL, "platform_profile");
|
||||
|
||||
sysfs_update_group(acpi_kobj, &platform_profile_group);
|
||||
|
||||
return 0;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(platform_profile_remove);
|
||||
|
||||
static void devm_platform_profile_release(struct device *dev, void *res)
|
||||
{
|
||||
struct platform_profile_handler **pprof = res;
|
||||
struct device **ppdev = res;
|
||||
|
||||
platform_profile_remove(*pprof);
|
||||
platform_profile_remove(*ppdev);
|
||||
}
|
||||
|
||||
int devm_platform_profile_register(struct platform_profile_handler *pprof, void *drvdata)
|
||||
/**
|
||||
* devm_platform_profile_register - Device managed version of platform_profile_register
|
||||
* @dev: Parent device
|
||||
* @name: Name of the class device
|
||||
* @drvdata: Driver data that will be attached to the class device
|
||||
* @ops: Platform profile's mandatory operations
|
||||
*
|
||||
* Return: pointer to the new class device on success, ERR_PTR on failure
|
||||
*/
|
||||
struct device *devm_platform_profile_register(struct device *dev, const char *name,
|
||||
void *drvdata,
|
||||
const struct platform_profile_ops *ops)
|
||||
{
|
||||
struct platform_profile_handler **dr;
|
||||
int ret;
|
||||
struct device *ppdev;
|
||||
struct device **dr;
|
||||
|
||||
dr = devres_alloc(devm_platform_profile_release, sizeof(*dr), GFP_KERNEL);
|
||||
if (!dr)
|
||||
return -ENOMEM;
|
||||
return ERR_PTR(-ENOMEM);
|
||||
|
||||
ret = platform_profile_register(pprof, drvdata);
|
||||
if (ret) {
|
||||
ppdev = platform_profile_register(dev, name, drvdata, ops);
|
||||
if (IS_ERR(ppdev)) {
|
||||
devres_free(dr);
|
||||
return ret;
|
||||
return ppdev;
|
||||
}
|
||||
|
||||
*dr = pprof;
|
||||
devres_add(pprof->dev, dr);
|
||||
*dr = ppdev;
|
||||
devres_add(dev, dr);
|
||||
|
||||
return 0;
|
||||
return ppdev;
|
||||
}
|
||||
EXPORT_SYMBOL_GPL(devm_platform_profile_register);
|
||||
|
||||
|
@ -567,6 +691,9 @@ static int __init platform_profile_init(void)
|
|||
{
|
||||
int err;
|
||||
|
||||
if (acpi_disabled)
|
||||
return -EOPNOTSUPP;
|
||||
|
||||
err = class_register(&platform_profile_class);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
@ -29,7 +29,7 @@ struct ssam_tmp_profile_info {
|
|||
|
||||
struct ssam_tmp_profile_device {
|
||||
struct ssam_device *sdev;
|
||||
struct platform_profile_handler handler;
|
||||
struct device *ppdev;
|
||||
};
|
||||
|
||||
SSAM_DEFINE_SYNC_REQUEST_CL_R(__ssam_tmp_profile_get, struct ssam_tmp_profile_info, {
|
||||
|
@ -168,25 +168,15 @@ static int surface_platform_profile_probe(struct ssam_device *sdev)
|
|||
tpd->sdev = sdev;
|
||||
ssam_device_set_drvdata(sdev, tpd);
|
||||
|
||||
tpd->handler.name = "Surface Platform Profile";
|
||||
tpd->handler.dev = &sdev->dev;
|
||||
tpd->handler.ops = &ssam_platform_profile_ops;
|
||||
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, tpd->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED, tpd->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, tpd->handler.choices);
|
||||
set_bit(PLATFORM_PROFILE_PERFORMANCE, tpd->handler.choices);
|
||||
|
||||
platform_profile_register(&tpd->handler, tpd);
|
||||
return 0;
|
||||
}
|
||||
tpd->ppdev = devm_platform_profile_register(&sdev->dev, "Surface Platform Profile",
|
||||
tpd, &ssam_platform_profile_ops);
|
||||
|
||||
static void surface_platform_profile_remove(struct ssam_device *sdev)
|
||||
{
|
||||
struct ssam_platform_profile_device *tpd;
|
||||
|
||||
tpd = ssam_device_get_drvdata(sdev);
|
||||
platform_profile_remove(&tpd->handler);
|
||||
return PTR_ERR_OR_ZERO(tpd->ppdev);
|
||||
}
|
||||
|
||||
static const struct ssam_device_id ssam_platform_profile_match[] = {
|
||||
|
@ -197,7 +187,6 @@ MODULE_DEVICE_TABLE(ssam, ssam_platform_profile_match);
|
|||
|
||||
static struct ssam_device_driver surface_platform_profile = {
|
||||
.probe = surface_platform_profile_probe,
|
||||
.remove = surface_platform_profile_remove,
|
||||
.match_table = ssam_platform_profile_match,
|
||||
.driver = {
|
||||
.name = "surface_platform_profile",
|
||||
|
|
|
@ -220,7 +220,7 @@ static void apmf_sbios_heartbeat_notify(struct work_struct *work)
|
|||
if (!info)
|
||||
return;
|
||||
|
||||
schedule_delayed_work(&dev->heart_beat, msecs_to_jiffies(dev->hb_interval * 1000));
|
||||
schedule_delayed_work(&dev->heart_beat, secs_to_jiffies(dev->hb_interval));
|
||||
kfree(info);
|
||||
}
|
||||
|
||||
|
|
|
@ -120,9 +120,9 @@ static void amd_pmf_set_automode(struct amd_pmf_dev *dev, int idx,
|
|||
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pwr_ctrl->sppt_apu_only, NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pwr_ctrl->stt_min, NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
|
||||
pwr_ctrl->stt_skin_temp[STT_TEMP_APU], NULL);
|
||||
fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_APU]), NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
|
||||
pwr_ctrl->stt_skin_temp[STT_TEMP_HS2], NULL);
|
||||
fixp_q88_fromint(pwr_ctrl->stt_skin_temp[STT_TEMP_HS2]), NULL);
|
||||
|
||||
if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))
|
||||
apmf_update_fan_idx(dev, config_store.mode_set[idx].fan_control.manual,
|
||||
|
|
|
@ -81,10 +81,10 @@ static int amd_pmf_set_cnqf(struct amd_pmf_dev *dev, int src, int idx,
|
|||
amd_pmf_send_cmd(dev, SET_SPPT, false, pc->sppt, NULL);
|
||||
amd_pmf_send_cmd(dev, SET_SPPT_APU_ONLY, false, pc->sppt_apu_only, NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false, pc->stt_min, NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, pc->stt_skin_temp[STT_TEMP_APU],
|
||||
NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, pc->stt_skin_temp[STT_TEMP_HS2],
|
||||
NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
|
||||
fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_APU]), NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
|
||||
fixp_q88_fromint(pc->stt_skin_temp[STT_TEMP_HS2]), NULL);
|
||||
|
||||
if (is_apmf_func_supported(dev, APMF_FUNC_SET_FAN_IDX))
|
||||
apmf_update_fan_idx(dev,
|
||||
|
|
|
@ -176,6 +176,20 @@ static void __maybe_unused amd_pmf_dump_registers(struct amd_pmf_dev *dev)
|
|||
dev_dbg(dev->dev, "AMD_PMF_REGISTER_MESSAGE:%x\n", value);
|
||||
}
|
||||
|
||||
/**
|
||||
* fixp_q88_fromint: Convert integer to Q8.8
|
||||
* @val: input value
|
||||
*
|
||||
* Converts an integer into binary fixed point format where 8 bits
|
||||
* are used for integer and 8 bits are used for the decimal.
|
||||
*
|
||||
* Return: unsigned integer converted to Q8.8 format
|
||||
*/
|
||||
u32 fixp_q88_fromint(u32 val)
|
||||
{
|
||||
return val << 8;
|
||||
}
|
||||
|
||||
int amd_pmf_send_cmd(struct amd_pmf_dev *dev, u8 message, bool get, u32 arg, u32 *data)
|
||||
{
|
||||
int rc;
|
||||
|
@ -266,7 +280,7 @@ int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer)
|
|||
dev_err(dev->dev, "Invalid CPU id: 0x%x", dev->cpu_id);
|
||||
}
|
||||
|
||||
dev->buf = kzalloc(dev->mtable_size, GFP_KERNEL);
|
||||
dev->buf = devm_kzalloc(dev->dev, dev->mtable_size, GFP_KERNEL);
|
||||
if (!dev->buf)
|
||||
return -ENOMEM;
|
||||
}
|
||||
|
@ -452,6 +466,7 @@ static int amd_pmf_probe(struct platform_device *pdev)
|
|||
|
||||
mutex_init(&dev->lock);
|
||||
mutex_init(&dev->update_mutex);
|
||||
mutex_init(&dev->cb_mutex);
|
||||
|
||||
apmf_acpi_init(dev);
|
||||
platform_set_drvdata(pdev, dev);
|
||||
|
@ -477,7 +492,7 @@ static void amd_pmf_remove(struct platform_device *pdev)
|
|||
amd_pmf_dbgfs_unregister(dev);
|
||||
mutex_destroy(&dev->lock);
|
||||
mutex_destroy(&dev->update_mutex);
|
||||
kfree(dev->buf);
|
||||
mutex_destroy(&dev->cb_mutex);
|
||||
}
|
||||
|
||||
static const struct attribute_group *amd_pmf_driver_groups[] = {
|
||||
|
|
|
@ -106,9 +106,12 @@ struct cookie_header {
|
|||
#define PMF_TA_IF_VERSION_MAJOR 1
|
||||
#define TA_PMF_ACTION_MAX 32
|
||||
#define TA_PMF_UNDO_MAX 8
|
||||
#define TA_OUTPUT_RESERVED_MEM 906
|
||||
#define TA_OUTPUT_RESERVED_MEM 922
|
||||
#define MAX_OPERATION_PARAMS 4
|
||||
|
||||
#define TA_ERROR_CRYPTO_INVALID_PARAM 0x20002
|
||||
#define TA_ERROR_CRYPTO_BIN_TOO_LARGE 0x2000d
|
||||
|
||||
#define PMF_IF_V1 1
|
||||
#define PMF_IF_V2 2
|
||||
|
||||
|
@ -338,7 +341,7 @@ struct amd_pmf_dev {
|
|||
struct mutex lock; /* protects the PMF interface */
|
||||
u32 supported_func;
|
||||
enum platform_profile_option current_profile;
|
||||
struct platform_profile_handler pprof;
|
||||
struct device *ppdev; /* platform profile class device */
|
||||
struct dentry *dbgfs_dir;
|
||||
int hb_interval; /* SBIOS heartbeat interval */
|
||||
struct delayed_work heart_beat;
|
||||
|
@ -774,6 +777,7 @@ int apmf_install_handler(struct amd_pmf_dev *pmf_dev);
|
|||
int apmf_os_power_slider_update(struct amd_pmf_dev *dev, u8 flag);
|
||||
int amd_pmf_set_dram_addr(struct amd_pmf_dev *dev, bool alloc_buffer);
|
||||
int amd_pmf_notify_sbios_heartbeat_event_v2(struct amd_pmf_dev *dev, u8 flag);
|
||||
u32 fixp_q88_fromint(u32 val);
|
||||
|
||||
/* SPS Layer */
|
||||
int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf);
|
||||
|
|
|
@ -219,12 +219,14 @@ static int amd_pmf_get_slider_info(struct amd_pmf_dev *dev, struct ta_pmf_enact_
|
|||
|
||||
switch (dev->current_profile) {
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
|
||||
val = TA_BEST_PERFORMANCE;
|
||||
break;
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
val = TA_BETTER_PERFORMANCE;
|
||||
break;
|
||||
case PLATFORM_PROFILE_LOW_POWER:
|
||||
case PLATFORM_PROFILE_QUIET:
|
||||
val = TA_BEST_BATTERY;
|
||||
break;
|
||||
default:
|
||||
|
|
|
@ -198,9 +198,11 @@ static void amd_pmf_update_slider_v2(struct amd_pmf_dev *dev, int idx)
|
|||
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
|
||||
apts_config_store.val[idx].stt_min_limit, NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
|
||||
apts_config_store.val[idx].stt_skin_temp_limit_apu, NULL);
|
||||
fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_apu),
|
||||
NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
|
||||
apts_config_store.val[idx].stt_skin_temp_limit_hs2, NULL);
|
||||
fixp_q88_fromint(apts_config_store.val[idx].stt_skin_temp_limit_hs2),
|
||||
NULL);
|
||||
}
|
||||
|
||||
void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
|
||||
|
@ -217,9 +219,11 @@ void amd_pmf_update_slider(struct amd_pmf_dev *dev, bool op, int idx,
|
|||
amd_pmf_send_cmd(dev, SET_STT_MIN_LIMIT, false,
|
||||
config_store.prop[src][idx].stt_min, NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
|
||||
config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU], NULL);
|
||||
fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_APU]),
|
||||
NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
|
||||
config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2], NULL);
|
||||
fixp_q88_fromint(config_store.prop[src][idx].stt_skin_temp[STT_TEMP_HS2]),
|
||||
NULL);
|
||||
} else if (op == SLIDER_OP_GET) {
|
||||
amd_pmf_send_cmd(dev, GET_SPL, true, ARG_NONE, &table->prop[src][idx].spl);
|
||||
amd_pmf_send_cmd(dev, GET_FPPT, true, ARG_NONE, &table->prop[src][idx].fppt);
|
||||
|
@ -297,12 +301,14 @@ int amd_pmf_get_pprof_modes(struct amd_pmf_dev *pmf)
|
|||
|
||||
switch (pmf->current_profile) {
|
||||
case PLATFORM_PROFILE_PERFORMANCE:
|
||||
case PLATFORM_PROFILE_BALANCED_PERFORMANCE:
|
||||
mode = POWER_MODE_PERFORMANCE;
|
||||
break;
|
||||
case PLATFORM_PROFILE_BALANCED:
|
||||
mode = POWER_MODE_BALANCED_POWER;
|
||||
break;
|
||||
case PLATFORM_PROFILE_LOW_POWER:
|
||||
case PLATFORM_PROFILE_QUIET:
|
||||
mode = POWER_MODE_POWER_SAVER;
|
||||
break;
|
||||
default:
|
||||
|
@ -387,6 +393,14 @@ static int amd_pmf_profile_set(struct device *dev,
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmf_hidden_choices(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
set_bit(PLATFORM_PROFILE_QUIET, choices);
|
||||
set_bit(PLATFORM_PROFILE_BALANCED_PERFORMANCE, choices);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmf_profile_probe(void *drvdata, unsigned long *choices)
|
||||
{
|
||||
set_bit(PLATFORM_PROFILE_LOW_POWER, choices);
|
||||
|
@ -398,14 +412,13 @@ static int amd_pmf_profile_probe(void *drvdata, unsigned long *choices)
|
|||
|
||||
static const struct platform_profile_ops amd_pmf_profile_ops = {
|
||||
.probe = amd_pmf_profile_probe,
|
||||
.hidden_choices = amd_pmf_hidden_choices,
|
||||
.profile_get = amd_pmf_profile_get,
|
||||
.profile_set = amd_pmf_profile_set,
|
||||
};
|
||||
|
||||
int amd_pmf_init_sps(struct amd_pmf_dev *dev)
|
||||
{
|
||||
int err;
|
||||
|
||||
dev->current_profile = PLATFORM_PROFILE_BALANCED;
|
||||
|
||||
if (is_apmf_func_supported(dev, APMF_FUNC_STATIC_SLIDER_GRANULAR)) {
|
||||
|
@ -420,15 +433,12 @@ 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.ops = &amd_pmf_profile_ops;
|
||||
|
||||
/* Create platform_profile structure and register */
|
||||
err = devm_platform_profile_register(&dev->pprof, dev);
|
||||
if (err)
|
||||
dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %d\n",
|
||||
err);
|
||||
dev->ppdev = devm_platform_profile_register(dev->dev, "amd-pmf", dev,
|
||||
&amd_pmf_profile_ops);
|
||||
if (IS_ERR(dev->ppdev))
|
||||
dev_err(dev->dev, "Failed to register SPS support, this is most likely an SBIOS bug: %ld\n",
|
||||
PTR_ERR(dev->ppdev));
|
||||
|
||||
return err;
|
||||
return PTR_ERR_OR_ZERO(dev->ppdev);
|
||||
}
|
||||
|
|
|
@ -27,8 +27,11 @@ module_param(pb_side_load, bool, 0444);
|
|||
MODULE_PARM_DESC(pb_side_load, "Sideload policy binaries debug policy failures");
|
||||
#endif
|
||||
|
||||
static const uuid_t amd_pmf_ta_uuid = UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d,
|
||||
0xb1, 0x2d, 0xc5, 0x29, 0xb1, 0x3d, 0x85, 0x43);
|
||||
static const uuid_t amd_pmf_ta_uuid[] = { UUID_INIT(0xd9b39bf2, 0x66bd, 0x4154, 0xaf, 0xb8, 0x8a,
|
||||
0xcc, 0x2b, 0x2b, 0x60, 0xd6),
|
||||
UUID_INIT(0x6fd93b77, 0x3fb8, 0x524d, 0xb1, 0x2d, 0xc5,
|
||||
0x29, 0xb1, 0x3d, 0x85, 0x43),
|
||||
};
|
||||
|
||||
static const char *amd_pmf_uevent_as_str(unsigned int state)
|
||||
{
|
||||
|
@ -120,7 +123,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
|
|||
|
||||
case PMF_POLICY_STT_SKINTEMP_APU:
|
||||
if (dev->prev_data->stt_skintemp_apu != val) {
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false, val, NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_APU, false,
|
||||
fixp_q88_fromint(val), NULL);
|
||||
dev_dbg(dev->dev, "update STT_SKINTEMP_APU: %u\n", val);
|
||||
dev->prev_data->stt_skintemp_apu = val;
|
||||
}
|
||||
|
@ -128,7 +132,8 @@ static void amd_pmf_apply_policies(struct amd_pmf_dev *dev, struct ta_pmf_enact_
|
|||
|
||||
case PMF_POLICY_STT_SKINTEMP_HS2:
|
||||
if (dev->prev_data->stt_skintemp_hs2 != val) {
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false, val, NULL);
|
||||
amd_pmf_send_cmd(dev, SET_STT_LIMIT_HS2, false,
|
||||
fixp_q88_fromint(val), NULL);
|
||||
dev_dbg(dev->dev, "update STT_SKINTEMP_HS2: %u\n", val);
|
||||
dev->prev_data->stt_skintemp_hs2 = val;
|
||||
}
|
||||
|
@ -321,14 +326,19 @@ static int amd_pmf_start_policy_engine(struct amd_pmf_dev *dev)
|
|||
*/
|
||||
schedule_delayed_work(&dev->pb_work, msecs_to_jiffies(pb_actions_ms * 3));
|
||||
} else {
|
||||
dev_err(dev->dev, "ta invoke cmd init failed err: %x\n", res);
|
||||
dev_dbg(dev->dev, "ta invoke cmd init failed err: %x\n", res);
|
||||
dev->smart_pc_enabled = false;
|
||||
return -EIO;
|
||||
return res;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static inline bool amd_pmf_pb_valid(struct amd_pmf_dev *dev)
|
||||
{
|
||||
return memchr_inv(dev->policy_buf, 0xff, dev->policy_sz);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_AMD_PMF_DEBUG
|
||||
static void amd_pmf_hex_dump_pb(struct amd_pmf_dev *dev)
|
||||
{
|
||||
|
@ -348,14 +358,22 @@ static ssize_t amd_pmf_get_pb_data(struct file *filp, const char __user *buf,
|
|||
return -EINVAL;
|
||||
|
||||
/* re-alloc to the new buffer length of the policy binary */
|
||||
new_policy_buf = memdup_user(buf, length);
|
||||
if (IS_ERR(new_policy_buf))
|
||||
return PTR_ERR(new_policy_buf);
|
||||
new_policy_buf = devm_kzalloc(dev->dev, length, GFP_KERNEL);
|
||||
if (!new_policy_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
kfree(dev->policy_buf);
|
||||
if (copy_from_user(new_policy_buf, buf, length)) {
|
||||
devm_kfree(dev->dev, new_policy_buf);
|
||||
return -EFAULT;
|
||||
}
|
||||
|
||||
devm_kfree(dev->dev, dev->policy_buf);
|
||||
dev->policy_buf = new_policy_buf;
|
||||
dev->policy_sz = length;
|
||||
|
||||
if (!amd_pmf_pb_valid(dev))
|
||||
return -EINVAL;
|
||||
|
||||
amd_pmf_hex_dump_pb(dev);
|
||||
ret = amd_pmf_start_policy_engine(dev);
|
||||
if (ret < 0)
|
||||
|
@ -390,24 +408,24 @@ static int amd_pmf_amdtee_ta_match(struct tee_ioctl_version_data *ver, const voi
|
|||
return ver->impl_id == TEE_IMPL_ID_AMDTEE;
|
||||
}
|
||||
|
||||
static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id)
|
||||
static int amd_pmf_ta_open_session(struct tee_context *ctx, u32 *id, const uuid_t *uuid)
|
||||
{
|
||||
struct tee_ioctl_open_session_arg sess_arg = {};
|
||||
int rc;
|
||||
|
||||
export_uuid(sess_arg.uuid, &amd_pmf_ta_uuid);
|
||||
export_uuid(sess_arg.uuid, uuid);
|
||||
sess_arg.clnt_login = TEE_IOCTL_LOGIN_PUBLIC;
|
||||
sess_arg.num_params = 0;
|
||||
|
||||
rc = tee_client_open_session(ctx, &sess_arg, NULL);
|
||||
if (rc < 0 || sess_arg.ret != 0) {
|
||||
pr_err("Failed to open TEE session err:%#x, rc:%d\n", sess_arg.ret, rc);
|
||||
return rc;
|
||||
return rc ?: -EINVAL;
|
||||
}
|
||||
|
||||
*id = sess_arg.session;
|
||||
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmf_register_input_device(struct amd_pmf_dev *dev)
|
||||
|
@ -434,7 +452,7 @@ static int amd_pmf_register_input_device(struct amd_pmf_dev *dev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int amd_pmf_tee_init(struct amd_pmf_dev *dev)
|
||||
static int amd_pmf_tee_init(struct amd_pmf_dev *dev, const uuid_t *uuid)
|
||||
{
|
||||
u32 size;
|
||||
int ret;
|
||||
|
@ -442,10 +460,12 @@ static int amd_pmf_tee_init(struct amd_pmf_dev *dev)
|
|||
dev->tee_ctx = tee_client_open_context(NULL, amd_pmf_amdtee_ta_match, NULL, NULL);
|
||||
if (IS_ERR(dev->tee_ctx)) {
|
||||
dev_err(dev->dev, "Failed to open TEE context\n");
|
||||
return PTR_ERR(dev->tee_ctx);
|
||||
ret = PTR_ERR(dev->tee_ctx);
|
||||
dev->tee_ctx = NULL;
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id);
|
||||
ret = amd_pmf_ta_open_session(dev->tee_ctx, &dev->session_id, uuid);
|
||||
if (ret) {
|
||||
dev_err(dev->dev, "Failed to open TA session (%d)\n", ret);
|
||||
ret = -EINVAL;
|
||||
|
@ -482,14 +502,18 @@ out_ctx:
|
|||
|
||||
static void amd_pmf_tee_deinit(struct amd_pmf_dev *dev)
|
||||
{
|
||||
if (!dev->tee_ctx)
|
||||
return;
|
||||
tee_shm_free(dev->fw_shm_pool);
|
||||
tee_client_close_session(dev->tee_ctx, dev->session_id);
|
||||
tee_client_close_context(dev->tee_ctx);
|
||||
dev->tee_ctx = NULL;
|
||||
}
|
||||
|
||||
int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
|
||||
{
|
||||
int ret;
|
||||
bool status;
|
||||
int ret, i;
|
||||
|
||||
ret = apmf_check_smart_pc(dev);
|
||||
if (ret) {
|
||||
|
@ -502,52 +526,61 @@ int amd_pmf_init_smart_pc(struct amd_pmf_dev *dev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
ret = amd_pmf_tee_init(dev);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
INIT_DELAYED_WORK(&dev->pb_work, amd_pmf_invoke_cmd);
|
||||
|
||||
ret = amd_pmf_set_dram_addr(dev, true);
|
||||
if (ret)
|
||||
goto error;
|
||||
return ret;
|
||||
|
||||
dev->policy_base = devm_ioremap_resource(dev->dev, dev->res);
|
||||
if (IS_ERR(dev->policy_base)) {
|
||||
ret = PTR_ERR(dev->policy_base);
|
||||
goto error;
|
||||
}
|
||||
if (IS_ERR(dev->policy_base))
|
||||
return PTR_ERR(dev->policy_base);
|
||||
|
||||
dev->policy_buf = kzalloc(dev->policy_sz, GFP_KERNEL);
|
||||
if (!dev->policy_buf) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
}
|
||||
dev->policy_buf = devm_kzalloc(dev->dev, dev->policy_sz, GFP_KERNEL);
|
||||
if (!dev->policy_buf)
|
||||
return -ENOMEM;
|
||||
|
||||
memcpy_fromio(dev->policy_buf, dev->policy_base, dev->policy_sz);
|
||||
|
||||
amd_pmf_hex_dump_pb(dev);
|
||||
|
||||
dev->prev_data = kzalloc(sizeof(*dev->prev_data), GFP_KERNEL);
|
||||
if (!dev->prev_data) {
|
||||
ret = -ENOMEM;
|
||||
goto error;
|
||||
if (!amd_pmf_pb_valid(dev)) {
|
||||
dev_info(dev->dev, "No Smart PC policy present\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = amd_pmf_start_policy_engine(dev);
|
||||
if (ret)
|
||||
goto error;
|
||||
amd_pmf_hex_dump_pb(dev);
|
||||
|
||||
dev->prev_data = devm_kzalloc(dev->dev, sizeof(*dev->prev_data), GFP_KERNEL);
|
||||
if (!dev->prev_data)
|
||||
return -ENOMEM;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(amd_pmf_ta_uuid); i++) {
|
||||
ret = amd_pmf_tee_init(dev, &amd_pmf_ta_uuid[i]);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = amd_pmf_start_policy_engine(dev);
|
||||
dev_dbg(dev->dev, "start policy engine ret: %d\n", ret);
|
||||
status = ret == TA_PMF_TYPE_SUCCESS;
|
||||
if (status)
|
||||
break;
|
||||
amd_pmf_tee_deinit(dev);
|
||||
}
|
||||
|
||||
if (!status && !pb_side_load) {
|
||||
ret = -EINVAL;
|
||||
goto err;
|
||||
}
|
||||
|
||||
if (pb_side_load)
|
||||
amd_pmf_open_pb(dev, dev->dbgfs_dir);
|
||||
|
||||
ret = amd_pmf_register_input_device(dev);
|
||||
if (ret)
|
||||
goto error;
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
error:
|
||||
err:
|
||||
amd_pmf_deinit_smart_pc(dev);
|
||||
|
||||
return ret;
|
||||
|
@ -562,11 +595,5 @@ void amd_pmf_deinit_smart_pc(struct amd_pmf_dev *dev)
|
|||
amd_pmf_remove_pb(dev);
|
||||
|
||||
cancel_delayed_work_sync(&dev->pb_work);
|
||||
kfree(dev->prev_data);
|
||||
dev->prev_data = NULL;
|
||||
kfree(dev->policy_buf);
|
||||
dev->policy_buf = NULL;
|
||||
kfree(dev->buf);
|
||||
dev->buf = NULL;
|
||||
amd_pmf_tee_deinit(dev);
|
||||
}
|
||||
|
|
|
@ -166,7 +166,7 @@ static const struct key_entry hp_wmi_keymap[] = {
|
|||
|
||||
static struct input_dev *hp_wmi_input_dev;
|
||||
static struct platform_device *hp_wmi_platform_dev;
|
||||
static struct platform_profile_handler platform_profile_handler;
|
||||
static struct device *platform_profile_device;
|
||||
static bool platform_profile_support;
|
||||
|
||||
static struct rfkill *wifi_rfkill;
|
||||
|
@ -957,6 +957,7 @@ static const struct platform_profile_ops hp_wmi_platform_profile_ops = {
|
|||
|
||||
static int thermal_profile_setup(struct platform_device *device)
|
||||
{
|
||||
const struct platform_profile_ops *ops;
|
||||
int err, tp;
|
||||
|
||||
tp = thermal_profile_get();
|
||||
|
@ -971,13 +972,12 @@ static int thermal_profile_setup(struct platform_device *device)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
platform_profile_handler.name = "hp-wmi";
|
||||
platform_profile_handler.dev = &device->dev;
|
||||
platform_profile_handler.ops = &hp_wmi_platform_profile_ops;
|
||||
ops = &hp_wmi_platform_profile_ops;
|
||||
|
||||
err = platform_profile_register(&platform_profile_handler, NULL);
|
||||
if (err)
|
||||
return err;
|
||||
platform_profile_device = devm_platform_profile_register(&device->dev, "hp-wmi",
|
||||
NULL, ops);
|
||||
if (IS_ERR(platform_profile_device))
|
||||
return PTR_ERR(platform_profile_device);
|
||||
|
||||
platform_profile_support = true;
|
||||
|
||||
|
@ -1022,9 +1022,6 @@ static int __exit hp_wmi_bios_remove(struct platform_device *device)
|
|||
rfkill_destroy(wwan_rfkill);
|
||||
}
|
||||
|
||||
if (platform_profile_support)
|
||||
platform_profile_remove(&platform_profile_handler);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -109,7 +109,7 @@ enum {
|
|||
|
||||
struct ideapad_dytc_priv {
|
||||
enum platform_profile_option current_profile;
|
||||
struct platform_profile_handler pprof;
|
||||
struct device *ppdev; /* platform profile device */
|
||||
struct mutex mutex; /* protects the DYTC interface */
|
||||
struct ideapad_private *priv;
|
||||
};
|
||||
|
@ -872,7 +872,7 @@ static void dytc_profile_refresh(struct ideapad_private *priv)
|
|||
|
||||
if (profile != priv->dytc->current_profile) {
|
||||
priv->dytc->current_profile = profile;
|
||||
platform_profile_notify(&priv->dytc->pprof);
|
||||
platform_profile_notify(priv->dytc->ppdev);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -909,15 +909,16 @@ 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.ops = &dytc_profile_ops;
|
||||
|
||||
/* Create platform_profile structure and register */
|
||||
err = platform_profile_register(&priv->dytc->pprof, &priv->dytc);
|
||||
if (err)
|
||||
priv->dytc->ppdev = devm_platform_profile_register(&priv->platform_device->dev,
|
||||
"ideapad-laptop", priv->dytc,
|
||||
&dytc_profile_ops);
|
||||
if (IS_ERR(priv->dytc->ppdev)) {
|
||||
err = PTR_ERR(priv->dytc->ppdev);
|
||||
goto pp_reg_failed;
|
||||
}
|
||||
|
||||
/* Ensure initial values are correct */
|
||||
dytc_profile_refresh(priv);
|
||||
|
@ -937,7 +938,6 @@ static void ideapad_dytc_profile_exit(struct ideapad_private *priv)
|
|||
if (!priv->dytc)
|
||||
return;
|
||||
|
||||
platform_profile_remove(&priv->dytc->pprof);
|
||||
mutex_destroy(&priv->dytc->mutex);
|
||||
kfree(priv->dytc);
|
||||
|
||||
|
|
|
@ -932,6 +932,7 @@ static const struct proc_ops dispatch_proc_ops = {
|
|||
static struct platform_device *tpacpi_pdev;
|
||||
static struct platform_device *tpacpi_sensors_pdev;
|
||||
static struct device *tpacpi_hwmon;
|
||||
static struct device *tpacpi_pprof;
|
||||
static struct input_dev *tpacpi_inputdev;
|
||||
static struct mutex tpacpi_inputdev_send_mutex;
|
||||
static LIST_HEAD(tpacpi_all_drivers);
|
||||
|
@ -10296,11 +10297,6 @@ static const struct platform_profile_ops dytc_profile_ops = {
|
|||
.profile_set = dytc_profile_set,
|
||||
};
|
||||
|
||||
static struct platform_profile_handler dytc_profile = {
|
||||
.name = "thinkpad-acpi",
|
||||
.ops = &dytc_profile_ops,
|
||||
};
|
||||
|
||||
static void dytc_profile_refresh(void)
|
||||
{
|
||||
enum platform_profile_option profile;
|
||||
|
@ -10320,7 +10316,7 @@ static void dytc_profile_refresh(void)
|
|||
convert_dytc_to_profile(perfmode, &profile);
|
||||
if (profile != dytc_current_profile) {
|
||||
dytc_current_profile = profile;
|
||||
platform_profile_notify(&dytc_profile);
|
||||
platform_profile_notify(tpacpi_pprof);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -10360,15 +10356,15 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
|
|||
if (!err && ((output & DYTC_ERR_MASK) == DYTC_ERR_SUCCESS))
|
||||
dytc_mmc_get_available = true;
|
||||
}
|
||||
dytc_profile.dev = &tpacpi_pdev->dev;
|
||||
/* Create platform_profile structure and register */
|
||||
err = platform_profile_register(&dytc_profile, NULL);
|
||||
tpacpi_pprof = platform_profile_register(&tpacpi_pdev->dev, "thinkpad-acpi-profile",
|
||||
NULL, &dytc_profile_ops);
|
||||
/*
|
||||
* If for some reason platform_profiles aren't enabled
|
||||
* don't quit terminally.
|
||||
*/
|
||||
if (err)
|
||||
return 0;
|
||||
if (IS_ERR(tpacpi_pprof))
|
||||
return -ENODEV;
|
||||
|
||||
dytc_profile_available = true;
|
||||
/* Ensure initial values are correct */
|
||||
|
@ -10379,10 +10375,10 @@ static int tpacpi_dytc_profile_init(struct ibm_init_struct *iibm)
|
|||
|
||||
static void dytc_profile_exit(void)
|
||||
{
|
||||
if (dytc_profile_available) {
|
||||
if (!IS_ERR_OR_NULL(tpacpi_pprof))
|
||||
platform_profile_remove(tpacpi_pprof);
|
||||
if (dytc_profile_available)
|
||||
dytc_profile_available = false;
|
||||
platform_profile_remove(&dytc_profile);
|
||||
}
|
||||
}
|
||||
|
||||
static struct ibm_struct dytc_profile_driver_data = {
|
||||
|
|
|
@ -28,27 +28,33 @@ enum platform_profile_option {
|
|||
PLATFORM_PROFILE_LAST, /*must always be last */
|
||||
};
|
||||
|
||||
struct platform_profile_handler;
|
||||
|
||||
/**
|
||||
* struct platform_profile_ops - platform profile operations
|
||||
* @probe: Callback to setup choices available to the new class device. These
|
||||
* choices will only be enforced when setting a new profile, not when
|
||||
* getting the current one.
|
||||
* @hidden_choices: Callback to setup choices that are not visible to the user
|
||||
* but can be set by the driver.
|
||||
* @profile_get: Callback that will be called when showing the current platform
|
||||
* profile in sysfs.
|
||||
* @profile_set: Callback that will be called when storing a new platform
|
||||
* profile in sysfs.
|
||||
*/
|
||||
struct platform_profile_ops {
|
||||
int (*probe)(void *drvdata, unsigned long *choices);
|
||||
int (*hidden_choices)(void *drvdata, unsigned long *choices);
|
||||
int (*profile_get)(struct device *dev, enum platform_profile_option *profile);
|
||||
int (*profile_set)(struct device *dev, enum platform_profile_option profile);
|
||||
};
|
||||
|
||||
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)];
|
||||
const struct platform_profile_ops *ops;
|
||||
};
|
||||
|
||||
int platform_profile_register(struct platform_profile_handler *pprof, void *drvdata);
|
||||
int platform_profile_remove(struct platform_profile_handler *pprof);
|
||||
int devm_platform_profile_register(struct platform_profile_handler *pprof, void *drvdata);
|
||||
struct device *platform_profile_register(struct device *dev, const char *name,
|
||||
void *drvdata,
|
||||
const struct platform_profile_ops *ops);
|
||||
void platform_profile_remove(struct device *dev);
|
||||
struct device *devm_platform_profile_register(struct device *dev, const char *name,
|
||||
void *drvdata,
|
||||
const struct platform_profile_ops *ops);
|
||||
int platform_profile_cycle(void);
|
||||
void platform_profile_notify(struct platform_profile_handler *pprof);
|
||||
void platform_profile_notify(struct device *dev);
|
||||
|
||||
#endif /*_PLATFORM_PROFILE_H_*/
|
||||
|
|
Loading…
Reference in New Issue