forked from Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
181 lines
4.1 KiB
181 lines
4.1 KiB
// SPDX-License-Identifier: GPL-2.0-or-later |
|
|
|
/* Platform profile sysfs interface */ |
|
|
|
#include <linux/acpi.h> |
|
#include <linux/bits.h> |
|
#include <linux/init.h> |
|
#include <linux/mutex.h> |
|
#include <linux/platform_profile.h> |
|
#include <linux/sysfs.h> |
|
|
|
static struct platform_profile_handler *cur_profile; |
|
static DEFINE_MUTEX(profile_lock); |
|
|
|
static const char * const profile_names[] = { |
|
[PLATFORM_PROFILE_LOW_POWER] = "low-power", |
|
[PLATFORM_PROFILE_COOL] = "cool", |
|
[PLATFORM_PROFILE_QUIET] = "quiet", |
|
[PLATFORM_PROFILE_BALANCED] = "balanced", |
|
[PLATFORM_PROFILE_BALANCED_PERFORMANCE] = "balanced-performance", |
|
[PLATFORM_PROFILE_PERFORMANCE] = "performance", |
|
}; |
|
static_assert(ARRAY_SIZE(profile_names) == PLATFORM_PROFILE_LAST); |
|
|
|
static ssize_t platform_profile_choices_show(struct device *dev, |
|
struct device_attribute *attr, |
|
char *buf) |
|
{ |
|
int len = 0; |
|
int err, i; |
|
|
|
err = mutex_lock_interruptible(&profile_lock); |
|
if (err) |
|
return err; |
|
|
|
if (!cur_profile) { |
|
mutex_unlock(&profile_lock); |
|
return -ENODEV; |
|
} |
|
|
|
for_each_set_bit(i, cur_profile->choices, PLATFORM_PROFILE_LAST) { |
|
if (len == 0) |
|
len += sysfs_emit_at(buf, len, "%s", profile_names[i]); |
|
else |
|
len += sysfs_emit_at(buf, len, " %s", profile_names[i]); |
|
} |
|
len += sysfs_emit_at(buf, len, "\n"); |
|
mutex_unlock(&profile_lock); |
|
return len; |
|
} |
|
|
|
static ssize_t platform_profile_show(struct device *dev, |
|
struct device_attribute *attr, |
|
char *buf) |
|
{ |
|
enum platform_profile_option profile = PLATFORM_PROFILE_BALANCED; |
|
int err; |
|
|
|
err = mutex_lock_interruptible(&profile_lock); |
|
if (err) |
|
return err; |
|
|
|
if (!cur_profile) { |
|
mutex_unlock(&profile_lock); |
|
return -ENODEV; |
|
} |
|
|
|
err = cur_profile->profile_get(cur_profile, &profile); |
|
mutex_unlock(&profile_lock); |
|
if (err) |
|
return err; |
|
|
|
/* Check that profile is valid index */ |
|
if (WARN_ON((profile < 0) || (profile >= ARRAY_SIZE(profile_names)))) |
|
return -EIO; |
|
|
|
return sysfs_emit(buf, "%s\n", profile_names[profile]); |
|
} |
|
|
|
static ssize_t platform_profile_store(struct device *dev, |
|
struct device_attribute *attr, |
|
const char *buf, size_t count) |
|
{ |
|
int err, i; |
|
|
|
err = mutex_lock_interruptible(&profile_lock); |
|
if (err) |
|
return err; |
|
|
|
if (!cur_profile) { |
|
mutex_unlock(&profile_lock); |
|
return -ENODEV; |
|
} |
|
|
|
/* Scan for a matching profile */ |
|
i = sysfs_match_string(profile_names, buf); |
|
if (i < 0) { |
|
mutex_unlock(&profile_lock); |
|
return -EINVAL; |
|
} |
|
|
|
/* Check that platform supports this profile choice */ |
|
if (!test_bit(i, cur_profile->choices)) { |
|
mutex_unlock(&profile_lock); |
|
return -EOPNOTSUPP; |
|
} |
|
|
|
err = cur_profile->profile_set(cur_profile, i); |
|
if (!err) |
|
sysfs_notify(acpi_kobj, NULL, "platform_profile"); |
|
|
|
mutex_unlock(&profile_lock); |
|
if (err) |
|
return err; |
|
return count; |
|
} |
|
|
|
static DEVICE_ATTR_RO(platform_profile_choices); |
|
static DEVICE_ATTR_RW(platform_profile); |
|
|
|
static struct attribute *platform_profile_attrs[] = { |
|
&dev_attr_platform_profile_choices.attr, |
|
&dev_attr_platform_profile.attr, |
|
NULL |
|
}; |
|
|
|
static const struct attribute_group platform_profile_group = { |
|
.attrs = platform_profile_attrs |
|
}; |
|
|
|
void platform_profile_notify(void) |
|
{ |
|
if (!cur_profile) |
|
return; |
|
sysfs_notify(acpi_kobj, NULL, "platform_profile"); |
|
} |
|
EXPORT_SYMBOL_GPL(platform_profile_notify); |
|
|
|
int platform_profile_register(struct platform_profile_handler *pprof) |
|
{ |
|
int err; |
|
|
|
mutex_lock(&profile_lock); |
|
/* We can only have one active profile */ |
|
if (cur_profile) { |
|
mutex_unlock(&profile_lock); |
|
return -EEXIST; |
|
} |
|
|
|
/* Sanity check the profile handler field are set */ |
|
if (!pprof || bitmap_empty(pprof->choices, PLATFORM_PROFILE_LAST) || |
|
!pprof->profile_set || !pprof->profile_get) { |
|
mutex_unlock(&profile_lock); |
|
return -EINVAL; |
|
} |
|
|
|
err = sysfs_create_group(acpi_kobj, &platform_profile_group); |
|
if (err) { |
|
mutex_unlock(&profile_lock); |
|
return err; |
|
} |
|
|
|
cur_profile = pprof; |
|
mutex_unlock(&profile_lock); |
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(platform_profile_register); |
|
|
|
int platform_profile_remove(void) |
|
{ |
|
sysfs_remove_group(acpi_kobj, &platform_profile_group); |
|
|
|
mutex_lock(&profile_lock); |
|
cur_profile = NULL; |
|
mutex_unlock(&profile_lock); |
|
return 0; |
|
} |
|
EXPORT_SYMBOL_GPL(platform_profile_remove); |
|
|
|
MODULE_AUTHOR("Mark Pearson <[email protected]>"); |
|
MODULE_LICENSE("GPL");
|
|
|