2019-05-27 08:55:01 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2017-08-10 09:01:20 +05:30
/*
* PowerNV OPAL Sensor - groups interface
*
* Copyright 2017 IBM Corp .
*/
# define pr_fmt(fmt) "opal-sensor-groups: " fmt
# include <linux/of.h>
# include <linux/kobject.h>
# include <linux/slab.h>
# include <asm/opal.h>
2019-07-02 21:17:33 +08:00
static DEFINE_MUTEX ( sg_mutex ) ;
2017-08-10 09:01:20 +05:30
static struct kobject * sg_kobj ;
struct sg_attr {
u32 handle ;
struct kobj_attribute attr ;
} ;
static struct sensor_group {
char name [ 20 ] ;
struct attribute_group sg ;
struct sg_attr * sgattrs ;
} * sgs ;
2018-07-24 14:43:08 +05:30
int sensor_group_enable ( u32 handle , bool enable )
{
struct opal_msg msg ;
int token , ret ;
token = opal_async_get_token_interruptible ( ) ;
if ( token < 0 )
return token ;
ret = opal_sensor_group_enable ( handle , token , enable ) ;
if ( ret = = OPAL_ASYNC_COMPLETION ) {
ret = opal_async_wait_response ( token , & msg ) ;
if ( ret ) {
pr_devel ( " Failed to wait for the async response \n " ) ;
ret = - EIO ;
goto out ;
}
ret = opal_error_code ( opal_get_async_rc ( msg ) ) ;
} else {
ret = opal_error_code ( ret ) ;
}
out :
opal_async_release_token ( token ) ;
return ret ;
}
EXPORT_SYMBOL_GPL ( sensor_group_enable ) ;
2017-08-10 09:01:20 +05:30
static ssize_t sg_store ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count )
{
struct sg_attr * sattr = container_of ( attr , struct sg_attr , attr ) ;
struct opal_msg msg ;
u32 data ;
int ret , token ;
ret = kstrtoint ( buf , 0 , & data ) ;
if ( ret )
return ret ;
if ( data ! = 1 )
return - EINVAL ;
token = opal_async_get_token_interruptible ( ) ;
if ( token < 0 ) {
pr_devel ( " Failed to get token \n " ) ;
return token ;
}
ret = mutex_lock_interruptible ( & sg_mutex ) ;
if ( ret )
goto out_token ;
ret = opal_sensor_group_clear ( sattr - > handle , token ) ;
switch ( ret ) {
case OPAL_ASYNC_COMPLETION :
ret = opal_async_wait_response ( token , & msg ) ;
if ( ret ) {
pr_devel ( " Failed to wait for the async response \n " ) ;
ret = - EIO ;
goto out ;
}
ret = opal_error_code ( opal_get_async_rc ( msg ) ) ;
if ( ! ret )
ret = count ;
break ;
case OPAL_SUCCESS :
ret = count ;
break ;
default :
ret = opal_error_code ( ret ) ;
}
out :
mutex_unlock ( & sg_mutex ) ;
out_token :
opal_async_release_token ( token ) ;
return ret ;
}
static struct sg_ops_info {
int opal_no ;
const char * attr_name ;
ssize_t ( * store ) ( struct kobject * kobj , struct kobj_attribute * attr ,
const char * buf , size_t count ) ;
} ops_info [ ] = {
{ OPAL_SENSOR_GROUP_CLEAR , " clear " , sg_store } ,
} ;
static void add_attr ( int handle , struct sg_attr * attr , int index )
{
attr - > handle = handle ;
sysfs_attr_init ( & attr - > attr . attr ) ;
attr - > attr . attr . name = ops_info [ index ] . attr_name ;
attr - > attr . attr . mode = 0220 ;
attr - > attr . store = ops_info [ index ] . store ;
}
2021-12-16 17:00:26 -05:00
static int __init add_attr_group ( const __be32 * ops , int len , struct sensor_group * sg ,
2017-08-10 09:01:20 +05:30
u32 handle )
{
int i , j ;
int count = 0 ;
for ( i = 0 ; i < len ; i + + )
for ( j = 0 ; j < ARRAY_SIZE ( ops_info ) ; j + + )
if ( be32_to_cpu ( ops [ i ] ) = = ops_info [ j ] . opal_no ) {
add_attr ( handle , & sg - > sgattrs [ count ] , j ) ;
sg - > sg . attrs [ count ] =
& sg - > sgattrs [ count ] . attr . attr ;
count + + ;
}
return sysfs_create_group ( sg_kobj , & sg - > sg ) ;
}
2021-12-16 17:00:26 -05:00
static int __init get_nr_attrs ( const __be32 * ops , int len )
2017-08-10 09:01:20 +05:30
{
int i , j ;
int nr_attrs = 0 ;
for ( i = 0 ; i < len ; i + + )
for ( j = 0 ; j < ARRAY_SIZE ( ops_info ) ; j + + )
if ( be32_to_cpu ( ops [ i ] ) = = ops_info [ j ] . opal_no )
nr_attrs + + ;
return nr_attrs ;
}
void __init opal_sensor_groups_init ( void )
{
struct device_node * sg , * node ;
int i = 0 ;
sg = of_find_compatible_node ( NULL , NULL , " ibm,opal-sensor-group " ) ;
if ( ! sg ) {
pr_devel ( " Sensor groups node not found \n " ) ;
return ;
}
sgs = kcalloc ( of_get_child_count ( sg ) , sizeof ( * sgs ) , GFP_KERNEL ) ;
if ( ! sgs )
return ;
sg_kobj = kobject_create_and_add ( " sensor_groups " , opal_kobj ) ;
if ( ! sg_kobj ) {
pr_warn ( " Failed to create sensor group kobject \n " ) ;
goto out_sgs ;
}
for_each_child_of_node ( sg , node ) {
const __be32 * ops ;
u32 sgid , len , nr_attrs , chipid ;
ops = of_get_property ( node , " ops " , & len ) ;
if ( ! ops )
continue ;
nr_attrs = get_nr_attrs ( ops , len ) ;
if ( ! nr_attrs )
continue ;
2017-01-19 17:15:30 +01:00
sgs [ i ] . sgattrs = kcalloc ( nr_attrs , sizeof ( * sgs [ i ] . sgattrs ) ,
2017-08-10 09:01:20 +05:30
GFP_KERNEL ) ;
if ( ! sgs [ i ] . sgattrs )
goto out_sgs_sgattrs ;
sgs [ i ] . sg . attrs = kcalloc ( nr_attrs + 1 ,
2017-01-19 17:15:30 +01:00
sizeof ( * sgs [ i ] . sg . attrs ) ,
2017-08-10 09:01:20 +05:30
GFP_KERNEL ) ;
if ( ! sgs [ i ] . sg . attrs ) {
kfree ( sgs [ i ] . sgattrs ) ;
goto out_sgs_sgattrs ;
}
if ( of_property_read_u32 ( node , " sensor-group-id " , & sgid ) ) {
pr_warn ( " sensor-group-id property not found \n " ) ;
goto out_sgs_sgattrs ;
}
if ( ! of_property_read_u32 ( node , " ibm,chip-id " , & chipid ) )
2018-08-27 20:52:07 -05:00
sprintf ( sgs [ i ] . name , " %pOFn%d " , node , chipid ) ;
2017-08-10 09:01:20 +05:30
else
2018-08-27 20:52:07 -05:00
sprintf ( sgs [ i ] . name , " %pOFn " , node ) ;
2017-08-10 09:01:20 +05:30
sgs [ i ] . sg . name = sgs [ i ] . name ;
if ( add_attr_group ( ops , len , & sgs [ i ] , sgid ) ) {
pr_warn ( " Failed to create sensor attribute group %s \n " ,
sgs [ i ] . sg . name ) ;
goto out_sgs_sgattrs ;
}
i + + ;
}
return ;
out_sgs_sgattrs :
while ( - - i > = 0 ) {
kfree ( sgs [ i ] . sgattrs ) ;
kfree ( sgs [ i ] . sg . attrs ) ;
}
kobject_put ( sg_kobj ) ;
out_sgs :
kfree ( sgs ) ;
}