2012-03-23 13:02:20 +04:00
/*
2012-09-21 15:04:22 +04:00
* mcp3021 . c - driver for Microchip MCP3021 and MCP3221
2012-03-23 13:02:20 +04:00
*
* Copyright ( C ) 2008 - 2009 , 2012 Freescale Semiconductor , Inc .
* Author : Mingkai Hu < Mingkai . hu @ freescale . com >
2012-09-21 15:04:21 +04:00
* Reworked by Sven Schuchmann < schuchmann @ schleissheimer . de >
2016-11-10 00:22:34 +03:00
* DT support added by Clemens Gruber < clemens . gruber @ pqgruber . com >
2012-03-23 13:02:20 +04:00
*
* This driver export the value of analog input voltage to sysfs , the
* voltage unit is mV . Through the sysfs interface , lm - sensors tool
* can also display the input voltage .
*
* 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 .
*/
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/hwmon.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/err.h>
# include <linux/device.h>
2016-11-10 00:22:34 +03:00
# include <linux/of.h>
# include <linux/of_device.h>
2012-03-23 13:02:20 +04:00
2016-11-10 00:22:34 +03:00
/* Vdd / reference voltage in millivolt */
# define MCP3021_VDD_REF_MAX 5500
# define MCP3021_VDD_REF_MIN 2700
# define MCP3021_VDD_REF_DEFAULT 3300
2012-03-23 13:02:20 +04:00
/* output format */
# define MCP3021_SAR_SHIFT 2
# define MCP3021_SAR_MASK 0x3ff
# define MCP3021_OUTPUT_RES 10 /* 10-bit resolution */
2012-09-21 15:04:22 +04:00
# define MCP3221_SAR_SHIFT 0
# define MCP3221_SAR_MASK 0xfff
# define MCP3221_OUTPUT_RES 12 /* 12-bit resolution */
2012-09-21 15:04:21 +04:00
enum chips {
2012-09-21 15:04:22 +04:00
mcp3021 ,
mcp3221
2012-09-21 15:04:21 +04:00
} ;
2012-09-21 15:04:22 +04:00
2012-03-23 13:02:20 +04:00
/*
* Client data ( each client gets its own )
*/
struct mcp3021_data {
struct device * hwmon_dev ;
2016-11-10 00:22:34 +03:00
u32 vdd ; /* supply and reference voltage in millivolt */
2012-09-21 15:04:21 +04:00
u16 sar_shift ;
u16 sar_mask ;
u8 output_res ;
2012-03-23 13:02:20 +04:00
} ;
static int mcp3021_read16 ( struct i2c_client * client )
{
2012-09-21 15:04:21 +04:00
struct mcp3021_data * data = i2c_get_clientdata ( client ) ;
2012-03-23 13:02:20 +04:00
int ret ;
u16 reg ;
__be16 buf ;
ret = i2c_master_recv ( client , ( char * ) & buf , 2 ) ;
if ( ret < 0 )
return ret ;
if ( ret ! = 2 )
return - EIO ;
/* The output code of the MCP3021 is transmitted with MSB first. */
reg = be16_to_cpu ( buf ) ;
/*
* The ten - bit output code is composed of the lower 4 - bit of the
* first byte and the upper 6 - bit of the second byte .
*/
2012-09-21 15:04:21 +04:00
reg = ( reg > > data - > sar_shift ) & data - > sar_mask ;
2012-03-23 13:02:20 +04:00
return reg ;
}
2012-09-21 15:04:21 +04:00
static inline u16 volts_from_reg ( struct mcp3021_data * data , u16 val )
2012-03-23 13:02:20 +04:00
{
2015-07-01 19:07:41 +03:00
return DIV_ROUND_CLOSEST ( data - > vdd * val , 1 < < data - > output_res ) ;
2012-03-23 13:02:20 +04:00
}
2016-12-22 15:05:00 +03:00
static ssize_t in0_input_show ( struct device * dev ,
struct device_attribute * attr , char * buf )
2012-03-23 13:02:20 +04:00
{
struct i2c_client * client = to_i2c_client ( dev ) ;
struct mcp3021_data * data = i2c_get_clientdata ( client ) ;
int reg , in_input ;
reg = mcp3021_read16 ( client ) ;
if ( reg < 0 )
return reg ;
2012-09-21 15:04:21 +04:00
in_input = volts_from_reg ( data , reg ) ;
2012-03-23 13:02:20 +04:00
return sprintf ( buf , " %d \n " , in_input ) ;
}
2016-12-22 15:05:00 +03:00
static DEVICE_ATTR_RO ( in0_input ) ;
2012-03-23 13:02:20 +04:00
static int mcp3021_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
int err ;
struct mcp3021_data * data = NULL ;
2016-11-10 00:22:34 +03:00
struct device_node * np = client - > dev . of_node ;
2012-03-23 13:02:20 +04:00
if ( ! i2c_check_functionality ( client - > adapter , I2C_FUNC_I2C ) )
return - ENODEV ;
2012-06-02 22:20:19 +04:00
data = devm_kzalloc ( & client - > dev , sizeof ( struct mcp3021_data ) ,
GFP_KERNEL ) ;
2012-03-23 13:02:20 +04:00
if ( ! data )
return - ENOMEM ;
i2c_set_clientdata ( client , data ) ;
2016-11-10 00:22:34 +03:00
if ( np ) {
if ( ! of_property_read_u32 ( np , " reference-voltage-microvolt " ,
& data - > vdd ) )
data - > vdd / = 1000 ;
else
data - > vdd = MCP3021_VDD_REF_DEFAULT ;
} else {
u32 * pdata = dev_get_platdata ( & client - > dev ) ;
if ( pdata )
data - > vdd = * pdata ;
else
data - > vdd = MCP3021_VDD_REF_DEFAULT ;
}
2012-09-21 15:04:21 +04:00
switch ( id - > driver_data ) {
case mcp3021 :
data - > sar_shift = MCP3021_SAR_SHIFT ;
data - > sar_mask = MCP3021_SAR_MASK ;
data - > output_res = MCP3021_OUTPUT_RES ;
break ;
2012-09-21 15:04:22 +04:00
case mcp3221 :
data - > sar_shift = MCP3221_SAR_SHIFT ;
data - > sar_mask = MCP3221_SAR_MASK ;
data - > output_res = MCP3221_OUTPUT_RES ;
break ;
2012-09-21 15:04:21 +04:00
}
2016-11-10 00:22:34 +03:00
if ( data - > vdd > MCP3021_VDD_REF_MAX | | data - > vdd < MCP3021_VDD_REF_MIN )
return - EINVAL ;
2012-03-23 13:02:20 +04:00
err = sysfs_create_file ( & client - > dev . kobj , & dev_attr_in0_input . attr ) ;
if ( err )
2012-06-02 22:20:19 +04:00
return err ;
2012-03-23 13:02:20 +04:00
data - > hwmon_dev = hwmon_device_register ( & client - > dev ) ;
if ( IS_ERR ( data - > hwmon_dev ) ) {
err = PTR_ERR ( data - > hwmon_dev ) ;
goto exit_remove ;
}
return 0 ;
exit_remove :
sysfs_remove_file ( & client - > dev . kobj , & dev_attr_in0_input . attr ) ;
return err ;
}
static int mcp3021_remove ( struct i2c_client * client )
{
struct mcp3021_data * data = i2c_get_clientdata ( client ) ;
hwmon_device_unregister ( data - > hwmon_dev ) ;
sysfs_remove_file ( & client - > dev . kobj , & dev_attr_in0_input . attr ) ;
return 0 ;
}
static const struct i2c_device_id mcp3021_id [ ] = {
2012-09-21 15:04:21 +04:00
{ " mcp3021 " , mcp3021 } ,
2012-09-21 15:04:22 +04:00
{ " mcp3221 " , mcp3221 } ,
2012-03-23 13:02:20 +04:00
{ }
} ;
MODULE_DEVICE_TABLE ( i2c , mcp3021_id ) ;
2016-11-10 00:22:34 +03:00
# ifdef CONFIG_OF
static const struct of_device_id of_mcp3021_match [ ] = {
{ . compatible = " microchip,mcp3021 " , . data = ( void * ) mcp3021 } ,
{ . compatible = " microchip,mcp3221 " , . data = ( void * ) mcp3221 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( of , of_mcp3021_match ) ;
# endif
2012-03-23 13:02:20 +04:00
static struct i2c_driver mcp3021_driver = {
. driver = {
. name = " mcp3021 " ,
2016-11-10 00:22:34 +03:00
. of_match_table = of_match_ptr ( of_mcp3021_match ) ,
2012-03-23 13:02:20 +04:00
} ,
. probe = mcp3021_probe ,
. remove = mcp3021_remove ,
. id_table = mcp3021_id ,
} ;
module_i2c_driver ( mcp3021_driver ) ;
MODULE_AUTHOR ( " Mingkai Hu <Mingkai.hu@freescale.com> " ) ;
2012-09-21 15:04:22 +04:00
MODULE_DESCRIPTION ( " Microchip MCP3021/MCP3221 driver " ) ;
2012-03-23 13:02:20 +04:00
MODULE_LICENSE ( " GPL " ) ;