Commit d4fd25b6 authored by Linus Torvalds's avatar Linus Torvalds
Browse files
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
parents dd047efb 9a43102d
Loading
Loading
Loading
Loading
+73 −21
Original line number Diff line number Diff line
@@ -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",
@@ -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);
@@ -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;
}
@@ -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);
}

/**
@@ -373,7 +411,10 @@ 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;

@@ -381,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,
@@ -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);
@@ -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);

@@ -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 */
+11 −0
Original line number Diff line number Diff line
@@ -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:
@@ -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);
@@ -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,
};
+3 −0
Original line number Diff line number Diff line
@@ -33,6 +33,8 @@ 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
@@ -40,6 +42,7 @@ enum platform_profile_option {
 */
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);
};