2012-02-15 23:48:03 +04:00
/* Hwmon client for industrial I/O devices
*
* Copyright ( c ) 2011 Jonathan Cameron
*
* This program is free software ; you can redistribute it and / or modify it
* under the terms of the GNU General Public License version 2 as published by
* the Free Software Foundation .
*/
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/module.h>
# include <linux/err.h>
# include <linux/platform_device.h>
# include <linux/hwmon.h>
2013-03-20 19:52:00 +04:00
# include <linux/of.h>
2012-02-15 23:48:03 +04:00
# include <linux/hwmon-sysfs.h>
2012-04-25 18:54:58 +04:00
# include <linux/iio/consumer.h>
# include <linux/iio/types.h>
2012-02-15 23:48:03 +04:00
/**
* struct iio_hwmon_state - device instance state
* @ channels : filled with array of channels from iio
* @ num_channels : number of channels in channels ( saves counting twice )
* @ hwmon_dev : associated hwmon device
* @ attr_group : the group of attributes
* @ attrs : null terminated array of attribute pointers .
*/
struct iio_hwmon_state {
struct iio_channel * channels ;
int num_channels ;
struct device * hwmon_dev ;
struct attribute_group attr_group ;
2013-12-02 07:33:16 +04:00
const struct attribute_group * groups [ 2 ] ;
2012-02-15 23:48:03 +04:00
struct attribute * * attrs ;
} ;
/*
* Assumes that IIO and hwmon operate in the same base units .
* This is supposed to be true , but needs verification for
* new channel types .
*/
static ssize_t iio_hwmon_read_val ( struct device * dev ,
struct device_attribute * attr ,
char * buf )
{
2012-09-17 16:17:00 +04:00
int result ;
int ret ;
2012-02-15 23:48:03 +04:00
struct sensor_device_attribute * sattr = to_sensor_dev_attr ( attr ) ;
struct iio_hwmon_state * state = dev_get_drvdata ( dev ) ;
2012-09-17 16:17:00 +04:00
ret = iio_read_channel_processed ( & state - > channels [ sattr - > index ] ,
& result ) ;
2012-02-15 23:48:03 +04:00
if ( ret < 0 )
return ret ;
2012-09-17 16:17:00 +04:00
return sprintf ( buf , " %d \n " , result ) ;
2012-02-15 23:48:03 +04:00
}
2012-11-19 22:21:57 +04:00
static int iio_hwmon_probe ( struct platform_device * pdev )
2012-02-15 23:48:03 +04:00
{
2013-02-01 01:42:00 +04:00
struct device * dev = & pdev - > dev ;
2012-02-15 23:48:03 +04:00
struct iio_hwmon_state * st ;
struct sensor_device_attribute * a ;
int ret , i ;
2014-09-27 19:31:12 +04:00
int in_i = 1 , temp_i = 1 , curr_i = 1 , humidity_i = 1 ;
2012-02-15 23:48:03 +04:00
enum iio_chan_type type ;
2013-02-01 01:43:00 +04:00
struct iio_channel * channels ;
2013-12-02 07:33:16 +04:00
const char * name = " iio_hwmon " ;
2016-02-16 08:00:53 +03:00
char * sname ;
2013-12-02 07:33:16 +04:00
if ( dev - > of_node & & dev - > of_node - > name )
name = dev - > of_node - > name ;
2013-02-01 01:43:00 +04:00
channels = iio_channel_get_all ( dev ) ;
2016-09-08 17:28:35 +03:00
if ( IS_ERR ( channels ) ) {
if ( PTR_ERR ( channels ) = = - ENODEV )
return - EPROBE_DEFER ;
2013-02-01 01:43:00 +04:00
return PTR_ERR ( channels ) ;
2016-09-08 17:28:35 +03:00
}
2012-02-15 23:48:03 +04:00
2013-02-01 01:42:00 +04:00
st = devm_kzalloc ( dev , sizeof ( * st ) , GFP_KERNEL ) ;
2013-04-23 21:06:43 +04:00
if ( st = = NULL ) {
ret = - ENOMEM ;
goto error_release_channels ;
}
2012-02-15 23:48:03 +04:00
2013-02-01 01:43:00 +04:00
st - > channels = channels ;
2012-02-15 23:48:03 +04:00
/* count how many attributes we have */
while ( st - > channels [ st - > num_channels ] . indio_dev )
st - > num_channels + + ;
2013-02-01 01:42:00 +04:00
st - > attrs = devm_kzalloc ( dev ,
2013-12-02 07:33:16 +04:00
sizeof ( * st - > attrs ) * ( st - > num_channels + 1 ) ,
2013-02-01 01:42:00 +04:00
GFP_KERNEL ) ;
2012-02-15 23:48:03 +04:00
if ( st - > attrs = = NULL ) {
ret = - ENOMEM ;
goto error_release_channels ;
}
2013-02-01 01:42:00 +04:00
2012-02-15 23:48:03 +04:00
for ( i = 0 ; i < st - > num_channels ; i + + ) {
2013-02-01 01:42:00 +04:00
a = devm_kzalloc ( dev , sizeof ( * a ) , GFP_KERNEL ) ;
2012-02-15 23:48:03 +04:00
if ( a = = NULL ) {
ret = - ENOMEM ;
2013-02-01 01:42:00 +04:00
goto error_release_channels ;
2012-02-15 23:48:03 +04:00
}
sysfs_attr_init ( & a - > dev_attr . attr ) ;
2012-05-02 00:04:24 +04:00
ret = iio_get_channel_type ( & st - > channels [ i ] , & type ) ;
2013-02-01 01:42:00 +04:00
if ( ret < 0 )
goto error_release_channels ;
2012-02-15 23:48:03 +04:00
switch ( type ) {
case IIO_VOLTAGE :
2016-07-26 10:47:09 +03:00
a - > dev_attr . attr . name = devm_kasprintf ( dev , GFP_KERNEL ,
" in%d_input " ,
in_i + + ) ;
2012-02-15 23:48:03 +04:00
break ;
case IIO_TEMP :
2016-07-26 10:47:09 +03:00
a - > dev_attr . attr . name = devm_kasprintf ( dev , GFP_KERNEL ,
" temp%d_input " ,
temp_i + + ) ;
2012-02-15 23:48:03 +04:00
break ;
case IIO_CURRENT :
2016-07-26 10:47:09 +03:00
a - > dev_attr . attr . name = devm_kasprintf ( dev , GFP_KERNEL ,
" curr%d_input " ,
curr_i + + ) ;
2012-02-15 23:48:03 +04:00
break ;
2014-09-27 19:31:12 +04:00
case IIO_HUMIDITYRELATIVE :
2016-07-26 10:47:09 +03:00
a - > dev_attr . attr . name = devm_kasprintf ( dev , GFP_KERNEL ,
" humidity%d_input " ,
humidity_i + + ) ;
2014-09-27 19:31:12 +04:00
break ;
2012-02-15 23:48:03 +04:00
default :
ret = - EINVAL ;
2013-02-01 01:42:00 +04:00
goto error_release_channels ;
2012-02-15 23:48:03 +04:00
}
if ( a - > dev_attr . attr . name = = NULL ) {
ret = - ENOMEM ;
2013-02-01 01:42:00 +04:00
goto error_release_channels ;
2012-02-15 23:48:03 +04:00
}
a - > dev_attr . show = iio_hwmon_read_val ;
a - > dev_attr . attr . mode = S_IRUGO ;
a - > index = i ;
st - > attrs [ i ] = & a - > dev_attr . attr ;
}
2013-12-02 07:33:16 +04:00
st - > attr_group . attrs = st - > attrs ;
st - > groups [ 0 ] = & st - > attr_group ;
2016-02-16 08:00:53 +03:00
sname = devm_kstrdup ( dev , name , GFP_KERNEL ) ;
if ( ! sname ) {
ret = - ENOMEM ;
goto error_release_channels ;
}
strreplace ( sname , ' - ' , ' _ ' ) ;
st - > hwmon_dev = hwmon_device_register_with_groups ( dev , sname , st ,
2013-12-02 07:33:16 +04:00
st - > groups ) ;
2012-02-15 23:48:03 +04:00
if ( IS_ERR ( st - > hwmon_dev ) ) {
ret = PTR_ERR ( st - > hwmon_dev ) ;
2013-12-02 07:33:16 +04:00
goto error_release_channels ;
2012-02-15 23:48:03 +04:00
}
2013-12-02 07:33:16 +04:00
platform_set_drvdata ( pdev , st ) ;
2012-02-15 23:48:03 +04:00
return 0 ;
error_release_channels :
2013-05-13 17:37:26 +04:00
iio_channel_release_all ( channels ) ;
2012-02-15 23:48:03 +04:00
return ret ;
}
2012-11-19 22:26:37 +04:00
static int iio_hwmon_remove ( struct platform_device * pdev )
2012-02-15 23:48:03 +04:00
{
struct iio_hwmon_state * st = platform_get_drvdata ( pdev ) ;
hwmon_device_unregister ( st - > hwmon_dev ) ;
2012-05-02 00:04:24 +04:00
iio_channel_release_all ( st - > channels ) ;
2012-02-15 23:48:03 +04:00
return 0 ;
}
2014-05-07 12:29:02 +04:00
static const struct of_device_id iio_hwmon_of_match [ ] = {
2013-02-01 01:43:00 +04:00
{ . compatible = " iio-hwmon " , } ,
{ }
} ;
2013-06-10 19:47:45 +04:00
MODULE_DEVICE_TABLE ( of , iio_hwmon_of_match ) ;
2013-02-01 01:43:00 +04:00
2012-02-15 23:48:03 +04:00
static struct platform_driver __refdata iio_hwmon_driver = {
. driver = {
. name = " iio_hwmon " ,
2013-02-01 01:43:00 +04:00
. of_match_table = iio_hwmon_of_match ,
2012-02-15 23:48:03 +04:00
} ,
. probe = iio_hwmon_probe ,
2012-11-19 22:21:38 +04:00
. remove = iio_hwmon_remove ,
2012-02-15 23:48:03 +04:00
} ;
2012-07-21 12:54:00 +04:00
module_platform_driver ( iio_hwmon_driver ) ;
2012-02-15 23:48:03 +04:00
2012-09-03 00:34:59 +04:00
MODULE_AUTHOR ( " Jonathan Cameron <jic23@kernel.org> " ) ;
2012-02-15 23:48:03 +04:00
MODULE_DESCRIPTION ( " IIO to hwmon driver " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;