Skip to content

Commit f2526ad

Browse files
committed
ACPI: platform_profile: Add support for hidden choices
JIRA: https://issues.redhat.com/browse/RHEL-89362 Conflicts: include/linux/platform_profile.h - the comments section was missing, so I added the entire text block commit 778b94d Author: Mario Limonciello <mario.limonciello@amd.com> Date: Fri Feb 28 11:01:53 2025 -0600 When two drivers don't support all the same profiles the legacy interface only exports the common profiles. This causes problems for cases where one driver uses low-power but another uses quiet because the result is that neither is exported to sysfs. To allow two drivers to disagree, add support for "hidden choices". Hidden choices are platform profiles that a driver supports to be compatible with the platform profile of another driver. Fixes: 6888347 ("ACPI: platform_profile: Allow multiple handlers") Reported-by: Antheas Kapenekakis <lkml@antheas.dev> Closes: https://lore.kernel.org/platform-driver-x86/e64b771e-3255-42ad-9257-5b8fc6c24ac9@gmx.de/T/#mc068042dd29df36c16c8af92664860fc4763974b Signed-off-by: Mario Limonciello <mario.limonciello@amd.com> Tested-by: Antheas Kapenekakis <lkml@antheas.dev> Tested-by: Derek J. Clark <derekjohn.clark@gmail.com> Acked-by: Ilpo Järvinen <ilpo.jarvinen@linux.intel.com> Link: https://patch.msgid.link/20250228170155.2623386-2-superm1@kernel.org Signed-off-by: Rafael J. Wysocki <rafael.j.wysocki@intel.com> Signed-off-by: Mark Langsdorf <mlangsdo@redhat.com>
1 parent 2f89b6d commit f2526ad

File tree

2 files changed

+86
-21
lines changed

2 files changed

+86
-21
lines changed

drivers/acpi/platform_profile.c

Lines changed: 73 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -20,9 +20,15 @@ struct platform_profile_handler {
2020
struct device class_dev;
2121
int minor;
2222
unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
23+
unsigned long hidden_choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
2324
const struct platform_profile_ops *ops;
2425
};
2526

