2020-12-30 03:18:26 +03:00
// 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>
2021-01-25 14:59:56 +03:00
static struct platform_profile_handler * cur_profile ;
2020-12-30 03:18:26 +03:00
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 " ,
2021-02-11 23:17:02 +03:00
[ PLATFORM_PROFILE_BALANCED_PERFORMANCE ] = " balanced-performance " ,
2020-12-30 03:18:26 +03:00
[ 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 ;
}
2021-01-25 14:59:57 +03:00
err = cur_profile - > profile_get ( cur_profile , & profile ) ;
2020-12-30 03:18:26 +03:00
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 ;
}
2021-01-25 14:59:57 +03:00
err = cur_profile - > profile_set ( cur_profile , i ) ;
2020-12-30 03:18:26 +03:00
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 ) ;
2021-01-25 14:59:56 +03:00
int platform_profile_register ( struct platform_profile_handler * pprof )
2020-12-30 03:18:26 +03:00
{
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 ) ;
2021-01-25 22:09:09 +03:00
mutex_lock ( & profile_lock ) ;
2020-12-30 03:18:26 +03:00
cur_profile = NULL ;
mutex_unlock ( & profile_lock ) ;
return 0 ;
}
EXPORT_SYMBOL_GPL ( platform_profile_remove ) ;
MODULE_AUTHOR ( " Mark Pearson <markpearson@lenovo.com> " ) ;
MODULE_LICENSE ( " GPL " ) ;