2019-05-30 02:57:50 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-03-01 16:42:26 +03:00
/*
*
* TWL4030 MADC module driver - This driver monitors the real time
* conversion of analog signals like battery temperature ,
* battery type , battery level etc .
*
2020-07-04 22:27:43 +03:00
* Copyright ( C ) 2011 Texas Instruments Incorporated - https : //www.ti.com/
2011-03-01 16:42:26 +03:00
* J Keerthy < j - keerthy @ ti . com >
*
* Based on twl4030 - madc . c
* Copyright ( C ) 2008 Nokia Corporation
* Mikko Ylinen < mikko . k . ylinen @ nokia . com >
*
* Amit Kucheria < amit . kucheria @ canonical . com >
*/
# include <linux/device.h>
# include <linux/interrupt.h>
# include <linux/kernel.h>
# include <linux/delay.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
2017-08-14 19:34:24 +03:00
# include <linux/mfd/twl.h>
2011-03-01 16:42:26 +03:00
# include <linux/module.h>
# include <linux/stddef.h>
# include <linux/mutex.h>
# include <linux/bitops.h>
# include <linux/jiffies.h>
# include <linux/types.h>
# include <linux/gfp.h>
# include <linux/err.h>
2015-08-04 21:15:48 +03:00
# include <linux/regulator/consumer.h>
2011-03-01 16:42:26 +03:00
2014-03-16 05:43:26 +04:00
# include <linux/iio/iio.h>
2017-04-27 18:30:12 +03:00
# define TWL4030_MADC_MAX_CHANNELS 16
# define TWL4030_MADC_CTRL1 0x00
# define TWL4030_MADC_CTRL2 0x01
# define TWL4030_MADC_RTSELECT_LSB 0x02
# define TWL4030_MADC_SW1SELECT_LSB 0x06
# define TWL4030_MADC_SW2SELECT_LSB 0x0A
# define TWL4030_MADC_RTAVERAGE_LSB 0x04
# define TWL4030_MADC_SW1AVERAGE_LSB 0x08
# define TWL4030_MADC_SW2AVERAGE_LSB 0x0C
# define TWL4030_MADC_CTRL_SW1 0x12
# define TWL4030_MADC_CTRL_SW2 0x13
# define TWL4030_MADC_RTCH0_LSB 0x17
# define TWL4030_MADC_GPCH0_LSB 0x37
# define TWL4030_MADC_MADCON (1 << 0) /* MADC power on */
# define TWL4030_MADC_BUSY (1 << 0) /* MADC busy */
/* MADC conversion completion */
# define TWL4030_MADC_EOC_SW (1 << 1)
/* MADC SWx start conversion */
# define TWL4030_MADC_SW_START (1 << 5)
# define TWL4030_MADC_ADCIN0 (1 << 0)
# define TWL4030_MADC_ADCIN1 (1 << 1)
# define TWL4030_MADC_ADCIN2 (1 << 2)
# define TWL4030_MADC_ADCIN3 (1 << 3)
# define TWL4030_MADC_ADCIN4 (1 << 4)
# define TWL4030_MADC_ADCIN5 (1 << 5)
# define TWL4030_MADC_ADCIN6 (1 << 6)
# define TWL4030_MADC_ADCIN7 (1 << 7)
# define TWL4030_MADC_ADCIN8 (1 << 8)
# define TWL4030_MADC_ADCIN9 (1 << 9)
# define TWL4030_MADC_ADCIN10 (1 << 10)
# define TWL4030_MADC_ADCIN11 (1 << 11)
# define TWL4030_MADC_ADCIN12 (1 << 12)
# define TWL4030_MADC_ADCIN13 (1 << 13)
# define TWL4030_MADC_ADCIN14 (1 << 14)
# define TWL4030_MADC_ADCIN15 (1 << 15)
/* Fixed channels */
# define TWL4030_MADC_BTEMP TWL4030_MADC_ADCIN1
# define TWL4030_MADC_VBUS TWL4030_MADC_ADCIN8
# define TWL4030_MADC_VBKB TWL4030_MADC_ADCIN9
# define TWL4030_MADC_ICHG TWL4030_MADC_ADCIN10
# define TWL4030_MADC_VCHG TWL4030_MADC_ADCIN11
# define TWL4030_MADC_VBAT TWL4030_MADC_ADCIN12
/* Step size and prescaler ratio */
# define TEMP_STEP_SIZE 147
# define TEMP_PSR_R 100
# define CURR_STEP_SIZE 147
# define CURR_PSR_R1 44
# define CURR_PSR_R2 88
# define TWL4030_BCI_BCICTL1 0x23
# define TWL4030_BCI_CGAIN 0x020
# define TWL4030_BCI_MESBAT (1 << 1)
# define TWL4030_BCI_TYPEN (1 << 4)
# define TWL4030_BCI_ITHEN (1 << 3)
# define REG_BCICTL2 0x024
# define TWL4030_BCI_ITHSENS 0x007
/* Register and bits for GPBR1 register */
# define TWL4030_REG_GPBR1 0x0c
# define TWL4030_GPBR1_MADC_HFCLK_EN (1 << 7)
2015-08-04 21:15:48 +03:00
# define TWL4030_USB_SEL_MADC_MCPC (1<<3)
# define TWL4030_USB_CARKIT_ANA_CTRL 0xBB
2017-04-27 18:30:12 +03:00
struct twl4030_madc_conversion_method {
u8 sel ;
u8 avg ;
u8 rbase ;
u8 ctrl ;
} ;
/**
* struct twl4030_madc_request - madc request packet for channel conversion
* @ channels : 16 bit bitmap for individual channels
* @ do_avg : sample the input channel for 4 consecutive cycles
* @ method : RT , SW1 , SW2
* @ type : Polling or interrupt based method
* @ active : Flag if request is active
* @ result_pending : Flag from irq handler , that result is ready
* @ raw : Return raw value , do not convert it
* @ rbuf : Result buffer
*/
struct twl4030_madc_request {
unsigned long channels ;
bool do_avg ;
u16 method ;
u16 type ;
bool active ;
bool result_pending ;
bool raw ;
int rbuf [ TWL4030_MADC_MAX_CHANNELS ] ;
} ;
enum conversion_methods {
TWL4030_MADC_RT ,
TWL4030_MADC_SW1 ,
TWL4030_MADC_SW2 ,
TWL4030_MADC_NUM_METHODS
} ;
enum sample_type {
TWL4030_MADC_WAIT ,
TWL4030_MADC_IRQ_ONESHOT ,
TWL4030_MADC_IRQ_REARM
} ;
2014-03-16 05:43:27 +04:00
/**
2011-03-01 16:42:26 +03:00
* struct twl4030_madc_data - a container for madc info
2014-03-16 05:43:27 +04:00
* @ dev : Pointer to device structure for madc
* @ lock : Mutex protecting this data structure
2020-07-17 19:55:11 +03:00
* @ usb3v1 : Pointer to bias regulator for madc
2014-03-16 05:43:27 +04:00
* @ requests : Array of request struct corresponding to SW1 , SW2 and RT
* @ use_second_irq : IRQ selection ( main or co - processor )
* @ imr : Interrupt mask register of MADC
* @ isr : Interrupt status register of MADC
2011-03-01 16:42:26 +03:00
*/
struct twl4030_madc_data {
struct device * dev ;
2020-07-17 19:55:11 +03:00
struct mutex lock ;
2015-08-04 21:15:48 +03:00
struct regulator * usb3v1 ;
2011-03-01 16:42:26 +03:00
struct twl4030_madc_request requests [ TWL4030_MADC_NUM_METHODS ] ;
2014-03-16 05:43:26 +04:00
bool use_second_irq ;
2014-03-16 05:43:27 +04:00
u8 imr ;
u8 isr ;
2011-03-01 16:42:26 +03:00
} ;
2017-04-27 18:30:09 +03:00
static int twl4030_madc_conversion ( struct twl4030_madc_request * req ) ;
2014-03-16 05:43:26 +04:00
static int twl4030_madc_read ( struct iio_dev * iio_dev ,
const struct iio_chan_spec * chan ,
int * val , int * val2 , long mask )
{
struct twl4030_madc_data * madc = iio_priv ( iio_dev ) ;
struct twl4030_madc_request req ;
int ret ;
req . method = madc - > use_second_irq ? TWL4030_MADC_SW2 : TWL4030_MADC_SW1 ;
req . channels = BIT ( chan - > channel ) ;
req . active = false ;
req . type = TWL4030_MADC_WAIT ;
req . raw = ! ( mask = = IIO_CHAN_INFO_PROCESSED ) ;
req . do_avg = ( mask = = IIO_CHAN_INFO_AVERAGE_RAW ) ;
ret = twl4030_madc_conversion ( & req ) ;
if ( ret < 0 )
return ret ;
* val = req . rbuf [ chan - > channel ] ;
return IIO_VAL_INT ;
}
static const struct iio_info twl4030_madc_iio_info = {
. read_raw = & twl4030_madc_read ,
} ;
# define TWL4030_ADC_CHANNEL(_channel, _type, _name) { \
. type = _type , \
. channel = _channel , \
. info_mask_separate = BIT ( IIO_CHAN_INFO_RAW ) | \
BIT ( IIO_CHAN_INFO_AVERAGE_RAW ) | \
BIT ( IIO_CHAN_INFO_PROCESSED ) , \
. datasheet_name = _name , \
. indexed = 1 , \
}
static const struct iio_chan_spec twl4030_madc_iio_channels [ ] = {
TWL4030_ADC_CHANNEL ( 0 , IIO_VOLTAGE , " ADCIN0 " ) ,
TWL4030_ADC_CHANNEL ( 1 , IIO_TEMP , " ADCIN1 " ) ,
TWL4030_ADC_CHANNEL ( 2 , IIO_VOLTAGE , " ADCIN2 " ) ,
TWL4030_ADC_CHANNEL ( 3 , IIO_VOLTAGE , " ADCIN3 " ) ,
TWL4030_ADC_CHANNEL ( 4 , IIO_VOLTAGE , " ADCIN4 " ) ,
TWL4030_ADC_CHANNEL ( 5 , IIO_VOLTAGE , " ADCIN5 " ) ,
TWL4030_ADC_CHANNEL ( 6 , IIO_VOLTAGE , " ADCIN6 " ) ,
TWL4030_ADC_CHANNEL ( 7 , IIO_VOLTAGE , " ADCIN7 " ) ,
TWL4030_ADC_CHANNEL ( 8 , IIO_VOLTAGE , " ADCIN8 " ) ,
TWL4030_ADC_CHANNEL ( 9 , IIO_VOLTAGE , " ADCIN9 " ) ,
TWL4030_ADC_CHANNEL ( 10 , IIO_CURRENT , " ADCIN10 " ) ,
TWL4030_ADC_CHANNEL ( 11 , IIO_VOLTAGE , " ADCIN11 " ) ,
TWL4030_ADC_CHANNEL ( 12 , IIO_VOLTAGE , " ADCIN12 " ) ,
TWL4030_ADC_CHANNEL ( 13 , IIO_VOLTAGE , " ADCIN13 " ) ,
TWL4030_ADC_CHANNEL ( 14 , IIO_VOLTAGE , " ADCIN14 " ) ,
TWL4030_ADC_CHANNEL ( 15 , IIO_VOLTAGE , " ADCIN15 " ) ,
} ;
2011-03-01 16:42:26 +03:00
static struct twl4030_madc_data * twl4030_madc ;
struct twl4030_prescale_divider_ratios {
s16 numerator ;
s16 denominator ;
} ;
static const struct twl4030_prescale_divider_ratios
twl4030_divider_ratios [ 16 ] = {
{ 1 , 1 } , /* CHANNEL 0 No Prescaler */
{ 1 , 1 } , /* CHANNEL 1 No Prescaler */
{ 6 , 10 } , /* CHANNEL 2 */
{ 6 , 10 } , /* CHANNEL 3 */
{ 6 , 10 } , /* CHANNEL 4 */
{ 6 , 10 } , /* CHANNEL 5 */
{ 6 , 10 } , /* CHANNEL 6 */
{ 6 , 10 } , /* CHANNEL 7 */
{ 3 , 14 } , /* CHANNEL 8 */
{ 1 , 3 } , /* CHANNEL 9 */
{ 1 , 1 } , /* CHANNEL 10 No Prescaler */
{ 15 , 100 } , /* CHANNEL 11 */
{ 1 , 4 } , /* CHANNEL 12 */
{ 1 , 1 } , /* CHANNEL 13 Reserved channels */
{ 1 , 1 } , /* CHANNEL 14 Reseved channels */
{ 5 , 11 } , /* CHANNEL 15 */
} ;
2014-03-16 05:43:27 +04:00
/* Conversion table from -3 to 55 degrees Celcius */
static int twl4030_therm_tbl [ ] = {
30800 , 29500 , 28300 , 27100 ,
26000 , 24900 , 23900 , 22900 , 22000 , 21100 , 20300 , 19400 , 18700 ,
17900 , 17200 , 16500 , 15900 , 15300 , 14700 , 14100 , 13600 , 13100 ,
12600 , 12100 , 11600 , 11200 , 10800 , 10400 , 10000 , 9630 , 9280 ,
8950 , 8620 , 8310 , 8020 , 7730 , 7460 , 7200 , 6950 , 6710 ,
6470 , 6250 , 6040 , 5830 , 5640 , 5450 , 5260 , 5090 , 4920 ,
4760 , 4600 , 4450 , 4310 , 4170 , 4040 , 3910 , 3790 , 3670 ,
3550
2011-03-01 16:42:26 +03:00
} ;
/*
* Structure containing the registers
* of different conversion methods supported by MADC .
* Hardware or RT real time conversion request initiated by external host
* processor for RT Signal conversions .
* External host processors can also request for non RT conversions
* SW1 and SW2 software conversions also called asynchronous or GPC request .
*/
static
const struct twl4030_madc_conversion_method twl4030_conversion_methods [ ] = {
[ TWL4030_MADC_RT ] = {
. sel = TWL4030_MADC_RTSELECT_LSB ,
. avg = TWL4030_MADC_RTAVERAGE_LSB ,
. rbase = TWL4030_MADC_RTCH0_LSB ,
} ,
[ TWL4030_MADC_SW1 ] = {
. sel = TWL4030_MADC_SW1SELECT_LSB ,
. avg = TWL4030_MADC_SW1AVERAGE_LSB ,
. rbase = TWL4030_MADC_GPCH0_LSB ,
. ctrl = TWL4030_MADC_CTRL_SW1 ,
} ,
[ TWL4030_MADC_SW2 ] = {
. sel = TWL4030_MADC_SW2SELECT_LSB ,
. avg = TWL4030_MADC_SW2AVERAGE_LSB ,
. rbase = TWL4030_MADC_GPCH0_LSB ,
. ctrl = TWL4030_MADC_CTRL_SW2 ,
} ,
} ;
2014-03-16 05:43:27 +04:00
/**
* twl4030_madc_channel_raw_read ( ) - Function to read a particular channel value
* @ madc : pointer to struct twl4030_madc_data
* @ reg : lsb of ADC Channel
*
* Return : 0 on success , an error code otherwise .
2011-03-01 16:42:26 +03:00
*/
static int twl4030_madc_channel_raw_read ( struct twl4030_madc_data * madc , u8 reg )
{
2014-03-16 05:43:29 +04:00
u16 val ;
2011-03-01 16:42:26 +03:00
int ret ;
/*
* For each ADC channel , we have MSB and LSB register pair . MSB address
* is always LSB address + 1. reg parameter is the address of LSB register
*/
2014-03-16 05:43:29 +04:00
ret = twl_i2c_read_u16 ( TWL4030_MODULE_MADC , & val , reg ) ;
2011-03-01 16:42:26 +03:00
if ( ret ) {
2014-03-16 05:43:29 +04:00
dev_err ( madc - > dev , " unable to read register 0x%X \n " , reg ) ;
2011-03-01 16:42:26 +03:00
return ret ;
}
2014-03-16 05:43:29 +04:00
return ( int ) ( val > > 6 ) ;
2011-03-01 16:42:26 +03:00
}
/*
2014-03-16 05:43:27 +04:00
* Return battery temperature in degrees Celsius
2011-03-01 16:42:26 +03:00
* Or < 0 on failure .
*/
static int twl4030battery_temperature ( int raw_volt )
{
u8 val ;
int temp , curr , volt , res , ret ;
volt = ( raw_volt * TEMP_STEP_SIZE ) / TEMP_PSR_R ;
2014-03-16 05:43:27 +04:00
/* Getting and calculating the supply current in micro amperes */
2012-11-13 12:28:51 +04:00
ret = twl_i2c_read_u8 ( TWL_MODULE_MAIN_CHARGE , & val ,
2011-03-01 16:42:26 +03:00
REG_BCICTL2 ) ;
if ( ret < 0 )
return ret ;
2014-03-16 05:43:27 +04:00
2015-05-28 22:50:18 +03:00
curr = ( ( val & TWL4030_BCI_ITHSENS ) + 1 ) * 10 ;
2011-03-01 16:42:26 +03:00
/* Getting and calculating the thermistor resistance in ohms */
res = volt * 1000 / curr ;
/* calculating temperature */
for ( temp = 58 ; temp > = 0 ; temp - - ) {
2014-03-16 05:43:27 +04:00
int actual = twl4030_therm_tbl [ temp ] ;
2011-03-01 16:42:26 +03:00
if ( ( actual - res ) > = 0 )
break ;
}
return temp + 1 ;
}
static int twl4030battery_current ( int raw_volt )
{
int ret ;
u8 val ;
2012-11-13 12:28:51 +04:00
ret = twl_i2c_read_u8 ( TWL_MODULE_MAIN_CHARGE , & val ,
2011-03-01 16:42:26 +03:00
TWL4030_BCI_BCICTL1 ) ;
if ( ret )
return ret ;
if ( val & TWL4030_BCI_CGAIN ) /* slope of 0.44 mV/mA */
return ( raw_volt * CURR_STEP_SIZE ) / CURR_PSR_R1 ;
else /* slope of 0.88 mV/mA */
return ( raw_volt * CURR_STEP_SIZE ) / CURR_PSR_R2 ;
}
2014-03-16 05:43:27 +04:00
2011-03-01 16:42:26 +03:00
/*
* Function to read channel values
* @ madc - pointer to twl4030_madc_data struct
* @ reg_base - Base address of the first channel
2014-03-16 05:43:27 +04:00
* @ Channels - 16 bit bitmap . If the bit is set , channel ' s value is read
2011-03-01 16:42:26 +03:00
* @ buf - The channel values are stored here . if read fails error
2013-02-16 02:56:49 +04:00
* @ raw - Return raw values without conversion
2011-03-01 16:42:26 +03:00
* value is stored
* Returns the number of successfully read channels .
*/
static int twl4030_madc_read_channels ( struct twl4030_madc_data * madc ,
u8 reg_base , unsigned
2013-02-16 02:56:49 +04:00
long channels , int * buf ,
bool raw )
2011-03-01 16:42:26 +03:00
{
2014-03-16 05:43:27 +04:00
int count = 0 ;
int i ;
2011-03-01 16:42:26 +03:00
u8 reg ;
for_each_set_bit ( i , & channels , TWL4030_MADC_MAX_CHANNELS ) {
2014-03-16 05:43:27 +04:00
reg = reg_base + ( 2 * i ) ;
2011-03-01 16:42:26 +03:00
buf [ i ] = twl4030_madc_channel_raw_read ( madc , reg ) ;
if ( buf [ i ] < 0 ) {
2014-03-16 05:43:27 +04:00
dev_err ( madc - > dev , " Unable to read register 0x%X \n " ,
reg ) ;
return buf [ i ] ;
2011-03-01 16:42:26 +03:00
}
2013-02-16 02:56:49 +04:00
if ( raw ) {
count + + ;
continue ;
}
2011-03-01 16:42:26 +03:00
switch ( i ) {
case 10 :
buf [ i ] = twl4030battery_current ( buf [ i ] ) ;
if ( buf [ i ] < 0 ) {
dev_err ( madc - > dev , " err reading current \n " ) ;
2014-03-16 05:43:27 +04:00
return buf [ i ] ;
2011-03-01 16:42:26 +03:00
} else {
count + + ;
buf [ i ] = buf [ i ] - 750 ;
}
break ;
case 1 :
buf [ i ] = twl4030battery_temperature ( buf [ i ] ) ;
if ( buf [ i ] < 0 ) {
dev_err ( madc - > dev , " err reading temperature \n " ) ;
2014-03-16 05:43:27 +04:00
return buf [ i ] ;
2011-03-01 16:42:26 +03:00
} else {
buf [ i ] - = 3 ;
count + + ;
}
break ;
default :
count + + ;
/* Analog Input (V) = conv_result * step_size / R
* conv_result = decimal value of 10 - bit conversion
* result
* step size = 1.5 / ( 2 ^ 10 - 1 )
* R = Prescaler ratio for input channels .
* Result given in mV hence multiplied by 1000.
*/
buf [ i ] = ( buf [ i ] * 3 * 1000 *
twl4030_divider_ratios [ i ] . denominator )
/ ( 2 * 1023 *
twl4030_divider_ratios [ i ] . numerator ) ;
}
}
return count ;
}
/*
* Disables irq .
* @ madc - pointer to twl4030_madc_data struct
* @ id - irq number to be disabled
* can take one of TWL4030_MADC_RT , TWL4030_MADC_SW1 , TWL4030_MADC_SW2
* corresponding to RT , SW1 , SW2 conversion requests .
* Returns error if i2c read / write fails .
*/
static int twl4030_madc_disable_irq ( struct twl4030_madc_data * madc , u8 id )
{
u8 val ;
int ret ;
ret = twl_i2c_read_u8 ( TWL4030_MODULE_MADC , & val , madc - > imr ) ;
if ( ret ) {
dev_err ( madc - > dev , " unable to read imr register 0x%X \n " ,
madc - > imr ) ;
return ret ;
}
val | = ( 1 < < id ) ;
ret = twl_i2c_write_u8 ( TWL4030_MODULE_MADC , val , madc - > imr ) ;
if ( ret ) {
dev_err ( madc - > dev ,
" unable to write imr register 0x%X \n " , madc - > imr ) ;
return ret ;
}
return 0 ;
}
static irqreturn_t twl4030_madc_threaded_irq_handler ( int irq , void * _madc )
{
struct twl4030_madc_data * madc = _madc ;
const struct twl4030_madc_conversion_method * method ;
u8 isr_val , imr_val ;
2020-07-17 19:55:10 +03:00
int i , ret ;
2011-03-01 16:42:26 +03:00
struct twl4030_madc_request * r ;
mutex_lock ( & madc - > lock ) ;
ret = twl_i2c_read_u8 ( TWL4030_MODULE_MADC , & isr_val , madc - > isr ) ;
if ( ret ) {
dev_err ( madc - > dev , " unable to read isr register 0x%X \n " ,
madc - > isr ) ;
goto err_i2c ;
}
ret = twl_i2c_read_u8 ( TWL4030_MODULE_MADC , & imr_val , madc - > imr ) ;
if ( ret ) {
dev_err ( madc - > dev , " unable to read imr register 0x%X \n " ,
madc - > imr ) ;
goto err_i2c ;
}
isr_val & = ~ imr_val ;
for ( i = 0 ; i < TWL4030_MADC_NUM_METHODS ; i + + ) {
if ( ! ( isr_val & ( 1 < < i ) ) )
continue ;
ret = twl4030_madc_disable_irq ( madc , i ) ;
if ( ret < 0 )
2014-03-16 05:43:27 +04:00
dev_dbg ( madc - > dev , " Disable interrupt failed %d \n " , i ) ;
2019-10-13 19:37:54 +03:00
madc - > requests [ i ] . result_pending = true ;
2011-03-01 16:42:26 +03:00
}
for ( i = 0 ; i < TWL4030_MADC_NUM_METHODS ; i + + ) {
r = & madc - > requests [ i ] ;
/* No pending results for this method, move to next one */
if ( ! r - > result_pending )
continue ;
method = & twl4030_conversion_methods [ r - > method ] ;
/* Read results */
2020-07-17 19:55:10 +03:00
twl4030_madc_read_channels ( madc , method - > rbase ,
r - > channels , r - > rbuf , r - > raw ) ;
2011-03-01 16:42:26 +03:00
/* Free request */
2019-10-13 19:37:54 +03:00
r - > result_pending = false ;
r - > active = false ;
2011-03-01 16:42:26 +03:00
}
mutex_unlock ( & madc - > lock ) ;
return IRQ_HANDLED ;
err_i2c :
/*
* In case of error check whichever request is active
* and service the same .
*/
for ( i = 0 ; i < TWL4030_MADC_NUM_METHODS ; i + + ) {
r = & madc - > requests [ i ] ;
2019-10-13 19:37:54 +03:00
if ( ! r - > active )
2011-03-01 16:42:26 +03:00
continue ;
method = & twl4030_conversion_methods [ r - > method ] ;
/* Read results */
2020-07-17 19:55:10 +03:00
twl4030_madc_read_channels ( madc , method - > rbase ,
r - > channels , r - > rbuf , r - > raw ) ;
2011-03-01 16:42:26 +03:00
/* Free request */
2019-10-13 19:37:54 +03:00
r - > result_pending = false ;
r - > active = false ;
2011-03-01 16:42:26 +03:00
}
mutex_unlock ( & madc - > lock ) ;
return IRQ_HANDLED ;
}
/*
* Function which enables the madc conversion
* by writing to the control register .
* @ madc - pointer to twl4030_madc_data struct
* @ conv_method - can be TWL4030_MADC_RT , TWL4030_MADC_SW2 , TWL4030_MADC_SW1
* corresponding to RT SW1 or SW2 conversion methods .
* Returns 0 if succeeds else a negative error value
*/
static int twl4030_madc_start_conversion ( struct twl4030_madc_data * madc ,
int conv_method )
{
const struct twl4030_madc_conversion_method * method ;
int ret = 0 ;
2014-03-16 05:43:27 +04:00
if ( conv_method ! = TWL4030_MADC_SW1 & & conv_method ! = TWL4030_MADC_SW2 )
return - ENOTSUPP ;
2011-03-01 16:42:26 +03:00
method = & twl4030_conversion_methods [ conv_method ] ;
2014-03-16 05:43:27 +04:00
ret = twl_i2c_write_u8 ( TWL4030_MODULE_MADC , TWL4030_MADC_SW_START ,
method - > ctrl ) ;
if ( ret ) {
dev_err ( madc - > dev , " unable to write ctrl register 0x%X \n " ,
method - > ctrl ) ;
return ret ;
2011-03-01 16:42:26 +03:00
}
return 0 ;
}
/*
* Function that waits for conversion to be ready
* @ madc - pointer to twl4030_madc_data struct
* @ timeout_ms - timeout value in milliseconds
* @ status_reg - ctrl register
* returns 0 if succeeds else a negative error value
*/
static int twl4030_madc_wait_conversion_ready ( struct twl4030_madc_data * madc ,
unsigned int timeout_ms ,
u8 status_reg )
{
unsigned long timeout ;
int ret ;
timeout = jiffies + msecs_to_jiffies ( timeout_ms ) ;
do {
u8 reg ;
ret = twl_i2c_read_u8 ( TWL4030_MODULE_MADC , & reg , status_reg ) ;
if ( ret ) {
dev_err ( madc - > dev ,
" unable to read status register 0x%X \n " ,
status_reg ) ;
return ret ;
}
if ( ! ( reg & TWL4030_MADC_BUSY ) & & ( reg & TWL4030_MADC_EOC_SW ) )
return 0 ;
usleep_range ( 500 , 2000 ) ;
} while ( ! time_after ( jiffies , timeout ) ) ;
dev_err ( madc - > dev , " conversion timeout! \n " ) ;
return - EAGAIN ;
}
/*
* An exported function which can be called from other kernel drivers .
* @ req twl4030_madc_request structure
* req - > rbuf will be filled with read values of channels based on the
* channel index . If a particular channel reading fails there will
* be a negative error value in the corresponding array element .
* returns 0 if succeeds else error value
*/
2017-04-27 18:30:09 +03:00
static int twl4030_madc_conversion ( struct twl4030_madc_request * req )
2011-03-01 16:42:26 +03:00
{
const struct twl4030_madc_conversion_method * method ;
int ret ;
2011-08-12 07:33:14 +04:00
if ( ! req | | ! twl4030_madc )
2011-03-01 16:42:26 +03:00
return - EINVAL ;
2011-08-12 07:33:14 +04:00
2011-03-01 16:42:26 +03:00
mutex_lock ( & twl4030_madc - > lock ) ;
if ( req - > method < TWL4030_MADC_RT | | req - > method > TWL4030_MADC_SW2 ) {
ret = - EINVAL ;
goto out ;
}
/* Do we have a conversion request ongoing */
if ( twl4030_madc - > requests [ req - > method ] . active ) {
ret = - EBUSY ;
goto out ;
}
method = & twl4030_conversion_methods [ req - > method ] ;
/* Select channels to be converted */
2014-03-16 05:43:29 +04:00
ret = twl_i2c_write_u16 ( TWL4030_MODULE_MADC , req - > channels , method - > sel ) ;
2011-03-01 16:42:26 +03:00
if ( ret ) {
dev_err ( twl4030_madc - > dev ,
2014-03-16 05:43:29 +04:00
" unable to write sel register 0x%X \n " , method - > sel ) ;
2011-07-11 19:20:31 +04:00
goto out ;
2011-03-01 16:42:26 +03:00
}
/* Select averaging for all channels if do_avg is set */
if ( req - > do_avg ) {
2014-03-16 05:43:29 +04:00
ret = twl_i2c_write_u16 ( TWL4030_MODULE_MADC , req - > channels ,
method - > avg ) ;
2011-03-01 16:42:26 +03:00
if ( ret ) {
dev_err ( twl4030_madc - > dev ,
" unable to write avg register 0x%X \n " ,
2014-03-16 05:43:27 +04:00
method - > avg ) ;
2011-07-11 19:20:31 +04:00
goto out ;
2011-03-01 16:42:26 +03:00
}
}
/* With RT method we should not be here anymore */
if ( req - > method = = TWL4030_MADC_RT ) {
ret = - EINVAL ;
goto out ;
}
ret = twl4030_madc_start_conversion ( twl4030_madc , req - > method ) ;
if ( ret < 0 )
goto out ;
2019-10-13 19:37:54 +03:00
twl4030_madc - > requests [ req - > method ] . active = true ;
2011-03-01 16:42:26 +03:00
/* Wait until conversion is ready (ctrl register returns EOC) */
ret = twl4030_madc_wait_conversion_ready ( twl4030_madc , 5 , method - > ctrl ) ;
if ( ret ) {
2019-10-13 19:37:54 +03:00
twl4030_madc - > requests [ req - > method ] . active = false ;
2011-03-01 16:42:26 +03:00
goto out ;
}
ret = twl4030_madc_read_channels ( twl4030_madc , method - > rbase ,
2013-02-16 02:56:49 +04:00
req - > channels , req - > rbuf , req - > raw ) ;
2019-10-13 19:37:54 +03:00
twl4030_madc - > requests [ req - > method ] . active = false ;
2011-03-01 16:42:26 +03:00
out :
mutex_unlock ( & twl4030_madc - > lock ) ;
return ret ;
}
2014-03-16 05:43:27 +04:00
/**
* twl4030_madc_set_current_generator ( ) - setup bias current
*
* @ madc : pointer to twl4030_madc_data struct
* @ chan : can be one of the two values :
2015-05-28 22:50:19 +03:00
* 0 - Enables bias current for main battery type reading
* 1 - Enables bias current for main battery temperature sensing
2014-03-16 05:43:27 +04:00
* @ on : enable or disable chan .
*
2011-03-01 16:42:26 +03:00
* Function to enable or disable bias current for
* main battery type reading or temperature sensing
*/
static int twl4030_madc_set_current_generator ( struct twl4030_madc_data * madc ,
int chan , int on )
{
int ret ;
2014-03-16 05:43:27 +04:00
int regmask ;
2011-03-01 16:42:26 +03:00
u8 regval ;
2012-11-13 12:28:51 +04:00
ret = twl_i2c_read_u8 ( TWL_MODULE_MAIN_CHARGE ,
2011-03-01 16:42:26 +03:00
& regval , TWL4030_BCI_BCICTL1 ) ;
if ( ret ) {
dev_err ( madc - > dev , " unable to read BCICTL1 reg 0x%X " ,
TWL4030_BCI_BCICTL1 ) ;
return ret ;
}
2014-03-16 05:43:27 +04:00
regmask = chan ? TWL4030_BCI_ITHEN : TWL4030_BCI_TYPEN ;
2011-03-01 16:42:26 +03:00
if ( on )
2014-03-16 05:43:27 +04:00
regval | = regmask ;
2011-03-01 16:42:26 +03:00
else
2014-03-16 05:43:27 +04:00
regval & = ~ regmask ;
2012-11-13 12:28:51 +04:00
ret = twl_i2c_write_u8 ( TWL_MODULE_MAIN_CHARGE ,
2011-03-01 16:42:26 +03:00
regval , TWL4030_BCI_BCICTL1 ) ;
if ( ret ) {
dev_err ( madc - > dev , " unable to write BCICTL1 reg 0x%X \n " ,
TWL4030_BCI_BCICTL1 ) ;
return ret ;
}
return 0 ;
}
/*
* Function that sets MADC software power on bit to enable MADC
* @ madc - pointer to twl4030_madc_data struct
2014-03-16 05:43:27 +04:00
* @ on - Enable or disable MADC software power on bit .
2011-03-01 16:42:26 +03:00
* returns error if i2c read / write fails else 0
*/
static int twl4030_madc_set_power ( struct twl4030_madc_data * madc , int on )
{
u8 regval ;
int ret ;
2012-11-13 12:28:51 +04:00
ret = twl_i2c_read_u8 ( TWL_MODULE_MAIN_CHARGE ,
2011-03-01 16:42:26 +03:00
& regval , TWL4030_MADC_CTRL1 ) ;
if ( ret ) {
dev_err ( madc - > dev , " unable to read madc ctrl1 reg 0x%X \n " ,
TWL4030_MADC_CTRL1 ) ;
return ret ;
}
if ( on )
regval | = TWL4030_MADC_MADCON ;
else
regval & = ~ TWL4030_MADC_MADCON ;
ret = twl_i2c_write_u8 ( TWL4030_MODULE_MADC , regval , TWL4030_MADC_CTRL1 ) ;
if ( ret ) {
dev_err ( madc - > dev , " unable to write madc ctrl1 reg 0x%X \n " ,
TWL4030_MADC_CTRL1 ) ;
return ret ;
}
return 0 ;
}
/*
* Initialize MADC and request for threaded irq
*/
2012-11-19 22:23:04 +04:00
static int twl4030_madc_probe ( struct platform_device * pdev )
2011-03-01 16:42:26 +03:00
{
struct twl4030_madc_data * madc ;
2013-07-30 12:10:05 +04:00
struct twl4030_madc_platform_data * pdata = dev_get_platdata ( & pdev - > dev ) ;
2014-03-16 05:43:26 +04:00
struct device_node * np = pdev - > dev . of_node ;
2014-03-16 05:43:25 +04:00
int irq , ret ;
2011-03-01 16:42:26 +03:00
u8 regval ;
2014-03-16 05:43:26 +04:00
struct iio_dev * iio_dev = NULL ;
2011-03-01 16:42:26 +03:00
2014-03-16 05:43:26 +04:00
if ( ! pdata & & ! np ) {
dev_err ( & pdev - > dev , " neither platform data nor Device Tree node available \n " ) ;
2011-03-01 16:42:26 +03:00
return - EINVAL ;
}
2014-03-16 05:43:26 +04:00
iio_dev = devm_iio_device_alloc ( & pdev - > dev , sizeof ( * madc ) ) ;
if ( ! iio_dev ) {
dev_err ( & pdev - > dev , " failed allocating iio device \n " ) ;
2011-03-01 16:42:26 +03:00
return - ENOMEM ;
2014-03-16 05:43:26 +04:00
}
2011-03-01 16:42:26 +03:00
2014-03-16 05:43:26 +04:00
madc = iio_priv ( iio_dev ) ;
2011-08-12 07:33:12 +04:00
madc - > dev = & pdev - > dev ;
2014-03-16 05:43:26 +04:00
iio_dev - > name = dev_name ( & pdev - > dev ) ;
iio_dev - > info = & twl4030_madc_iio_info ;
iio_dev - > modes = INDIO_DIRECT_MODE ;
iio_dev - > channels = twl4030_madc_iio_channels ;
iio_dev - > num_channels = ARRAY_SIZE ( twl4030_madc_iio_channels ) ;
2011-03-01 16:42:26 +03:00
/*
* Phoenix provides 2 interrupt lines . The first one is connected to
* the OMAP . The other one can be connected to the other processor such
* as modem . Hence two separate ISR and IMR registers .
*/
2014-03-16 05:43:26 +04:00
if ( pdata )
madc - > use_second_irq = ( pdata - > irq_line ! = 1 ) ;
else
madc - > use_second_irq = of_property_read_bool ( np ,
" ti,system-uses-second-madc-irq " ) ;
madc - > imr = madc - > use_second_irq ? TWL4030_MADC_IMR2 :
TWL4030_MADC_IMR1 ;
madc - > isr = madc - > use_second_irq ? TWL4030_MADC_ISR2 :
TWL4030_MADC_ISR1 ;
2011-03-01 16:42:26 +03:00
ret = twl4030_madc_set_power ( madc , 1 ) ;
if ( ret < 0 )
2014-03-16 05:43:25 +04:00
return ret ;
2011-03-01 16:42:26 +03:00
ret = twl4030_madc_set_current_generator ( madc , 0 , 1 ) ;
if ( ret < 0 )
goto err_current_generator ;
2012-11-13 12:28:51 +04:00
ret = twl_i2c_read_u8 ( TWL_MODULE_MAIN_CHARGE ,
2011-03-01 16:42:26 +03:00
& regval , TWL4030_BCI_BCICTL1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " unable to read reg BCI CTL1 0x%X \n " ,
TWL4030_BCI_BCICTL1 ) ;
goto err_i2c ;
}
regval | = TWL4030_BCI_MESBAT ;
2012-11-13 12:28:51 +04:00
ret = twl_i2c_write_u8 ( TWL_MODULE_MAIN_CHARGE ,
2011-03-01 16:42:26 +03:00
regval , TWL4030_BCI_BCICTL1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " unable to write reg BCI Ctl1 0x%X \n " ,
TWL4030_BCI_BCICTL1 ) ;
goto err_i2c ;
}
2011-08-12 07:33:13 +04:00
/* Check that MADC clock is on */
ret = twl_i2c_read_u8 ( TWL4030_MODULE_INTBR , & regval , TWL4030_REG_GPBR1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " unable to read reg GPBR1 0x%X \n " ,
TWL4030_REG_GPBR1 ) ;
goto err_i2c ;
}
/* If MADC clk is not on, turn it on */
if ( ! ( regval & TWL4030_GPBR1_MADC_HFCLK_EN ) ) {
dev_info ( & pdev - > dev , " clk disabled, enabling \n " ) ;
regval | = TWL4030_GPBR1_MADC_HFCLK_EN ;
ret = twl_i2c_write_u8 ( TWL4030_MODULE_INTBR , regval ,
TWL4030_REG_GPBR1 ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " unable to write reg GPBR1 0x%X \n " ,
TWL4030_REG_GPBR1 ) ;
goto err_i2c ;
}
}
2014-03-16 05:43:26 +04:00
platform_set_drvdata ( pdev , iio_dev ) ;
2011-03-01 16:42:26 +03:00
mutex_init ( & madc - > lock ) ;
2014-03-16 05:43:25 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
ret = devm_request_threaded_irq ( & pdev - > dev , irq , NULL ,
2011-03-01 16:42:26 +03:00
twl4030_madc_threaded_irq_handler ,
2015-05-24 23:39:04 +03:00
IRQF_TRIGGER_RISING | IRQF_ONESHOT ,
" twl4030_madc " , madc ) ;
2011-03-01 16:42:26 +03:00
if ( ret ) {
2014-03-16 05:43:26 +04:00
dev_err ( & pdev - > dev , " could not request irq \n " ) ;
2013-05-06 08:08:08 +04:00
goto err_i2c ;
2011-03-01 16:42:26 +03:00
}
twl4030_madc = madc ;
2014-03-16 05:43:26 +04:00
2015-08-04 21:15:48 +03:00
/* Configure MADC[3:6] */
ret = twl_i2c_read_u8 ( TWL_MODULE_USB , & regval ,
TWL4030_USB_CARKIT_ANA_CTRL ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " unable to read reg CARKIT_ANA_CTRL 0x%X \n " ,
TWL4030_USB_CARKIT_ANA_CTRL ) ;
goto err_i2c ;
}
regval | = TWL4030_USB_SEL_MADC_MCPC ;
ret = twl_i2c_write_u8 ( TWL_MODULE_USB , regval ,
TWL4030_USB_CARKIT_ANA_CTRL ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " unable to write reg CARKIT_ANA_CTRL 0x%X \n " ,
TWL4030_USB_CARKIT_ANA_CTRL ) ;
goto err_i2c ;
}
/* Enable 3v1 bias regulator for MADC[3:6] */
madc - > usb3v1 = devm_regulator_get ( madc - > dev , " vusb3v1 " ) ;
2017-09-23 09:06:18 +03:00
if ( IS_ERR ( madc - > usb3v1 ) ) {
ret = - ENODEV ;
goto err_i2c ;
}
2015-08-04 21:15:48 +03:00
ret = regulator_enable ( madc - > usb3v1 ) ;
2017-09-23 09:06:20 +03:00
if ( ret ) {
2015-08-04 21:15:48 +03:00
dev_err ( madc - > dev , " could not enable 3v1 bias regulator \n " ) ;
2017-09-23 09:06:20 +03:00
goto err_i2c ;
}
2015-08-04 21:15:48 +03:00
2014-03-16 05:43:26 +04:00
ret = iio_device_register ( iio_dev ) ;
if ( ret ) {
dev_err ( & pdev - > dev , " could not register iio device \n " ) ;
2017-09-23 09:06:19 +03:00
goto err_usb3v1 ;
2014-03-16 05:43:26 +04:00
}
2011-03-01 16:42:26 +03:00
return 0 ;
2014-03-16 05:43:26 +04:00
2017-09-23 09:06:19 +03:00
err_usb3v1 :
regulator_disable ( madc - > usb3v1 ) ;
2011-03-01 16:42:26 +03:00
err_i2c :
twl4030_madc_set_current_generator ( madc , 0 , 0 ) ;
err_current_generator :
twl4030_madc_set_power ( madc , 0 ) ;
return ret ;
}
2012-11-19 22:26:01 +04:00
static int twl4030_madc_remove ( struct platform_device * pdev )
2011-03-01 16:42:26 +03:00
{
2014-03-16 05:43:26 +04:00
struct iio_dev * iio_dev = platform_get_drvdata ( pdev ) ;
struct twl4030_madc_data * madc = iio_priv ( iio_dev ) ;
iio_device_unregister ( iio_dev ) ;
2011-03-01 16:42:26 +03:00
twl4030_madc_set_current_generator ( madc , 0 , 0 ) ;
twl4030_madc_set_power ( madc , 0 ) ;
2015-08-04 21:15:48 +03:00
regulator_disable ( madc - > usb3v1 ) ;
2011-03-01 16:42:26 +03:00
return 0 ;
}
2014-03-16 05:43:26 +04:00
# ifdef CONFIG_OF
static const struct of_device_id twl_madc_of_match [ ] = {
{ . compatible = " ti,twl4030-madc " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , twl_madc_of_match ) ;
# endif
2011-03-01 16:42:26 +03:00
static struct platform_driver twl4030_madc_driver = {
. probe = twl4030_madc_probe ,
2013-03-15 01:56:38 +04:00
. remove = twl4030_madc_remove ,
2011-03-01 16:42:26 +03:00
. driver = {
. name = " twl4030_madc " ,
2014-03-16 05:43:26 +04:00
. of_match_table = of_match_ptr ( twl_madc_of_match ) ,
2014-03-16 05:43:27 +04:00
} ,
2011-03-01 16:42:26 +03:00
} ;
2011-11-24 02:58:34 +04:00
module_platform_driver ( twl4030_madc_driver ) ;
2011-03-01 16:42:26 +03:00
MODULE_DESCRIPTION ( " TWL4030 ADC driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_AUTHOR ( " J Keerthy " ) ;
2011-03-07 06:02:29 +03:00
MODULE_ALIAS ( " platform:twl4030_madc " ) ;