2013-05-03 13:58:00 +04:00
/*
* Copyright ( C ) 2013 Oskar Andero < oskar . andero @ gmail . com >
2014-10-08 22:42:00 +04:00
* Copyright ( C ) 2014 Rose Technology
* Allan Bendorff Jensen < abj @ rosetechnology . dk >
* Soren Andersen < san @ rosetechnology . dk >
*
* Driver for following ADC chips from Microchip Technology ' s :
* 10 Bit converter
* MCP3001
* MCP3002
* MCP3004
* MCP3008
* - - - - - - - - - - - -
* 12 bit converter
* MCP3201
* MCP3202
* MCP3204
* MCP3208
* - - - - - - - - - - - -
2013-05-03 13:58:00 +04:00
*
* Datasheet can be found here :
2014-10-08 22:42:00 +04:00
* http : //ww1.microchip.com/downloads/en/DeviceDoc/21293C.pdf mcp3001
* http : //ww1.microchip.com/downloads/en/DeviceDoc/21294E.pdf mcp3002
* http : //ww1.microchip.com/downloads/en/DeviceDoc/21295d.pdf mcp3004/08
* http : //ww1.microchip.com/downloads/en/DeviceDoc/21290D.pdf mcp3201
* http : //ww1.microchip.com/downloads/en/DeviceDoc/21034D.pdf mcp3202
* http : //ww1.microchip.com/downloads/en/DeviceDoc/21298c.pdf mcp3204/08
2013-05-03 13:58:00 +04:00
*
* 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/err.h>
2014-10-08 22:42:00 +04:00
# include <linux/delay.h>
2013-05-03 13:58:00 +04:00
# include <linux/spi/spi.h>
# include <linux/module.h>
# include <linux/iio/iio.h>
# include <linux/regulator/consumer.h>
enum {
2014-10-08 22:42:00 +04:00
mcp3001 ,
mcp3002 ,
mcp3004 ,
mcp3008 ,
mcp3201 ,
mcp3202 ,
2013-05-03 13:58:00 +04:00
mcp3204 ,
mcp3208 ,
} ;
2014-10-08 22:42:00 +04:00
struct mcp320x_chip_info {
const struct iio_chan_spec * channels ;
unsigned int num_channels ;
unsigned int resolution ;
} ;
2013-05-03 13:58:00 +04:00
struct mcp320x {
struct spi_device * spi ;
struct spi_message msg ;
struct spi_transfer transfer [ 2 ] ;
struct regulator * reg ;
struct mutex lock ;
2014-10-08 22:42:00 +04:00
const struct mcp320x_chip_info * chip_info ;
2015-05-06 19:49:17 +03:00
u8 tx_buf ____cacheline_aligned ;
u8 rx_buf [ 2 ] ;
2013-05-03 13:58:00 +04:00
} ;
2014-10-08 22:42:00 +04:00
static int mcp320x_channel_to_tx_data ( int device_index ,
const unsigned int channel , bool differential )
{
int start_bit = 1 ;
switch ( device_index ) {
case mcp3001 :
case mcp3201 :
return 0 ;
case mcp3002 :
case mcp3202 :
return ( ( start_bit < < 4 ) | ( ! differential < < 3 ) |
( channel < < 2 ) ) ;
case mcp3004 :
case mcp3204 :
case mcp3008 :
case mcp3208 :
return ( ( start_bit < < 6 ) | ( ! differential < < 5 ) |
( channel < < 2 ) ) ;
default :
return - EINVAL ;
}
}
static int mcp320x_adc_conversion ( struct mcp320x * adc , u8 channel ,
bool differential , int device_index )
2013-05-03 13:58:00 +04:00
{
int ret ;
2014-10-08 22:42:00 +04:00
adc - > rx_buf [ 0 ] = 0 ;
adc - > rx_buf [ 1 ] = 0 ;
adc - > tx_buf = mcp320x_channel_to_tx_data ( device_index ,
channel , differential ) ;
if ( device_index ! = mcp3001 & & device_index ! = mcp3201 ) {
ret = spi_sync ( adc - > spi , & adc - > msg ) ;
if ( ret < 0 )
return ret ;
} else {
ret = spi_read ( adc - > spi , & adc - > rx_buf , sizeof ( adc - > rx_buf ) ) ;
if ( ret < 0 )
return ret ;
}
2013-05-03 13:58:00 +04:00
2014-10-08 22:42:00 +04:00
switch ( device_index ) {
case mcp3001 :
return ( adc - > rx_buf [ 0 ] < < 5 | adc - > rx_buf [ 1 ] > > 3 ) ;
case mcp3002 :
case mcp3004 :
case mcp3008 :
return ( adc - > rx_buf [ 0 ] < < 2 | adc - > rx_buf [ 1 ] > > 6 ) ;
case mcp3201 :
return ( adc - > rx_buf [ 0 ] < < 7 | adc - > rx_buf [ 1 ] > > 1 ) ;
case mcp3202 :
case mcp3204 :
case mcp3208 :
return ( adc - > rx_buf [ 0 ] < < 4 | adc - > rx_buf [ 1 ] > > 4 ) ;
default :
return - EINVAL ;
}
2013-05-03 13:58:00 +04:00
}
static int mcp320x_read_raw ( struct iio_dev * indio_dev ,
struct iio_chan_spec const * channel , int * val ,
int * val2 , long mask )
{
struct mcp320x * adc = iio_priv ( indio_dev ) ;
int ret = - EINVAL ;
2014-10-08 22:42:00 +04:00
int device_index = 0 ;
2013-05-03 13:58:00 +04:00
mutex_lock ( & adc - > lock ) ;
2014-10-08 22:42:00 +04:00
device_index = spi_get_device_id ( adc - > spi ) - > driver_data ;
2013-05-03 13:58:00 +04:00
switch ( mask ) {
case IIO_CHAN_INFO_RAW :
2014-10-08 22:42:00 +04:00
ret = mcp320x_adc_conversion ( adc , channel - > address ,
channel - > differential , device_index ) ;
2013-05-03 13:58:00 +04:00
if ( ret < 0 )
goto out ;
* val = ret ;
ret = IIO_VAL_INT ;
break ;
case IIO_CHAN_INFO_SCALE :
ret = regulator_get_voltage ( adc - > reg ) ;
if ( ret < 0 )
goto out ;
2014-10-08 22:42:00 +04:00
/* convert regulator output voltage to mV */
2013-05-03 13:58:00 +04:00
* val = ret / 1000 ;
2014-10-08 22:42:00 +04:00
* val2 = adc - > chip_info - > resolution ;
2013-05-03 13:58:00 +04:00
ret = IIO_VAL_FRACTIONAL_LOG2 ;
break ;
}
out :
mutex_unlock ( & adc - > lock ) ;
return ret ;
}
# define MCP320X_VOLTAGE_CHANNEL(num) \
{ \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. channel = ( num ) , \
. address = ( num ) , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) \
}
# define MCP320X_VOLTAGE_CHANNEL_DIFF(num) \
{ \
. type = IIO_VOLTAGE , \
. indexed = 1 , \
. channel = ( num * 2 ) , \
. channel2 = ( num * 2 + 1 ) , \
. address = ( num * 2 ) , \
. differential = 1 , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) , \
. info_mask_shared_by_type = BIT ( IIO_CHAN_INFO_SCALE ) \
}
2014-10-08 22:42:00 +04:00
static const struct iio_chan_spec mcp3201_channels [ ] = {
MCP320X_VOLTAGE_CHANNEL_DIFF ( 0 ) ,
} ;
static const struct iio_chan_spec mcp3202_channels [ ] = {
MCP320X_VOLTAGE_CHANNEL ( 0 ) ,
MCP320X_VOLTAGE_CHANNEL ( 1 ) ,
MCP320X_VOLTAGE_CHANNEL_DIFF ( 0 ) ,
} ;
2013-05-03 13:58:00 +04:00
static const struct iio_chan_spec mcp3204_channels [ ] = {
MCP320X_VOLTAGE_CHANNEL ( 0 ) ,
MCP320X_VOLTAGE_CHANNEL ( 1 ) ,
MCP320X_VOLTAGE_CHANNEL ( 2 ) ,
MCP320X_VOLTAGE_CHANNEL ( 3 ) ,
MCP320X_VOLTAGE_CHANNEL_DIFF ( 0 ) ,
MCP320X_VOLTAGE_CHANNEL_DIFF ( 1 ) ,
} ;
static const struct iio_chan_spec mcp3208_channels [ ] = {
MCP320X_VOLTAGE_CHANNEL ( 0 ) ,
MCP320X_VOLTAGE_CHANNEL ( 1 ) ,
MCP320X_VOLTAGE_CHANNEL ( 2 ) ,
MCP320X_VOLTAGE_CHANNEL ( 3 ) ,
MCP320X_VOLTAGE_CHANNEL ( 4 ) ,
MCP320X_VOLTAGE_CHANNEL ( 5 ) ,
MCP320X_VOLTAGE_CHANNEL ( 6 ) ,
MCP320X_VOLTAGE_CHANNEL ( 7 ) ,
MCP320X_VOLTAGE_CHANNEL_DIFF ( 0 ) ,
MCP320X_VOLTAGE_CHANNEL_DIFF ( 1 ) ,
MCP320X_VOLTAGE_CHANNEL_DIFF ( 2 ) ,
MCP320X_VOLTAGE_CHANNEL_DIFF ( 3 ) ,
} ;
static const struct iio_info mcp320x_info = {
. read_raw = mcp320x_read_raw ,
. driver_module = THIS_MODULE ,
} ;
2014-10-08 22:42:00 +04:00
static const struct mcp320x_chip_info mcp320x_chip_infos [ ] = {
[ mcp3001 ] = {
. channels = mcp3201_channels ,
. num_channels = ARRAY_SIZE ( mcp3201_channels ) ,
. resolution = 10
} ,
[ mcp3002 ] = {
. channels = mcp3202_channels ,
. num_channels = ARRAY_SIZE ( mcp3202_channels ) ,
. resolution = 10
} ,
[ mcp3004 ] = {
. channels = mcp3204_channels ,
. num_channels = ARRAY_SIZE ( mcp3204_channels ) ,
. resolution = 10
} ,
[ mcp3008 ] = {
. channels = mcp3208_channels ,
. num_channels = ARRAY_SIZE ( mcp3208_channels ) ,
. resolution = 10
} ,
[ mcp3201 ] = {
. channels = mcp3201_channels ,
. num_channels = ARRAY_SIZE ( mcp3201_channels ) ,
. resolution = 12
} ,
[ mcp3202 ] = {
. channels = mcp3202_channels ,
. num_channels = ARRAY_SIZE ( mcp3202_channels ) ,
. resolution = 12
} ,
2013-05-03 13:58:00 +04:00
[ mcp3204 ] = {
. channels = mcp3204_channels ,
2014-10-08 22:42:00 +04:00
. num_channels = ARRAY_SIZE ( mcp3204_channels ) ,
. resolution = 12
2013-05-03 13:58:00 +04:00
} ,
[ mcp3208 ] = {
. channels = mcp3208_channels ,
2014-10-08 22:42:00 +04:00
. num_channels = ARRAY_SIZE ( mcp3208_channels ) ,
. resolution = 12
2013-05-03 13:58:00 +04:00
} ,
} ;
static int mcp320x_probe ( struct spi_device * spi )
{
struct iio_dev * indio_dev ;
struct mcp320x * adc ;
2014-10-08 22:42:00 +04:00
const struct mcp320x_chip_info * chip_info ;
2013-05-03 13:58:00 +04:00
int ret ;
2013-07-23 12:58:00 +04:00
indio_dev = devm_iio_device_alloc ( & spi - > dev , sizeof ( * adc ) ) ;
2013-05-03 13:58:00 +04:00
if ( ! indio_dev )
return - ENOMEM ;
adc = iio_priv ( indio_dev ) ;
adc - > spi = spi ;
indio_dev - > dev . parent = & spi - > dev ;
indio_dev - > name = spi_get_device_id ( spi ) - > name ;
indio_dev - > modes = INDIO_DIRECT_MODE ;
indio_dev - > info = & mcp320x_info ;
2014-10-08 22:42:00 +04:00
chip_info = & mcp320x_chip_infos [ spi_get_device_id ( spi ) - > driver_data ] ;
2013-05-03 13:58:00 +04:00
indio_dev - > channels = chip_info - > channels ;
indio_dev - > num_channels = chip_info - > num_channels ;
adc - > transfer [ 0 ] . tx_buf = & adc - > tx_buf ;
adc - > transfer [ 0 ] . len = sizeof ( adc - > tx_buf ) ;
adc - > transfer [ 1 ] . rx_buf = adc - > rx_buf ;
adc - > transfer [ 1 ] . len = sizeof ( adc - > rx_buf ) ;
spi_message_init_with_transfers ( & adc - > msg , adc - > transfer ,
ARRAY_SIZE ( adc - > transfer ) ) ;
2013-07-23 12:58:00 +04:00
adc - > reg = devm_regulator_get ( & spi - > dev , " vref " ) ;
if ( IS_ERR ( adc - > reg ) )
return PTR_ERR ( adc - > reg ) ;
2013-05-03 13:58:00 +04:00
ret = regulator_enable ( adc - > reg ) ;
if ( ret < 0 )
2013-07-23 12:58:00 +04:00
return ret ;
2013-05-03 13:58:00 +04:00
mutex_init ( & adc - > lock ) ;
ret = iio_device_register ( indio_dev ) ;
if ( ret < 0 )
goto reg_disable ;
return 0 ;
reg_disable :
regulator_disable ( adc - > reg ) ;
return ret ;
}
static int mcp320x_remove ( struct spi_device * spi )
{
struct iio_dev * indio_dev = spi_get_drvdata ( spi ) ;
struct mcp320x * adc = iio_priv ( indio_dev ) ;
iio_device_unregister ( indio_dev ) ;
regulator_disable ( adc - > reg ) ;
return 0 ;
}
2014-10-08 22:42:00 +04:00
# if defined(CONFIG_OF)
static const struct of_device_id mcp320x_dt_ids [ ] = {
{
. compatible = " mcp3001 " ,
. data = & mcp320x_chip_infos [ mcp3001 ] ,
} , {
. compatible = " mcp3002 " ,
. data = & mcp320x_chip_infos [ mcp3002 ] ,
} , {
. compatible = " mcp3004 " ,
. data = & mcp320x_chip_infos [ mcp3004 ] ,
} , {
. compatible = " mcp3008 " ,
. data = & mcp320x_chip_infos [ mcp3008 ] ,
} , {
. compatible = " mcp3201 " ,
. data = & mcp320x_chip_infos [ mcp3201 ] ,
} , {
. compatible = " mcp3202 " ,
. data = & mcp320x_chip_infos [ mcp3202 ] ,
} , {
. compatible = " mcp3204 " ,
. data = & mcp320x_chip_infos [ mcp3204 ] ,
} , {
. compatible = " mcp3208 " ,
. data = & mcp320x_chip_infos [ mcp3208 ] ,
} , {
}
} ;
MODULE_DEVICE_TABLE ( of , mcp320x_dt_ids ) ;
# endif
2013-05-03 13:58:00 +04:00
static const struct spi_device_id mcp320x_id [ ] = {
2014-10-08 22:42:00 +04:00
{ " mcp3001 " , mcp3001 } ,
{ " mcp3002 " , mcp3002 } ,
{ " mcp3004 " , mcp3004 } ,
{ " mcp3008 " , mcp3008 } ,
{ " mcp3201 " , mcp3201 } ,
{ " mcp3202 " , mcp3202 } ,
2013-05-03 13:58:00 +04:00
{ " mcp3204 " , mcp3204 } ,
{ " mcp3208 " , mcp3208 } ,
{ }
} ;
MODULE_DEVICE_TABLE ( spi , mcp320x_id ) ;
static struct spi_driver mcp320x_driver = {
. driver = {
. name = " mcp320x " ,
. owner = THIS_MODULE ,
} ,
. probe = mcp320x_probe ,
. remove = mcp320x_remove ,
. id_table = mcp320x_id ,
} ;
module_spi_driver ( mcp320x_driver ) ;
MODULE_AUTHOR ( " Oskar Andero <oskar.andero@gmail.com> " ) ;
2014-10-08 22:42:00 +04:00
MODULE_DESCRIPTION ( " Microchip Technology MCP3x01/02/04/08 " ) ;
2013-05-03 13:58:00 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;