27+
struct aggregate_choices_data {
28+
unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
29+
int count;
30+
};
31+
2632
static const char * const profile_names[] = {
2733
[PLATFORM_PROFILE_LOW_POWER] = "low-power",
2834
[PLATFORM_PROFILE_COOL] = "cool",
@@ -72,7 +78,7 @@ static int _store_class_profile(struct device *dev, void *data)
7278

7379
lockdep_assert_held(&profile_lock);
7480
handler = to_pprof_handler(dev);
75-
if (!test_bit(*bit, handler->choices))
81+
if (!test_bit(*bit, handler->choices) && !test_bit(*bit, handler->hidden_choices))
7682
return -EOPNOTSUPP;
7783

7884
return handler->ops->profile_set(dev, *bit);
@@ -238,21 +244,44 @@ static const struct class platform_profile_class = {
238244
/**
239245
* _aggregate_choices - Aggregate the available profile choices
240246
* @dev: The device
241-
* @data: The available profile choices
247+
* @arg: struct aggregate_choices_data
242248
*
243249
* Return: 0 on success, -errno on failure
244250
*/
245-
static int _aggregate_choices(struct device *dev, void *data)
251+
static int _aggregate_choices(struct device *dev, void *arg)
246252
{
253+
unsigned long tmp[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
254+
struct aggregate_choices_data *data = arg;
247255
struct platform_profile_handler *handler;
248-
unsigned long *aggregate = data;
249256

250257
lockdep_assert_held(&profile_lock);
251258
handler = to_pprof_handler(dev);
252-
if (test_bit(PLATFORM_PROFILE_LAST, aggregate))
253-
bitmap_copy(aggregate, handler->choices, PLATFORM_PROFILE_LAST);
259+
bitmap_or(tmp, handler->choices, handler->hidden_choices, PLATFORM_PROFILE_LAST);
260+
if (test_bit(PLATFORM_PROFILE_LAST, data->aggregate))
261+
bitmap_copy(data->aggregate, tmp, PLATFORM_PROFILE_LAST);
254262
else
255-
bitmap_and(aggregate, handler->choices, aggregate, PLATFORM_PROFILE_LAST);
263+
bitmap_and(data->aggregate, tmp, data->aggregate, PLATFORM_PROFILE_LAST);
264+
data->count++;
265+
266+
return 0;
267+
}
268+
269+
/**
270+
* _remove_hidden_choices - Remove hidden choices from aggregate data
271+
* @dev: The device
272+
* @arg: struct aggregate_choices_data
273+
*
274+
* Return: 0 on success, -errno on failure
275+
*/
276+
static int _remove_hidden_choices(struct device *dev, void *arg)
277+
{
278+
struct aggregate_choices_data *data = arg;
279+
struct platform_profile_handler *handler;
280+
281+
lockdep_assert_held(&profile_lock);
282+
handler = to_pprof_handler(dev);
283+
bitmap_andnot(data->aggregate, handler->choices,
284+
handler->hidden_choices, PLATFORM_PROFILE_LAST);
256285

257286
return 0;
258287
}
@@ -269,22 +298,31 @@ static ssize_t platform_profile_choices_show(struct device *dev,
269298
struct device_attribute *attr,
270299
char *buf)
271300
{
272-
unsigned long aggregate[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
301+
struct aggregate_choices_data data = {
302+
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
303+
.count = 0,
304+
};
273305
int err;
274306

275-
set_bit(PLATFORM_PROFILE_LAST, aggregate);
307+
set_bit(PLATFORM_PROFILE_LAST, data.aggregate);
276308
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
277309
err = class_for_each_device(&platform_profile_class, NULL,
278-
aggregate, _aggregate_choices);
310+
&data, _aggregate_choices);
279311
if (err)
280312
return err;
313+
if (data.count == 1) {
314+
err = class_for_each_device(&platform_profile_class, NULL,
315+
&data, _remove_hidden_choices);
316+
if (err)
317+
return err;
318+
}
281319
}
282320

283321
/* no profile handler registered any more */
284-
if (bitmap_empty(aggregate, PLATFORM_PROFILE_LAST))
322+
if (bitmap_empty(data.aggregate, PLATFORM_PROFILE_LAST))
285323
return -EINVAL;
286324

287-
return _commmon_choices_show(aggregate, buf);
325+
return _commmon_choices_show(data.aggregate, buf);
288326
}
289327

290328
/**
@@ -372,21 +410,24 @@ static ssize_t platform_profile_store(struct device *dev,
372410
struct device_attribute *attr,
373411
const char *buf, size_t count)
374412
{
375-
unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
413+
struct aggregate_choices_data data = {
414+
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
415+
.count = 0,
416+
};
376417
int ret;
377418
int i;
378419

379420
/* Scan for a matching profile */
380421
i = sysfs_match_string(profile_names, buf);
381422
if (i < 0 || i == PLATFORM_PROFILE_CUSTOM)
382423
return -EINVAL;
383-
set_bit(PLATFORM_PROFILE_LAST, choices);
424+
set_bit(PLATFORM_PROFILE_LAST, data.aggregate);
384425
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
385426
ret = class_for_each_device(&platform_profile_class, NULL,
386-
choices, _aggregate_choices);
427+
&data, _aggregate_choices);
387428
if (ret)
388429
return ret;
389-
if (!test_bit(i, choices))
430+
if (!test_bit(i, data.aggregate))
390431
return -EOPNOTSUPP;
391432

392433
ret = class_for_each_device(&platform_profile_class, NULL, &i,
@@ -437,12 +478,15 @@ EXPORT_SYMBOL_GPL(platform_profile_notify);
437478

438479
int platform_profile_cycle(void)
439480
{
481+
struct aggregate_choices_data data = {
482+
.aggregate = { [0 ... BITS_TO_LONGS(PLATFORM_PROFILE_LAST) - 1] = ~0UL },
483+
.count = 0,
484+
};
440485
enum platform_profile_option next = PLATFORM_PROFILE_LAST;
441486
enum platform_profile_option profile = PLATFORM_PROFILE_LAST;
442-
unsigned long choices[BITS_TO_LONGS(PLATFORM_PROFILE_LAST)];
443487
int err;
444488

445-
set_bit(PLATFORM_PROFILE_LAST, choices);
489+
set_bit(PLATFORM_PROFILE_LAST, data.aggregate);
446490
scoped_cond_guard(mutex_intr, return -ERESTARTSYS, &profile_lock) {
447491
err = class_for_each_device(&platform_profile_class, NULL,
448492
&profile, _aggregate_profiles);
@@ -454,14 +498,14 @@ int platform_profile_cycle(void)
454498
return -EINVAL;
455499

456500
err = class_for_each_device(&platform_profile_class, NULL,
457-
choices, _aggregate_choices);
501+
&data, _aggregate_choices);
458502
if (err)
459503
return err;
460504

461505
/* never iterate into a custom if all drivers supported it */
462-
clear_bit(PLATFORM_PROFILE_CUSTOM, choices);
506+
clear_bit(PLATFORM_PROFILE_CUSTOM, data.aggregate);
463507

464-
next = find_next_bit_wrap(choices,
508+
next = find_next_bit_wrap(data.aggregate,
465509
PLATFORM_PROFILE_LAST,
466510
profile + 1);
467511

@@ -507,6 +551,14 @@ struct device *platform_profile_register(struct device *dev, const char *name,
507551
return ERR_PTR(-EINVAL);
508552
}
509553

554+
if (ops->hidden_choices) {
555+
err = ops->hidden_choices(drvdata, pprof->hidden_choices);
556+
if (err) {
557+
dev_err(dev, "platform_profile hidden_choices failed\n");
558+
return ERR_PTR(err);
559+
}
560+
}
561+
510562
guard(mutex)(&profile_lock);
511563

512564
/* create class interface for individual handler */

include/linux/platform_profile.h

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -28,8 +28,21 @@ enum platform_profile_option {
2828
PLATFORM_PROFILE_LAST, /*must always be last */
2929
};
3030

31+
/**
32+
* struct platform_profile_ops - platform profile operations
33+
* @probe: Callback to setup choices available to the new class device. These
34+
* choices will only be enforced when setting a new profile, not when
35+
* getting the current one.
36+
* @hidden_choices: Callback to setup choices that are not visible to the user
37+
* but can be set by the driver.
38+
* @profile_get: Callback that will be called when showing the current platform
39+
* profile in sysfs.
40+
* @profile_set: Callback that will be called when storing a new platform
41+
* profile in sysfs.
42+
*/
3143
struct platform_profile_ops {
3244
int (*probe)(void *drvdata, unsigned long *choices);
45+
int (*hidden_choices)(void *drvdata, unsigned long *choices);
3346
int (*profile_get)(struct device *dev, enum platform_profile_option *profile);
3447
int (*profile_set)(struct device *dev, enum platform_profile_option profile);
3548
};

0 commit comments

Comments
 (0)