brintos

brintos / linux-shallow public Read only

0
0
Text · 5.0 KiB · d2f7fd7 Raw
222 lines · c
1// SPDX-License-Identifier: GPL-2.0-or-later2 3/* Platform profile sysfs interface */4 5#include <linux/acpi.h>6#include <linux/bits.h>7#include <linux/init.h>8#include <linux/mutex.h>9#include <linux/platform_profile.h>10#include <linux/sysfs.h>11 12static struct platform_profile_handler *cur_profile;13static DEFINE_MUTEX(profile_lock);14 15static const char * const profile_names[] = {16	[PLATFORM_PROFILE_LOW_POWER] = "low-power",17	[PLATFORM_PROFILE_COOL] = "cool",18	[PLATFORM_PROFILE_QUIET] = "quiet",19	[PLATFORM_PROFILE_BALANCED] = "balanced",20	[PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance",21	[PLATFORM_PROFILE_PERFORMANCE] = "performance",22};23static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST);24 25static ssize_t platform_profile_choices_show(struct device *dev,26					struct device_attribute *attr,27					char *buf)28{29	int len = 0;30	int err, i;31 32	err = mutex_lock_interruptible(&profile_lock);33	if (err)34		return err;35 36	if (!cur_profile) {37		mutex_unlock(&profile_lock);38		return -ENODEV;39	}40 41	for_each_set_bit(i, cur_profile->choices, PLATFORM_PROFILE_LAST) {42		if (len == 0)43			len += sysfs_emit_at(buf, len, "%s", profile_names[i]);44		else45			len += sysfs_emit_at(buf, len, " %s", profile_names[i]);46	}47	len += sysfs_emit_at(buf, len, "\n");48	mutex_unlock(&profile_lock);49	return len;50}51 52static ssize_t platform_profile_show(struct device *dev,53					struct device_attribute *attr,54					char *buf)55{56	enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED;57	int err;58 59	err = mutex_lock_interruptible(&profile_lock);60	if (err)61		return err;62 63	if (!cur_profile) {64		mutex_unlock(&profile_lock);65		return -ENODEV;66	}67 68	err = cur_profile->profile_get(cur_profile, &profile);69	mutex_unlock(&profile_lock);70	if (err)71		return err;72 73	/* Check that profile is valid index */74	if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names))))75		return -EIO;76 77	return sysfs_emit(buf, "%s\n", profile_names[profile]);78}79 80static ssize_t platform_profile_store(struct device *dev,81			    struct device_attribute *attr,82			    const char *buf, size_t count)83{84	int err, i;85 86	err = mutex_lock_interruptible(&profile_lock);87	if (err)88		return err;89 90	if (!cur_profile) {91		mutex_unlock(&profile_lock);92		return -ENODEV;93	}94 95	/* Scan for a matching profile */96	i = sysfs_match_string(profile_names, buf);97	if (i < 0) {98		mutex_unlock(&profile_lock);99		return -EINVAL;100	}101 102	/* Check that platform supports this profile choice */103	if (!test_bit(i, cur_profile->choices)) {104		mutex_unlock(&profile_lock);105		return -EOPNOTSUPP;106	}107 108	err = cur_profile->profile_set(cur_profile, i);109	if (!err)110		sysfs_notify(acpi_kobj, NULL, "platform_profile");111 112	mutex_unlock(&profile_lock);113	if (err)114		return err;115	return count;116}117 118static DEVICE_ATTR_RO(platform_profile_choices);119static DEVICE_ATTR_RW(platform_profile);120 121static struct attribute *platform_profile_attrs[] = {122	&dev_attr_platform_profile_choices.attr,123	&dev_attr_platform_profile.attr,124	NULL125};126 127static const struct attribute_group platform_profile_group = {128	.attrs = platform_profile_attrs129};130 131void platform_profile_notify(void)132{133	if (!cur_profile)134		return;135	sysfs_notify(acpi_kobj, NULL, "platform_profile");136}137EXPORT_SYMBOL_GPL(platform_profile_notify);138 139int platform_profile_cycle(void)140{141	enum platform_profile_option profile;142	enum platform_profile_option next;143	int err;144 145	err = mutex_lock_interruptible(&profile_lock);146	if (err)147		return err;148 149	if (!cur_profile) {150		mutex_unlock(&profile_lock);151		return -ENODEV;152	}153 154	err = cur_profile->profile_get(cur_profile, &profile);155	if (err) {156		mutex_unlock(&profile_lock);157		return err;158	}159 160	next = find_next_bit_wrap(cur_profile->choices, PLATFORM_PROFILE_LAST,161				  profile + 1);162 163	if (WARN_ON(next == PLATFORM_PROFILE_LAST)) {164		mutex_unlock(&profile_lock);165		return -EINVAL;166	}167 168	err = cur_profile->profile_set(cur_profile, next);169	mutex_unlock(&profile_lock);170 171	if (!err)172		sysfs_notify(acpi_kobj, NULL, "platform_profile");173 174	return err;175}176EXPORT_SYMBOL_GPL(platform_profile_cycle);177 178int platform_profile_register(struct platform_profile_handler *pprof)179{180	int err;181 182	mutex_lock(&profile_lock);183	/* We can only have one active profile */184	if (cur_profile) {185		mutex_unlock(&profile_lock);186		return -EEXIST;187	}188 189	/* Sanity check the profile handler field are set */190	if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) ||191		!pprof->profile_set || !pprof->profile_get) {192		mutex_unlock(&profile_lock);193		return -EINVAL;194	}195 196	err = sysfs_create_group(acpi_kobj, &platform_profile_group);197	if (err) {198		mutex_unlock(&profile_lock);199		return err;200	}201 202	cur_profile = pprof;203	mutex_unlock(&profile_lock);204	return 0;205}206EXPORT_SYMBOL_GPL(platform_profile_register);207 208int platform_profile_remove(void)209{210	sysfs_remove_group(acpi_kobj, &platform_profile_group);211 212	mutex_lock(&profile_lock);213	cur_profile = NULL;214	mutex_unlock(&profile_lock);215	return 0;216}217EXPORT_SYMBOL_GPL(platform_profile_remove);218 219MODULE_AUTHOR("Mark Pearson <markpearson@lenovo.com>");220MODULE_DESCRIPTION("ACPI platform profile sysfs interface");221MODULE_LICENSE("GPL");222