2019-05-27 09:55:01 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
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
*
2022-07-12 22:35:04 +03:00
* This driver exports the value of analog input voltage to sysfs , the
2012-03-23 13:02:20 +04:00
* voltage unit is mV . Through the sysfs interface , lm - sensors tool
* can also display the input voltage .
*/
# 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 {
2022-07-12 22:35:04 +03:00
struct i2c_client * client ;
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
} ;
2022-07-12 22:35:04 +03:00
static inline u16 volts_from_reg ( struct mcp3021_data * data , u16 val )
2012-03-23 13:02:20 +04:00
{
2022-07-12 22:35:04 +03:00
return DIV_ROUND_CLOSEST ( data - > vdd * val , 1 < < data - > output_res ) ;
}
static int mcp3021_read ( struct device * dev , enum hwmon_sensor_types type ,
u32 attr , int channel , long * val )
{
struct mcp3021_data * data = dev_get_drvdata ( dev ) ;
struct i2c_client * client = data - > client ;
2012-03-23 13:02:20 +04:00
__be16 buf ;
2022-07-12 22:35:04 +03:00
u16 reg ;
int ret ;
if ( type ! = hwmon_in )
return - EOPNOTSUPP ;
2012-03-23 13:02:20 +04:00
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
2022-07-12 22:35:04 +03:00
* val = volts_from_reg ( data , reg ) ;
2012-03-23 13:02:20 +04:00
2022-07-12 22:35:04 +03:00
return 0 ;
2012-03-23 13:02:20 +04:00
}
2022-07-12 22:35:04 +03:00
static umode_t mcp3021_is_visible ( const void * _data ,
enum hwmon_sensor_types type ,
u32 attr , int channel )
2012-03-23 13:02:20 +04:00
{
2022-07-12 22:35:04 +03:00
if ( type ! = hwmon_in )
return 0 ;
2012-03-23 13:02:20 +04:00
2022-07-12 22:35:04 +03:00
if ( attr ! = hwmon_in_input )
return 0 ;
2012-03-23 13:02:20 +04:00
2022-07-12 22:35:04 +03:00
return 0444 ;
2012-03-23 13:02:20 +04:00
}
2023-04-06 23:35:24 +03:00
static const struct hwmon_channel_info * const mcp3021_info [ ] = {
2022-07-12 22:35:04 +03:00
HWMON_CHANNEL_INFO ( in , HWMON_I_INPUT ) ,
NULL
} ;
static const struct hwmon_ops mcp3021_hwmon_ops = {
. is_visible = mcp3021_is_visible ,
. read = mcp3021_read ,
} ;
static const struct hwmon_chip_info mcp3021_chip_info = {
. ops = & mcp3021_hwmon_ops ,
. info = mcp3021_info ,
} ;
2012-03-23 13:02:20 +04:00
2020-08-13 19:02:22 +03:00
static const struct i2c_device_id mcp3021_id [ ] ;
static int mcp3021_probe ( struct i2c_client * client )
2012-03-23 13:02:20 +04:00
{
struct mcp3021_data * data = NULL ;
2016-11-10 00:22:34 +03:00
struct device_node * np = client - > dev . of_node ;
2022-07-12 22:35:04 +03:00
struct device * hwmon_dev ;
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 ;
}
2020-08-13 19:02:22 +03:00
switch ( i2c_match_id ( mcp3021_id , client ) - > driver_data ) {
2012-09-21 15:04:21 +04:00
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
}
2022-07-12 22:35:04 +03:00
data - > client = client ;
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
2022-07-12 22:35:04 +03:00
hwmon_dev = devm_hwmon_device_register_with_info ( & client - > dev ,
client - > name ,
data ,
& mcp3021_chip_info ,
NULL ) ;
return PTR_ERR_OR_ZERO ( hwmon_dev ) ;
2012-03-23 13:02:20 +04:00
}
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
} ,
2023-05-05 16:17:18 +03:00
. probe = mcp3021_probe ,
2012-03-23 13:02:20 +04:00
. 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 " ) ;