Skip to content

Commit

Permalink
ACPI: platform_profile: Make sure all profile handlers agree on profile
Browse files Browse the repository at this point in the history
If for any reason multiple profile handlers don't agree on the profile
return the custom profile.

Reviewed-by: Armin Wolf <W_Armin@gmx.de>
Tested-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: Mark Pearson <mpearson-lenovo@squebb.ca>
Reviewed-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
Signed-off-by: Mario Limonciello <mario.limonciello@amd.com>
Link: https://lore.kernel.org/r/20241206031918.1537-18-mario.limonciello@amd.com
Signed-off-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com>
  • Loading branch information
Mario Limonciello authored and Ilpo Järvinen committed Dec 10, 2024
1 parent 494637c commit e836b7d
Showing 1 changed file with 102 additions and 23 deletions.
125 changes: 102 additions & 23 deletions drivers/acpi/platform_profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,24 @@ static int _store_class_profile(struct device *dev, void *data)
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
Expand Down Expand Up @@ -251,51 +269,112 @@ static ssize_t platform_profile_choices_show(struct device *dev,
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 (*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)
struct device_attribute *attr,
char *buf)
{
enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;
enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
int err;

scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
if (!cur_profile)
return -ENODEV;

err = cur_profile->profile_get(cur_profile, &profile);
err = class_for_each_device(&platform_profile_class, NULL,
&profile, _aggregate_profiles);
if (err)
return err;
}

/* Check that profile is valid index */
if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))
return -EIO;
/* 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)
struct device_attribute *attr,
const char *buf, size_t count)
{
int err, i;
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)
if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
return -EINVAL;

set_bit(PLATFORM_PROFILE_LAST, choices);
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
if (!cur_profile)
return -ENODEV;

/* Check that platform supports this profile choice */
if (!test_bit(i, cur_profile->choices))
ret = class_for_each_device(&platform_profile_class, NULL,
choices, _aggregate_choices);
if (ret)
return ret;
if (!test_bit(i, choices))
return -EOPNOTSUPP;

err = cur_profile->profile_set(cur_profile, i);
if (err)
return err;
ret = class_for_each_device(&platform_profile_class, NULL, &i,
_store_and_notify);
if (ret)
return ret;
}

sysfs_notify(acpi_kobj, NULL, "platform_profile");
Expand Down

0 comments on commit e836b7d

Please sign in to comment.