2009-08-19 03:40:28 +04:00
/*
2009-11-30 02:53:17 +03:00
* Copyright 2009 Pengutronix
* Uwe Kleine - Koenig < u . kleine - koenig @ pengutronix . de >
2009-08-19 03:40:28 +04:00
*
2009-11-30 02:53:17 +03:00
* loosely based on an earlier driver that has
* Copyright 2009 Pengutronix , Sascha Hauer < s . hauer @ pengutronix . de >
2009-08-19 03:40:28 +04:00
*
2009-11-30 02:53:17 +03: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 .
2009-08-19 03:40:28 +04:00
*/
# include <linux/module.h>
2009-11-30 02:53:17 +03:00
# include <linux/spi/spi.h>
# include <linux/mfd/core.h>
# include <linux/mfd/mc13783-private.h>
# define MC13783_IRQSTAT0 0
# define MC13783_IRQSTAT0_ADCDONEI (1 << 0)
# define MC13783_IRQSTAT0_ADCBISDONEI (1 << 1)
# define MC13783_IRQSTAT0_TSI (1 << 2)
# define MC13783_IRQSTAT0_WHIGHI (1 << 3)
# define MC13783_IRQSTAT0_WLOWI (1 << 4)
# define MC13783_IRQSTAT0_CHGDETI (1 << 6)
# define MC13783_IRQSTAT0_CHGOVI (1 << 7)
# define MC13783_IRQSTAT0_CHGREVI (1 << 8)
# define MC13783_IRQSTAT0_CHGSHORTI (1 << 9)
# define MC13783_IRQSTAT0_CCCVI (1 << 10)
# define MC13783_IRQSTAT0_CHGCURRI (1 << 11)
# define MC13783_IRQSTAT0_BPONI (1 << 12)
# define MC13783_IRQSTAT0_LOBATLI (1 << 13)
# define MC13783_IRQSTAT0_LOBATHI (1 << 14)
# define MC13783_IRQSTAT0_UDPI (1 << 15)
# define MC13783_IRQSTAT0_USBI (1 << 16)
# define MC13783_IRQSTAT0_IDI (1 << 19)
# define MC13783_IRQSTAT0_SE1I (1 << 21)
# define MC13783_IRQSTAT0_CKDETI (1 << 22)
# define MC13783_IRQSTAT0_UDMI (1 << 23)
# define MC13783_IRQMASK0 1
# define MC13783_IRQMASK0_ADCDONEM MC13783_IRQSTAT0_ADCDONEI
# define MC13783_IRQMASK0_ADCBISDONEM MC13783_IRQSTAT0_ADCBISDONEI
# define MC13783_IRQMASK0_TSM MC13783_IRQSTAT0_TSI
# define MC13783_IRQMASK0_WHIGHM MC13783_IRQSTAT0_WHIGHI
# define MC13783_IRQMASK0_WLOWM MC13783_IRQSTAT0_WLOWI
# define MC13783_IRQMASK0_CHGDETM MC13783_IRQSTAT0_CHGDETI
# define MC13783_IRQMASK0_CHGOVM MC13783_IRQSTAT0_CHGOVI
# define MC13783_IRQMASK0_CHGREVM MC13783_IRQSTAT0_CHGREVI
# define MC13783_IRQMASK0_CHGSHORTM MC13783_IRQSTAT0_CHGSHORTI
# define MC13783_IRQMASK0_CCCVM MC13783_IRQSTAT0_CCCVI
# define MC13783_IRQMASK0_CHGCURRM MC13783_IRQSTAT0_CHGCURRI
# define MC13783_IRQMASK0_BPONM MC13783_IRQSTAT0_BPONI
# define MC13783_IRQMASK0_LOBATLM MC13783_IRQSTAT0_LOBATLI
# define MC13783_IRQMASK0_LOBATHM MC13783_IRQSTAT0_LOBATHI
# define MC13783_IRQMASK0_UDPM MC13783_IRQSTAT0_UDPI
# define MC13783_IRQMASK0_USBM MC13783_IRQSTAT0_USBI
# define MC13783_IRQMASK0_IDM MC13783_IRQSTAT0_IDI
# define MC13783_IRQMASK0_SE1M MC13783_IRQSTAT0_SE1I
# define MC13783_IRQMASK0_CKDETM MC13783_IRQSTAT0_CKDETI
# define MC13783_IRQMASK0_UDMM MC13783_IRQSTAT0_UDMI
# define MC13783_IRQSTAT1 3
# define MC13783_IRQSTAT1_1HZI (1 << 0)
# define MC13783_IRQSTAT1_TODAI (1 << 1)
# define MC13783_IRQSTAT1_ONOFD1I (1 << 3)
# define MC13783_IRQSTAT1_ONOFD2I (1 << 4)
# define MC13783_IRQSTAT1_ONOFD3I (1 << 5)
# define MC13783_IRQSTAT1_SYSRSTI (1 << 6)
# define MC13783_IRQSTAT1_RTCRSTI (1 << 7)
# define MC13783_IRQSTAT1_PCI (1 << 8)
# define MC13783_IRQSTAT1_WARMI (1 << 9)
# define MC13783_IRQSTAT1_MEMHLDI (1 << 10)
# define MC13783_IRQSTAT1_PWRRDYI (1 << 11)
# define MC13783_IRQSTAT1_THWARNLI (1 << 12)
# define MC13783_IRQSTAT1_THWARNHI (1 << 13)
# define MC13783_IRQSTAT1_CLKI (1 << 14)
# define MC13783_IRQSTAT1_SEMAFI (1 << 15)
# define MC13783_IRQSTAT1_MC2BI (1 << 17)
# define MC13783_IRQSTAT1_HSDETI (1 << 18)
# define MC13783_IRQSTAT1_HSLI (1 << 19)
# define MC13783_IRQSTAT1_ALSPTHI (1 << 20)
# define MC13783_IRQSTAT1_AHSSHORTI (1 << 21)
# define MC13783_IRQMASK1 4
# define MC13783_IRQMASK1_1HZM MC13783_IRQSTAT1_1HZI
# define MC13783_IRQMASK1_TODAM MC13783_IRQSTAT1_TODAI
# define MC13783_IRQMASK1_ONOFD1M MC13783_IRQSTAT1_ONOFD1I
# define MC13783_IRQMASK1_ONOFD2M MC13783_IRQSTAT1_ONOFD2I
# define MC13783_IRQMASK1_ONOFD3M MC13783_IRQSTAT1_ONOFD3I
# define MC13783_IRQMASK1_SYSRSTM MC13783_IRQSTAT1_SYSRSTI
# define MC13783_IRQMASK1_RTCRSTM MC13783_IRQSTAT1_RTCRSTI
# define MC13783_IRQMASK1_PCM MC13783_IRQSTAT1_PCI
# define MC13783_IRQMASK1_WARMM MC13783_IRQSTAT1_WARMI
# define MC13783_IRQMASK1_MEMHLDM MC13783_IRQSTAT1_MEMHLDI
# define MC13783_IRQMASK1_PWRRDYM MC13783_IRQSTAT1_PWRRDYI
# define MC13783_IRQMASK1_THWARNLM MC13783_IRQSTAT1_THWARNLI
# define MC13783_IRQMASK1_THWARNHM MC13783_IRQSTAT1_THWARNHI
# define MC13783_IRQMASK1_CLKM MC13783_IRQSTAT1_CLKI
# define MC13783_IRQMASK1_SEMAFM MC13783_IRQSTAT1_SEMAFI
# define MC13783_IRQMASK1_MC2BM MC13783_IRQSTAT1_MC2BI
# define MC13783_IRQMASK1_HSDETM MC13783_IRQSTAT1_HSDETI
# define MC13783_IRQMASK1_HSLM MC13783_IRQSTAT1_HSLI
# define MC13783_IRQMASK1_ALSPTHM MC13783_IRQSTAT1_ALSPTHI
# define MC13783_IRQMASK1_AHSSHORTM MC13783_IRQSTAT1_AHSSHORTI
# define MC13783_ADC1 44
# define MC13783_ADC1_ADEN (1 << 0)
# define MC13783_ADC1_RAND (1 << 1)
# define MC13783_ADC1_ADSEL (1 << 3)
# define MC13783_ADC1_ASC (1 << 20)
# define MC13783_ADC1_ADTRIGIGN (1 << 21)
# define MC13783_NUMREGS 0x3f
void mc13783_lock ( struct mc13783 * mc13783 )
{
if ( ! mutex_trylock ( & mc13783 - > lock ) ) {
dev_dbg ( & mc13783 - > spidev - > dev , " wait for %s from %pf \n " ,
__func__ , __builtin_return_address ( 0 ) ) ;
mutex_lock ( & mc13783 - > lock ) ;
}
dev_dbg ( & mc13783 - > spidev - > dev , " %s from %pf \n " ,
__func__ , __builtin_return_address ( 0 ) ) ;
}
EXPORT_SYMBOL ( mc13783_lock ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
void mc13783_unlock ( struct mc13783 * mc13783 )
{
dev_dbg ( & mc13783 - > spidev - > dev , " %s from %pf \n " ,
__func__ , __builtin_return_address ( 0 ) ) ;
mutex_unlock ( & mc13783 - > lock ) ;
}
EXPORT_SYMBOL ( mc13783_unlock ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
# define MC13783_REGOFFSET_SHIFT 25
int mc13783_reg_read ( struct mc13783 * mc13783 , unsigned int offset , u32 * val )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
struct spi_transfer t ;
2009-08-19 03:40:28 +04:00
struct spi_message m ;
2009-11-30 02:53:17 +03:00
int ret ;
BUG_ON ( ! mutex_is_locked ( & mc13783 - > lock ) ) ;
if ( offset > MC13783_NUMREGS )
return - EINVAL ;
* val = offset < < MC13783_REGOFFSET_SHIFT ;
memset ( & t , 0 , sizeof ( t ) ) ;
t . tx_buf = val ;
t . rx_buf = val ;
t . len = sizeof ( u32 ) ;
2009-08-19 03:40:28 +04:00
spi_message_init ( & m ) ;
spi_message_add_tail ( & t , & m ) ;
2009-11-30 02:53:17 +03:00
ret = spi_sync ( mc13783 - > spidev , & m ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
/* error in message.status implies error return from spi_sync */
BUG_ON ( ! ret & & m . status ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
if ( ret )
return ret ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
* val & = 0xffffff ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
dev_vdbg ( & mc13783 - > spidev - > dev , " [0x%02x] -> 0x%06x \n " , offset , * val ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
return 0 ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
EXPORT_SYMBOL ( mc13783_reg_read ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
int mc13783_reg_write ( struct mc13783 * mc13783 , unsigned int offset , u32 val )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
u32 buf ;
struct spi_transfer t ;
struct spi_message m ;
int ret ;
BUG_ON ( ! mutex_is_locked ( & mc13783 - > lock ) ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
dev_vdbg ( & mc13783 - > spidev - > dev , " [0x%02x] <- 0x%06x \n " , offset , val ) ;
if ( offset > MC13783_NUMREGS | | val > 0xffffff )
2009-08-19 03:40:28 +04:00
return - EINVAL ;
2009-11-30 02:53:17 +03:00
buf = 1 < < 31 | offset < < MC13783_REGOFFSET_SHIFT | val ;
memset ( & t , 0 , sizeof ( t ) ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
t . tx_buf = & buf ;
t . rx_buf = & buf ;
t . len = sizeof ( u32 ) ;
spi_message_init ( & m ) ;
spi_message_add_tail ( & t , & m ) ;
ret = spi_sync ( mc13783 - > spidev , & m ) ;
BUG_ON ( ! ret & & m . status ) ;
if ( ret )
return ret ;
return 0 ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
EXPORT_SYMBOL ( mc13783_reg_write ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
int mc13783_reg_rmw ( struct mc13783 * mc13783 , unsigned int offset ,
u32 mask , u32 val )
2009-08-19 03:40:28 +04:00
{
int ret ;
2009-11-30 02:53:17 +03:00
u32 valread ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
BUG_ON ( val & ~ mask ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
ret = mc13783_reg_read ( mc13783 , offset , & valread ) ;
if ( ret )
return ret ;
valread = ( valread & ~ mask ) | val ;
return mc13783_reg_write ( mc13783 , offset , valread ) ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
EXPORT_SYMBOL ( mc13783_reg_rmw ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
int mc13783_mask ( struct mc13783 * mc13783 , int irq )
2009-08-19 03:40:28 +04:00
{
int ret ;
2009-11-30 02:53:17 +03:00
unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1 ;
u32 irqbit = 1 < < ( irq < 24 ? irq : irq - 24 ) ;
u32 mask ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
if ( irq < 0 | | irq > = MC13783_NUM_IRQ )
return - EINVAL ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
ret = mc13783_reg_read ( mc13783 , offmask , & mask ) ;
if ( ret )
return ret ;
if ( mask & irqbit )
/* already masked */
return 0 ;
return mc13783_reg_write ( mc13783 , offmask , mask | irqbit ) ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
EXPORT_SYMBOL ( mc13783_mask ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
int mc13783_unmask ( struct mc13783 * mc13783 , int irq )
2009-08-19 03:40:28 +04:00
{
int ret ;
2009-11-30 02:53:17 +03:00
unsigned int offmask = irq < 24 ? MC13783_IRQMASK0 : MC13783_IRQMASK1 ;
u32 irqbit = 1 < < ( irq < 24 ? irq : irq - 24 ) ;
u32 mask ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
if ( irq < 0 | | irq > = MC13783_NUM_IRQ )
return - EINVAL ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
ret = mc13783_reg_read ( mc13783 , offmask , & mask ) ;
if ( ret )
return ret ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
if ( ! ( mask & irqbit ) )
/* already unmasked */
return 0 ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
return mc13783_reg_write ( mc13783 , offmask , mask & ~ irqbit ) ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
EXPORT_SYMBOL ( mc13783_unmask ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
int mc13783_irq_request_nounmask ( struct mc13783 * mc13783 , int irq ,
irq_handler_t handler , const char * name , void * dev )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
BUG_ON ( ! mutex_is_locked ( & mc13783 - > lock ) ) ;
BUG_ON ( ! handler ) ;
if ( irq < 0 | | irq > = MC13783_NUM_IRQ )
2009-08-19 03:40:28 +04:00
return - EINVAL ;
2009-11-30 02:53:17 +03:00
if ( mc13783 - > irqhandler [ irq ] )
2009-08-19 03:40:28 +04:00
return - EBUSY ;
2009-11-30 02:53:17 +03:00
mc13783 - > irqhandler [ irq ] = handler ;
mc13783 - > irqdata [ irq ] = dev ;
2009-08-19 03:40:28 +04:00
return 0 ;
}
2009-11-30 02:53:17 +03:00
EXPORT_SYMBOL ( mc13783_irq_request_nounmask ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
int mc13783_irq_request ( struct mc13783 * mc13783 , int irq ,
irq_handler_t handler , const char * name , void * dev )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
int ret ;
ret = mc13783_irq_request_nounmask ( mc13783 , irq , handler , name , dev ) ;
if ( ret )
return ret ;
ret = mc13783_unmask ( mc13783 , irq ) ;
if ( ret ) {
mc13783 - > irqhandler [ irq ] = NULL ;
mc13783 - > irqdata [ irq ] = NULL ;
return ret ;
}
return 0 ;
}
EXPORT_SYMBOL ( mc13783_irq_request ) ;
int mc13783_irq_free ( struct mc13783 * mc13783 , int irq , void * dev )
{
int ret ;
BUG_ON ( ! mutex_is_locked ( & mc13783 - > lock ) ) ;
if ( irq < 0 | | irq > = MC13783_NUM_IRQ | | ! mc13783 - > irqhandler [ irq ] | |
mc13783 - > irqdata [ irq ] ! = dev )
2009-08-19 03:40:28 +04:00
return - EINVAL ;
2009-11-30 02:53:17 +03:00
ret = mc13783_mask ( mc13783 , irq ) ;
if ( ret )
return ret ;
mc13783 - > irqhandler [ irq ] = NULL ;
mc13783 - > irqdata [ irq ] = NULL ;
2009-08-19 03:40:28 +04:00
return 0 ;
}
2009-11-30 02:53:17 +03:00
EXPORT_SYMBOL ( mc13783_irq_free ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
static inline irqreturn_t mc13783_irqhandler ( struct mc13783 * mc13783 , int irq )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
return mc13783 - > irqhandler [ irq ] ( irq , mc13783 - > irqdata [ irq ] ) ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
int mc13783_ackirq ( struct mc13783 * mc13783 , int irq )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
unsigned int offstat = irq < 24 ? MC13783_IRQSTAT0 : MC13783_IRQSTAT1 ;
unsigned int val = 1 < < ( irq < 24 ? irq : irq - 24 ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
BUG_ON ( irq < 0 | | irq > = MC13783_NUM_IRQ ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
return mc13783_reg_write ( mc13783 , offstat , val ) ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
EXPORT_SYMBOL ( mc13783_ackirq ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
/*
* returns : number of handled irqs or negative error
* locking : holds mc13783 - > lock
*/
static int mc13783_irq_handle ( struct mc13783 * mc13783 ,
unsigned int offstat , unsigned int offmask , int baseirq )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
u32 stat , mask ;
int ret = mc13783_reg_read ( mc13783 , offstat , & stat ) ;
int num_handled = 0 ;
if ( ret )
return ret ;
ret = mc13783_reg_read ( mc13783 , offmask , & mask ) ;
if ( ret )
return ret ;
while ( stat & ~ mask ) {
int irq = __ffs ( stat & ~ mask ) ;
stat & = ~ ( 1 < < irq ) ;
if ( likely ( mc13783 - > irqhandler [ baseirq + irq ] ) ) {
irqreturn_t handled ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
handled = mc13783_irqhandler ( mc13783 , baseirq + irq ) ;
if ( handled = = IRQ_HANDLED )
num_handled + + ;
} else {
dev_err ( & mc13783 - > spidev - > dev ,
" BUG: irq %u but no handler \n " ,
baseirq + irq ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
mask | = 1 < < irq ;
ret = mc13783_reg_write ( mc13783 , offmask , mask ) ;
}
}
return num_handled ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
static irqreturn_t mc13783_irq_thread ( int irq , void * data )
{
struct mc13783 * mc13783 = data ;
irqreturn_t ret ;
int handled = 0 ;
mc13783_lock ( mc13783 ) ;
ret = mc13783_irq_handle ( mc13783 , MC13783_IRQSTAT0 ,
MC13783_IRQMASK0 , MC13783_IRQ_ADCDONE ) ;
if ( ret > 0 )
handled = 1 ;
ret = mc13783_irq_handle ( mc13783 , MC13783_IRQSTAT1 ,
MC13783_IRQMASK1 , MC13783_IRQ_1HZ ) ;
if ( ret > 0 )
handled = 1 ;
mc13783_unlock ( mc13783 ) ;
return IRQ_RETVAL ( handled ) ;
}
# define MC13783_ADC1_CHAN0_SHIFT 5
# define MC13783_ADC1_CHAN1_SHIFT 8
struct mc13783_adcdone_data {
struct mc13783 * mc13783 ;
struct completion done ;
} ;
static irqreturn_t mc13783_handler_adcdone ( int irq , void * data )
{
struct mc13783_adcdone_data * adcdone_data = data ;
mc13783_ackirq ( adcdone_data - > mc13783 , irq ) ;
complete_all ( & adcdone_data - > done ) ;
return IRQ_HANDLED ;
}
# define MC13783_ADC_WORKING (1 << 16)
2009-08-19 03:40:28 +04:00
int mc13783_adc_do_conversion ( struct mc13783 * mc13783 , unsigned int mode ,
unsigned int channel , unsigned int * sample )
{
2009-11-30 02:53:17 +03:00
u32 adc0 , adc1 , old_adc0 ;
int i , ret ;
struct mc13783_adcdone_data adcdone_data = {
. mc13783 = mc13783 ,
} ;
init_completion ( & adcdone_data . done ) ;
dev_dbg ( & mc13783 - > spidev - > dev , " %s \n " , __func__ ) ;
mc13783_lock ( mc13783 ) ;
if ( mc13783 - > flags & MC13783_ADC_WORKING ) {
ret = - EBUSY ;
goto out ;
}
mc13783 - > flags | = MC13783_ADC_WORKING ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
mc13783_reg_read ( mc13783 , MC13783_ADC0 , & old_adc0 ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
adc0 = MC13783_ADC0_ADINC1 | MC13783_ADC0_ADINC2 ;
adc1 = MC13783_ADC1_ADEN | MC13783_ADC1_ADTRIGIGN | MC13783_ADC1_ASC ;
2009-08-19 03:40:28 +04:00
if ( channel > 7 )
2009-11-30 02:53:17 +03:00
adc1 | = MC13783_ADC1_ADSEL ;
2009-08-19 03:40:28 +04:00
switch ( mode ) {
case MC13783_ADC_MODE_TS :
2009-12-02 21:54:31 +03:00
adc0 | = MC13783_ADC0_ADREFEN | MC13783_ADC0_TSMOD0 |
MC13783_ADC0_TSMOD1 ;
2009-11-30 02:53:17 +03:00
adc1 | = 4 < < MC13783_ADC1_CHAN1_SHIFT ;
2009-08-19 03:40:28 +04:00
break ;
2009-11-30 02:53:17 +03:00
2009-08-19 03:40:28 +04:00
case MC13783_ADC_MODE_SINGLE_CHAN :
2009-11-30 02:53:17 +03:00
adc0 | = old_adc0 & MC13783_ADC0_TSMOD_MASK ;
adc1 | = ( channel & 0x7 ) < < MC13783_ADC1_CHAN0_SHIFT ;
adc1 | = MC13783_ADC1_RAND ;
2009-08-19 03:40:28 +04:00
break ;
2009-11-30 02:53:17 +03:00
2009-08-19 03:40:28 +04:00
case MC13783_ADC_MODE_MULT_CHAN :
2009-11-30 02:53:17 +03:00
adc0 | = old_adc0 & MC13783_ADC0_TSMOD_MASK ;
adc1 | = 4 < < MC13783_ADC1_CHAN1_SHIFT ;
2009-08-19 03:40:28 +04:00
break ;
2009-11-30 02:53:17 +03:00
2009-08-19 03:40:28 +04:00
default :
2009-11-30 02:53:17 +03:00
mc13783_unlock ( mc13783 ) ;
2009-08-19 03:40:28 +04:00
return - EINVAL ;
}
2009-11-30 02:53:17 +03:00
dev_dbg ( & mc13783 - > spidev - > dev , " %s: request irq \n " , __func__ ) ;
mc13783_irq_request ( mc13783 , MC13783_IRQ_ADCDONE ,
mc13783_handler_adcdone , __func__ , & adcdone_data ) ;
mc13783_ackirq ( mc13783 , MC13783_IRQ_ADCDONE ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
mc13783_reg_write ( mc13783 , MC13783_REG_ADC_0 , adc0 ) ;
mc13783_reg_write ( mc13783 , MC13783_REG_ADC_1 , adc1 ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
mc13783_unlock ( mc13783 ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
ret = wait_for_completion_interruptible_timeout ( & adcdone_data . done , HZ ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
if ( ! ret )
ret = - ETIMEDOUT ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
mc13783_lock ( mc13783 ) ;
mc13783_irq_free ( mc13783 , MC13783_IRQ_ADCDONE , & adcdone_data ) ;
if ( ret > 0 )
for ( i = 0 ; i < 4 ; + + i ) {
ret = mc13783_reg_read ( mc13783 ,
MC13783_REG_ADC_2 , & sample [ i ] ) ;
if ( ret )
break ;
}
if ( mode = = MC13783_ADC_MODE_TS )
/* restore TSMOD */
mc13783_reg_write ( mc13783 , MC13783_REG_ADC_0 , old_adc0 ) ;
mc13783 - > flags & = ~ MC13783_ADC_WORKING ;
out :
mc13783_unlock ( mc13783 ) ;
return ret ;
2009-08-19 03:40:28 +04:00
}
EXPORT_SYMBOL_GPL ( mc13783_adc_do_conversion ) ;
2009-11-30 02:53:17 +03:00
static int mc13783_add_subdevice_pdata ( struct mc13783 * mc13783 ,
const char * name , void * pdata , size_t pdata_size )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
struct mfd_cell cell = {
. name = name ,
. platform_data = pdata ,
. data_size = pdata_size ,
} ;
return mfd_add_devices ( & mc13783 - > spidev - > dev , - 1 , & cell , 1 , NULL , 0 ) ;
}
static int mc13783_add_subdevice ( struct mc13783 * mc13783 , const char * name )
{
return mc13783_add_subdevice_pdata ( mc13783 , name , NULL , 0 ) ;
2009-08-19 03:40:28 +04:00
}
static int mc13783_check_revision ( struct mc13783 * mc13783 )
{
u32 rev_id , rev1 , rev2 , finid , icid ;
2009-11-30 02:53:17 +03:00
mc13783_reg_read ( mc13783 , MC13783_REG_REVISION , & rev_id ) ;
2009-08-19 03:40:28 +04:00
rev1 = ( rev_id & 0x018 ) > > 3 ;
rev2 = ( rev_id & 0x007 ) ;
icid = ( rev_id & 0x01C0 ) > > 6 ;
finid = ( rev_id & 0x01E00 ) > > 9 ;
/* Ver 0.2 is actually 3.2a. Report as 3.2 */
if ( ( rev1 = = 0 ) & & ( rev2 = = 2 ) )
rev1 = 3 ;
if ( rev1 = = 0 | | icid ! = 2 ) {
2009-11-30 02:53:17 +03:00
dev_err ( & mc13783 - > spidev - > dev , " No MC13783 detected. \n " ) ;
2009-08-19 03:40:28 +04:00
return - ENODEV ;
}
2009-11-30 02:53:17 +03:00
dev_info ( & mc13783 - > spidev - > dev ,
" MC13783 Rev %d.%d FinVer %x detected \n " ,
rev1 , rev2 , finid ) ;
2009-08-19 03:40:28 +04:00
return 0 ;
}
2009-11-30 02:53:17 +03:00
static int mc13783_probe ( struct spi_device * spi )
2009-08-19 03:40:28 +04:00
{
struct mc13783 * mc13783 ;
2009-11-30 02:53:17 +03:00
struct mc13783_platform_data * pdata = dev_get_platdata ( & spi - > dev ) ;
2009-08-19 03:40:28 +04:00
int ret ;
2009-11-30 02:53:17 +03:00
mc13783 = kzalloc ( sizeof ( * mc13783 ) , GFP_KERNEL ) ;
2009-08-19 03:40:28 +04:00
if ( ! mc13783 )
return - ENOMEM ;
dev_set_drvdata ( & spi - > dev , mc13783 ) ;
spi - > mode = SPI_MODE_0 | SPI_CS_HIGH ;
spi - > bits_per_word = 32 ;
spi_setup ( spi ) ;
2009-11-30 02:53:17 +03:00
mc13783 - > spidev = spi ;
mutex_init ( & mc13783 - > lock ) ;
mc13783_lock ( mc13783 ) ;
ret = mc13783_check_revision ( mc13783 ) ;
if ( ret )
goto err_revision ;
/* mask all irqs */
ret = mc13783_reg_write ( mc13783 , MC13783_IRQMASK0 , 0x00ffffff ) ;
if ( ret )
goto err_mask ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
ret = mc13783_reg_write ( mc13783 , MC13783_IRQMASK1 , 0x00ffffff ) ;
if ( ret )
goto err_mask ;
ret = request_threaded_irq ( spi - > irq , NULL , mc13783_irq_thread ,
IRQF_ONESHOT | IRQF_TRIGGER_HIGH , " mc13783 " , mc13783 ) ;
if ( ret ) {
err_mask :
err_revision :
mutex_unlock ( & mc13783 - > lock ) ;
dev_set_drvdata ( & spi - > dev , NULL ) ;
kfree ( mc13783 ) ;
return ret ;
}
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
/* This should go away (BEGIN) */
2009-08-19 03:40:28 +04:00
if ( pdata ) {
mc13783 - > flags = pdata - > flags ;
mc13783 - > regulators = pdata - > regulators ;
mc13783 - > num_regulators = pdata - > num_regulators ;
}
2009-11-30 02:53:17 +03:00
/* This should go away (END) */
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
if ( pdata - > flags & MC13783_USE_ADC )
mc13783_add_subdevice ( mc13783 , " mc13783-adc " ) ;
if ( pdata - > flags & MC13783_USE_CODEC )
mc13783_add_subdevice ( mc13783 , " mc13783-codec " ) ;
if ( pdata - > flags & MC13783_USE_REGULATOR ) {
struct mc13783_regulator_platform_data regulator_pdata = {
. num_regulators = pdata - > num_regulators ,
. regulators = pdata - > regulators ,
} ;
mc13783_add_subdevice_pdata ( mc13783 , " mc13783-regulator " ,
& regulator_pdata , sizeof ( regulator_pdata ) ) ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
if ( pdata - > flags & MC13783_USE_RTC )
mc13783_add_subdevice ( mc13783 , " mc13783-rtc " ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
if ( pdata - > flags & MC13783_USE_TOUCHSCREEN )
mc13783_add_subdevice ( mc13783 , " mc13783-ts " ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
mc13783_unlock ( mc13783 ) ;
2009-08-19 03:40:28 +04:00
return 0 ;
}
static int __devexit mc13783_remove ( struct spi_device * spi )
{
2009-11-30 02:53:17 +03:00
struct mc13783 * mc13783 = dev_get_drvdata ( & spi - > dev ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
free_irq ( mc13783 - > spidev - > irq , mc13783 ) ;
2009-08-19 03:40:28 +04:00
mfd_remove_devices ( & spi - > dev ) ;
return 0 ;
}
2009-11-30 02:53:17 +03:00
static struct spi_driver mc13783_driver = {
2009-08-19 03:40:28 +04:00
. driver = {
2009-11-30 02:53:17 +03:00
. name = " mc13783 " ,
. bus = & spi_bus_type ,
. owner = THIS_MODULE ,
2009-08-19 03:40:28 +04:00
} ,
. probe = mc13783_probe ,
. remove = __devexit_p ( mc13783_remove ) ,
} ;
2009-11-30 02:53:17 +03:00
static int __init mc13783_init ( void )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
return spi_register_driver ( & mc13783_driver ) ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
subsys_initcall ( mc13783_init ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
static void __exit mc13783_exit ( void )
2009-08-19 03:40:28 +04:00
{
2009-11-30 02:53:17 +03:00
spi_unregister_driver ( & mc13783_driver ) ;
2009-08-19 03:40:28 +04:00
}
2009-11-30 02:53:17 +03:00
module_exit ( mc13783_exit ) ;
2009-08-19 03:40:28 +04:00
2009-11-30 02:53:17 +03:00
MODULE_DESCRIPTION ( " Core driver for Freescale MC13783 PMIC " ) ;
MODULE_AUTHOR ( " Uwe Kleine-Koenig <u.kleine-koenig@pengutronix.de> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;