Skip to content

Commit

Permalink
Merge tag 'acpi-6.14-rc6' of git://git.kernel.org/pub/scm/linux/kerne…
Browse files Browse the repository at this point in the history
…l/git/rafael/linux-pm

Pull ACPI fix from Rafael Wysocki:
 "Restore the previous behavior of the ACPI platform_profile sysfs
  interface that has been changed recently in a way incompatible with
  the existing user space (Mario Limonciello)"

* tag 'acpi-6.14-rc6' of git://git.kernel.org/pub/scm/linux/kernel/git/rafael/linux-pm:
  platform/x86/amd: pmf: Add balanced-performance to hidden choices
  platform/x86/amd: pmf: Add 'quiet' to hidden choices
  ACPI: platform_profile: Add support for hidden choices
  • Loading branch information
Linus Torvalds committed Mar 7, 2025
2 parents dd047ef + 9a43102 commit d4fd25b
Show file tree
Hide file tree
Showing 3 changed files with 87 additions and 21 deletions.
94 changes: 73 additions & 21 deletions drivers/acpi/platform_profile.c
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,15 @@ struct platform_profile_handler {
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",
Expand Down Expand Up @@ -73,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);
Expand Down Expand Up @@ -239,21 +245,44 @@ static const struct class platform_profile_class = {
/**
* _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;
}
Expand All @@ -270,22 +299,31 @@ 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)];
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);
}

/**
Expand Down Expand Up @@ -373,21 +411,24 @@ 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)];
struct aggregate_choices_data data = {
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
.count = 0,
};
int ret;
int i;

/* Scan for a matching profile */
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,
Expand Down Expand Up @@ -453,12 +494,15 @@ EXPORT_SYMBOL_GPL(platform_profile_notify);
*/
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);
Expand All @@ -470,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);

Expand Down Expand Up @@ -532,6 +576,14 @@ struct device *platform_profile_register(struct device *dev, const char *name,
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 */
Expand Down
11 changes: 11 additions & 0 deletions drivers/platform/x86/amd/pmf/sps.c
Original file line number Diff line number Diff line change
Expand Up @@ -297,12 +297,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:
Expand Down Expand Up @@ -387,6 +389,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);
Expand All @@ -398,6 +408,7 @@ 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,
};
Expand Down
3 changes: 3 additions & 0 deletions include/linux/platform_profile.h
Original file line number Diff line number Diff line change
Expand Up @@ -33,13 +33,16 @@ enum platform_profile_option {
* @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);
};
Expand Down

0 comments on commit d4fd25b

Please sign in to comment.