2008-01-23 02:00:02 +03:00
/*
2012-10-04 00:54:08 +04:00
* ads7828 . c - driver for TI ADS7828 8 - channel A / D converter and compatibles
2012-01-19 23:02:14 +04:00
* ( C ) 2007 EADS Astrium
*
* This driver is based on the lm75 and other lm_sensors / hwmon drivers
*
* Written by Steve Hardy < shardy @ redhat . com >
*
2012-10-04 00:54:08 +04:00
* ADS7830 support , by Guillaume Roguez < guillaume . roguez @ savoirfairelinux . com >
*
2012-10-04 00:54:07 +04:00
* For further information , see the Documentation / hwmon / ads7828 file .
2012-01-19 23:02:14 +04:00
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program ; if not , write to the Free Software
* Foundation , Inc . , 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*/
2008-01-23 02:00:02 +03:00
2012-10-04 00:54:07 +04:00
# include <linux/err.h>
2008-01-23 02:00:02 +03:00
# include <linux/hwmon.h>
# include <linux/hwmon-sysfs.h>
2012-10-04 00:54:07 +04:00
# include <linux/i2c.h>
# include <linux/init.h>
# include <linux/module.h>
2017-02-24 16:12:57 +03:00
# include <linux/of_device.h>
2012-10-04 00:54:07 +04:00
# include <linux/platform_data/ads7828.h>
2015-01-16 21:08:27 +03:00
# include <linux/regmap.h>
2012-10-04 00:54:07 +04:00
# include <linux/slab.h>
2017-03-30 06:46:37 +03:00
# include <linux/regulator/consumer.h>
2008-01-23 02:00:02 +03:00
/* The ADS7828 registers */
2012-10-04 00:54:07 +04:00
# define ADS7828_CMD_SD_SE 0x80 /* Single ended inputs */
# define ADS7828_CMD_PD1 0x04 /* Internal vref OFF && A/D ON */
# define ADS7828_CMD_PD3 0x0C /* Internal vref ON && A/D ON */
# define ADS7828_INT_VREF_MV 2500 /* Internal vref is 2.5V, 2500mV */
# define ADS7828_EXT_VREF_MV_MIN 50 /* External vref min value 0.05V */
# define ADS7828_EXT_VREF_MV_MAX 5250 /* External vref max value 5.25V */
2012-10-04 00:54:08 +04:00
/* List of supported devices */
enum ads7828_chips { ads7828 , ads7830 } ;
2012-10-04 00:54:07 +04:00
/* Client specific data */
2008-01-23 02:00:02 +03:00
struct ads7828_data {
2015-01-16 21:08:27 +03:00
struct regmap * regmap ;
2012-10-04 00:54:07 +04:00
u8 cmd_byte ; /* Command byte without channel bits */
unsigned int lsb_resol ; /* Resolution of the ADC sample LSB */
2008-01-23 02:00:02 +03:00
} ;
2012-10-04 00:54:07 +04:00
/* Command byte C2,C1,C0 - see datasheet */
static inline u8 ads7828_cmd_byte ( u8 cmd , int ch )
2008-01-23 02:00:02 +03:00
{
2012-10-04 00:54:07 +04:00
return cmd | ( ( ( ch > > 1 ) | ( ch & 0x01 ) < < 2 ) < < 4 ) ;
2008-01-23 02:00:02 +03:00
}
/* sysfs callback function */
2018-12-11 01:02:01 +03:00
static ssize_t ads7828_in_show ( struct device * dev ,
struct device_attribute * da , char * buf )
2008-01-23 02:00:02 +03:00
{
struct sensor_device_attribute * attr = to_sensor_dev_attr ( da ) ;
2015-01-16 21:08:27 +03:00
struct ads7828_data * data = dev_get_drvdata ( dev ) ;
u8 cmd = ads7828_cmd_byte ( data - > cmd_byte , attr - > index ) ;
unsigned int regval ;
int err ;
2008-01-23 02:00:02 +03:00
2015-01-16 21:08:27 +03:00
err = regmap_read ( data - > regmap , cmd , & regval ) ;
if ( err < 0 )
return err ;
return sprintf ( buf , " %d \n " ,
DIV_ROUND_CLOSEST ( regval * data - > lsb_resol , 1000 ) ) ;
2012-10-04 00:54:07 +04:00
}
2008-01-23 02:00:02 +03:00
2018-12-11 01:02:01 +03:00
static SENSOR_DEVICE_ATTR_RO ( in0_input , ads7828_in , 0 ) ;
static SENSOR_DEVICE_ATTR_RO ( in1_input , ads7828_in , 1 ) ;
static SENSOR_DEVICE_ATTR_RO ( in2_input , ads7828_in , 2 ) ;
static SENSOR_DEVICE_ATTR_RO ( in3_input , ads7828_in , 3 ) ;
static SENSOR_DEVICE_ATTR_RO ( in4_input , ads7828_in , 4 ) ;
static SENSOR_DEVICE_ATTR_RO ( in5_input , ads7828_in , 5 ) ;
static SENSOR_DEVICE_ATTR_RO ( in6_input , ads7828_in , 6 ) ;
static SENSOR_DEVICE_ATTR_RO ( in7_input , ads7828_in , 7 ) ;
2008-01-23 02:00:02 +03:00
2014-06-17 11:38:23 +04:00
static struct attribute * ads7828_attrs [ ] = {
2008-01-23 02:00:02 +03:00
& sensor_dev_attr_in0_input . dev_attr . attr ,
& sensor_dev_attr_in1_input . dev_attr . attr ,
& sensor_dev_attr_in2_input . dev_attr . attr ,
& sensor_dev_attr_in3_input . dev_attr . attr ,
& sensor_dev_attr_in4_input . dev_attr . attr ,
& sensor_dev_attr_in5_input . dev_attr . attr ,
& sensor_dev_attr_in6_input . dev_attr . attr ,
& sensor_dev_attr_in7_input . dev_attr . attr ,
NULL
} ;
2014-06-17 11:38:23 +04:00
ATTRIBUTE_GROUPS ( ads7828 ) ;
2008-01-23 02:00:02 +03:00
2015-01-16 21:08:27 +03:00
static const struct regmap_config ads2828_regmap_config = {
. reg_bits = 8 ,
. val_bits = 16 ,
} ;
static const struct regmap_config ads2830_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
} ;
2008-07-16 21:30:10 +04:00
static int ads7828_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
2014-06-17 11:38:23 +04:00
struct device * dev = & client - > dev ;
struct ads7828_platform_data * pdata = dev_get_platdata ( dev ) ;
2008-07-16 21:30:10 +04:00
struct ads7828_data * data ;
2014-06-17 11:38:23 +04:00
struct device * hwmon_dev ;
2015-01-16 21:12:50 +03:00
unsigned int vref_mv = ADS7828_INT_VREF_MV ;
2017-03-30 06:46:37 +03:00
unsigned int vref_uv ;
2015-01-16 21:12:50 +03:00
bool diff_input = false ;
bool ext_vref = false ;
2016-04-18 22:47:53 +03:00
unsigned int regval ;
2017-02-24 16:12:57 +03:00
enum ads7828_chips chip ;
2017-03-30 06:46:37 +03:00
struct regulator * reg ;
2008-01-23 02:00:02 +03:00
2014-06-17 11:38:23 +04:00
data = devm_kzalloc ( dev , sizeof ( struct ads7828_data ) , GFP_KERNEL ) ;
2012-06-02 20:58:00 +04:00
if ( ! data )
return - ENOMEM ;
2008-01-23 02:00:02 +03:00
2012-10-04 00:54:07 +04:00
if ( pdata ) {
2015-01-16 21:12:50 +03:00
diff_input = pdata - > diff_input ;
ext_vref = pdata - > ext_vref ;
if ( ext_vref & & pdata - > vref_mv )
vref_mv = pdata - > vref_mv ;
2017-03-30 06:46:37 +03:00
} else if ( dev - > of_node ) {
diff_input = of_property_read_bool ( dev - > of_node ,
" ti,differential-input " ) ;
reg = devm_regulator_get_optional ( dev , " vref " ) ;
if ( ! IS_ERR ( reg ) ) {
vref_uv = regulator_get_voltage ( reg ) ;
vref_mv = DIV_ROUND_CLOSEST ( vref_uv , 1000 ) ;
if ( vref_mv < ADS7828_EXT_VREF_MV_MIN | |
vref_mv > ADS7828_EXT_VREF_MV_MAX )
return - EINVAL ;
ext_vref = true ;
}
2012-10-04 00:54:07 +04:00
}
2017-02-24 16:12:57 +03:00
if ( client - > dev . of_node )
chip = ( enum ads7828_chips )
of_device_get_match_data ( & client - > dev ) ;
else
chip = id - > driver_data ;
2015-01-16 21:12:50 +03:00
/* Bound Vref with min/max values */
vref_mv = clamp_val ( vref_mv , ADS7828_EXT_VREF_MV_MIN ,
ADS7828_EXT_VREF_MV_MAX ) ;
2012-10-04 00:54:07 +04:00
2012-10-04 00:54:08 +04:00
/* ADS7828 uses 12-bit samples, while ADS7830 is 8-bit */
2017-02-24 16:12:57 +03:00
if ( chip = = ads7828 ) {
2015-01-16 21:12:50 +03:00
data - > lsb_resol = DIV_ROUND_CLOSEST ( vref_mv * 1000 , 4096 ) ;
2015-01-16 21:08:27 +03:00
data - > regmap = devm_regmap_init_i2c ( client ,
& ads2828_regmap_config ) ;
2012-10-04 00:54:08 +04:00
} else {
2015-01-16 21:12:50 +03:00
data - > lsb_resol = DIV_ROUND_CLOSEST ( vref_mv * 1000 , 256 ) ;
2015-01-16 21:08:27 +03:00
data - > regmap = devm_regmap_init_i2c ( client ,
& ads2830_regmap_config ) ;
2012-10-04 00:54:08 +04:00
}
2012-10-04 00:54:07 +04:00
2015-02-10 13:24:07 +03:00
if ( IS_ERR ( data - > regmap ) )
return PTR_ERR ( data - > regmap ) ;
2015-01-16 21:12:50 +03:00
data - > cmd_byte = ext_vref ? ADS7828_CMD_PD1 : ADS7828_CMD_PD3 ;
if ( ! diff_input )
2012-10-04 00:54:07 +04:00
data - > cmd_byte | = ADS7828_CMD_SD_SE ;
2016-04-18 22:47:53 +03:00
/*
* Datasheet specifies internal reference voltage is disabled by
* default . The internal reference voltage needs to be enabled and
* voltage needs to settle before getting valid ADC data . So perform a
* dummy read to enable the internal reference voltage .
*/
if ( ! ext_vref )
regmap_read ( data - > regmap , data - > cmd_byte , & regval ) ;
2014-06-17 11:38:23 +04:00
hwmon_dev = devm_hwmon_device_register_with_groups ( dev , client - > name ,
data ,
ads7828_groups ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
2008-01-23 02:00:02 +03:00
}
2012-10-04 00:54:07 +04:00
static const struct i2c_device_id ads7828_device_ids [ ] = {
2012-10-04 00:54:08 +04:00
{ " ads7828 " , ads7828 } ,
{ " ads7830 " , ads7830 } ,
2012-10-04 00:54:07 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , ads7828_device_ids ) ;
2008-01-23 02:00:02 +03:00
2017-02-24 16:12:57 +03:00
static const struct of_device_id ads7828_of_match [ ] = {
{
. compatible = " ti,ads7828 " ,
. data = ( void * ) ads7828
} ,
{
. compatible = " ti,ads7830 " ,
. data = ( void * ) ads7830
} ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , ads7828_of_match ) ;
2012-10-04 00:54:07 +04:00
static struct i2c_driver ads7828_driver = {
. driver = {
. name = " ads7828 " ,
2017-02-24 16:12:57 +03:00
. of_match_table = of_match_ptr ( ads7828_of_match ) ,
2012-10-04 00:54:07 +04:00
} ,
2008-01-23 02:00:02 +03:00
2012-10-04 00:54:07 +04:00
. id_table = ads7828_device_ids ,
. probe = ads7828_probe ,
} ;
2008-01-23 02:00:02 +03:00
2012-10-04 00:54:07 +04:00
module_i2c_driver ( ads7828_driver ) ;
2008-01-23 02:00:02 +03:00
MODULE_LICENSE ( " GPL " ) ;
2012-10-04 00:54:07 +04:00
MODULE_AUTHOR ( " Steve Hardy <shardy@redhat.com> " ) ;
2012-10-04 00:54:08 +04:00
MODULE_DESCRIPTION ( " Driver for TI ADS7828 A/D converter and compatibles " ) ;