2010-05-11 01:39:47 +04:00
/*
* Copyright ( C ) ST - Ericsson SA 2010
*
* License Terms : GNU General Public License , version 2
* Author : Hanumath Prasad < hanumath . prasad @ stericsson . com > for ST - Ericsson
* Author : Rabin Vincent < rabin . vincent @ stericsson . com > for ST - Ericsson
*/
# include <linux/module.h>
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/mfd/core.h>
2010-12-13 07:03:12 +03:00
# include <linux/mfd/tc3589x.h>
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:18 +03:00
# define TC3589x_CLKMODE_MODCTL_SLEEP 0x0
# define TC3589x_CLKMODE_MODCTL_OPERATION (1 << 0)
2010-05-11 01:39:47 +04:00
/**
2010-12-13 07:03:14 +03:00
* tc3589x_reg_read ( ) - read a single TC3589x register
* @ tc3589x : Device to read from
2010-05-11 01:39:47 +04:00
* @ reg : Register to read
*/
2010-12-13 07:03:14 +03:00
int tc3589x_reg_read ( struct tc3589x * tc3589x , u8 reg )
2010-05-11 01:39:47 +04:00
{
int ret ;
2010-12-13 07:03:14 +03:00
ret = i2c_smbus_read_byte_data ( tc3589x - > i2c , reg ) ;
2010-05-11 01:39:47 +04:00
if ( ret < 0 )
2010-12-13 07:03:14 +03:00
dev_err ( tc3589x - > dev , " failed to read reg %#x: %d \n " ,
2010-05-11 01:39:47 +04:00
reg , ret ) ;
return ret ;
}
2010-12-13 07:03:14 +03:00
EXPORT_SYMBOL_GPL ( tc3589x_reg_read ) ;
2010-05-11 01:39:47 +04:00
/**
2010-12-13 07:03:14 +03:00
* tc3589x_reg_read ( ) - write a single TC3589x register
* @ tc3589x : Device to write to
2010-05-11 01:39:47 +04:00
* @ reg : Register to read
* @ data : Value to write
*/
2010-12-13 07:03:14 +03:00
int tc3589x_reg_write ( struct tc3589x * tc3589x , u8 reg , u8 data )
2010-05-11 01:39:47 +04:00
{
int ret ;
2010-12-13 07:03:14 +03:00
ret = i2c_smbus_write_byte_data ( tc3589x - > i2c , reg , data ) ;
2010-05-11 01:39:47 +04:00
if ( ret < 0 )
2010-12-13 07:03:14 +03:00
dev_err ( tc3589x - > dev , " failed to write reg %#x: %d \n " ,
2010-05-11 01:39:47 +04:00
reg , ret ) ;
return ret ;
}
2010-12-13 07:03:14 +03:00
EXPORT_SYMBOL_GPL ( tc3589x_reg_write ) ;
2010-05-11 01:39:47 +04:00
/**
2010-12-13 07:03:14 +03:00
* tc3589x_block_read ( ) - read multiple TC3589x registers
* @ tc3589x : Device to read from
2010-05-11 01:39:47 +04:00
* @ reg : First register
* @ length : Number of registers
* @ values : Buffer to write to
*/
2010-12-13 07:03:14 +03:00
int tc3589x_block_read ( struct tc3589x * tc3589x , u8 reg , u8 length , u8 * values )
2010-05-11 01:39:47 +04:00
{
int ret ;
2010-12-13 07:03:14 +03:00
ret = i2c_smbus_read_i2c_block_data ( tc3589x - > i2c , reg , length , values ) ;
2010-05-11 01:39:47 +04:00
if ( ret < 0 )
2010-12-13 07:03:14 +03:00
dev_err ( tc3589x - > dev , " failed to read regs %#x: %d \n " ,
2010-05-11 01:39:47 +04:00
reg , ret ) ;
return ret ;
}
2010-12-13 07:03:14 +03:00
EXPORT_SYMBOL_GPL ( tc3589x_block_read ) ;
2010-05-11 01:39:47 +04:00
/**
2010-12-13 07:03:14 +03:00
* tc3589x_block_write ( ) - write multiple TC3589x registers
* @ tc3589x : Device to write to
2010-05-11 01:39:47 +04:00
* @ reg : First register
* @ length : Number of registers
* @ values : Values to write
*/
2010-12-13 07:03:14 +03:00
int tc3589x_block_write ( struct tc3589x * tc3589x , u8 reg , u8 length ,
2010-05-11 01:39:47 +04:00
const u8 * values )
{
int ret ;
2010-12-13 07:03:14 +03:00
ret = i2c_smbus_write_i2c_block_data ( tc3589x - > i2c , reg , length ,
2010-05-11 01:39:47 +04:00
values ) ;
if ( ret < 0 )
2010-12-13 07:03:14 +03:00
dev_err ( tc3589x - > dev , " failed to write regs %#x: %d \n " ,
2010-05-11 01:39:47 +04:00
reg , ret ) ;
return ret ;
}
2010-12-13 07:03:14 +03:00
EXPORT_SYMBOL_GPL ( tc3589x_block_write ) ;
2010-05-11 01:39:47 +04:00
/**
2010-12-13 07:03:14 +03:00
* tc3589x_set_bits ( ) - set the value of a bitfield in a TC3589x register
* @ tc3589x : Device to write to
2010-05-11 01:39:47 +04:00
* @ reg : Register to write
* @ mask : Mask of bits to set
* @ values : Value to set
*/
2010-12-13 07:03:14 +03:00
int tc3589x_set_bits ( struct tc3589x * tc3589x , u8 reg , u8 mask , u8 val )
2010-05-11 01:39:47 +04:00
{
int ret ;
2010-12-13 07:03:14 +03:00
mutex_lock ( & tc3589x - > lock ) ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:14 +03:00
ret = tc3589x_reg_read ( tc3589x , reg ) ;
2010-05-11 01:39:47 +04:00
if ( ret < 0 )
goto out ;
ret & = ~ mask ;
ret | = val ;
2010-12-13 07:03:14 +03:00
ret = tc3589x_reg_write ( tc3589x , reg , ret ) ;
2010-05-11 01:39:47 +04:00
out :
2010-12-13 07:03:14 +03:00
mutex_unlock ( & tc3589x - > lock ) ;
2010-05-11 01:39:47 +04:00
return ret ;
}
2010-12-13 07:03:14 +03:00
EXPORT_SYMBOL_GPL ( tc3589x_set_bits ) ;
2010-05-11 01:39:47 +04:00
static struct resource gpio_resources [ ] = {
{
2010-12-13 07:03:14 +03:00
. start = TC3589x_INT_GPIIRQ ,
. end = TC3589x_INT_GPIIRQ ,
2010-05-11 01:39:47 +04:00
. flags = IORESOURCE_IRQ ,
} ,
} ;
2010-12-21 13:23:31 +03:00
static struct resource keypad_resources [ ] = {
{
. start = TC3589x_INT_KBDIRQ ,
. end = TC3589x_INT_KBDIRQ ,
. flags = IORESOURCE_IRQ ,
} ,
} ;
2010-12-13 07:03:15 +03:00
static struct mfd_cell tc3589x_dev_gpio [ ] = {
2010-05-11 01:39:47 +04:00
{
2010-12-13 07:03:14 +03:00
. name = " tc3589x-gpio " ,
2010-05-11 01:39:47 +04:00
. num_resources = ARRAY_SIZE ( gpio_resources ) ,
. resources = & gpio_resources [ 0 ] ,
} ,
} ;
2010-12-21 13:23:31 +03:00
static struct mfd_cell tc3589x_dev_keypad [ ] = {
{
. name = " tc3589x-keypad " ,
. num_resources = ARRAY_SIZE ( keypad_resources ) ,
. resources = & keypad_resources [ 0 ] ,
} ,
} ;
2010-12-13 07:03:14 +03:00
static irqreturn_t tc3589x_irq ( int irq , void * data )
2010-05-11 01:39:47 +04:00
{
2010-12-13 07:03:14 +03:00
struct tc3589x * tc3589x = data ;
2010-05-11 01:39:47 +04:00
int status ;
2010-12-13 07:03:16 +03:00
again :
2010-12-13 07:03:14 +03:00
status = tc3589x_reg_read ( tc3589x , TC3589x_IRQST ) ;
2010-05-11 01:39:47 +04:00
if ( status < 0 )
return IRQ_NONE ;
while ( status ) {
int bit = __ffs ( status ) ;
2010-12-13 07:03:14 +03:00
handle_nested_irq ( tc3589x - > irq_base + bit ) ;
2010-05-11 01:39:47 +04:00
status & = ~ ( 1 < < bit ) ;
}
/*
* A dummy read or write ( to any register ) appears to be necessary to
* have the last interrupt clear ( for example , GPIO IC write ) take
2010-12-13 07:03:16 +03:00
* effect . In such a case , recheck for any interrupt which is still
* pending .
2010-05-11 01:39:47 +04:00
*/
2010-12-13 07:03:16 +03:00
status = tc3589x_reg_read ( tc3589x , TC3589x_IRQST ) ;
if ( status )
goto again ;
2010-05-11 01:39:47 +04:00
return IRQ_HANDLED ;
}
2010-12-13 07:03:14 +03:00
static int tc3589x_irq_init ( struct tc3589x * tc3589x )
2010-05-11 01:39:47 +04:00
{
2010-12-13 07:03:14 +03:00
int base = tc3589x - > irq_base ;
2010-05-11 01:39:47 +04:00
int irq ;
2010-12-13 07:03:14 +03:00
for ( irq = base ; irq < base + TC3589x_NR_INTERNAL_IRQS ; irq + + ) {
2011-03-25 14:12:32 +03:00
irq_set_chip_data ( irq , tc3589x ) ;
irq_set_chip_and_handler ( irq , & dummy_irq_chip ,
2010-05-11 01:39:47 +04:00
handle_edge_irq ) ;
2011-03-25 14:12:32 +03:00
irq_set_nested_thread ( irq , 1 ) ;
2010-05-11 01:39:47 +04:00
# ifdef CONFIG_ARM
set_irq_flags ( irq , IRQF_VALID ) ;
# else
2011-03-25 14:12:32 +03:00
irq_set_noprobe ( irq ) ;
2010-05-11 01:39:47 +04:00
# endif
}
return 0 ;
}
2010-12-13 07:03:14 +03:00
static void tc3589x_irq_remove ( struct tc3589x * tc3589x )
2010-05-11 01:39:47 +04:00
{
2010-12-13 07:03:14 +03:00
int base = tc3589x - > irq_base ;
2010-05-11 01:39:47 +04:00
int irq ;
2010-12-13 07:03:14 +03:00
for ( irq = base ; irq < base + TC3589x_NR_INTERNAL_IRQS ; irq + + ) {
2010-05-11 01:39:47 +04:00
# ifdef CONFIG_ARM
set_irq_flags ( irq , 0 ) ;
# endif
2011-03-25 14:12:32 +03:00
irq_set_chip_and_handler ( irq , NULL , NULL ) ;
irq_set_chip_data ( irq , NULL ) ;
2010-05-11 01:39:47 +04:00
}
}
2010-12-13 07:03:14 +03:00
static int tc3589x_chip_init ( struct tc3589x * tc3589x )
2010-05-11 01:39:47 +04:00
{
int manf , ver , ret ;
2010-12-13 07:03:14 +03:00
manf = tc3589x_reg_read ( tc3589x , TC3589x_MANFCODE ) ;
2010-05-11 01:39:47 +04:00
if ( manf < 0 )
return manf ;
2010-12-13 07:03:14 +03:00
ver = tc3589x_reg_read ( tc3589x , TC3589x_VERSION ) ;
2010-05-11 01:39:47 +04:00
if ( ver < 0 )
return ver ;
2010-12-13 07:03:14 +03:00
if ( manf ! = TC3589x_MANFCODE_MAGIC ) {
dev_err ( tc3589x - > dev , " unknown manufacturer: %#x \n " , manf ) ;
2010-05-11 01:39:47 +04:00
return - EINVAL ;
}
2010-12-13 07:03:14 +03:00
dev_info ( tc3589x - > dev , " manufacturer: %#x, version: %#x \n " , manf , ver ) ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:17 +03:00
/*
* Put everything except the IRQ module into reset ;
* also spare the GPIO module for any pin initialization
* done during pre - kernel boot
*/
2010-12-13 07:03:14 +03:00
ret = tc3589x_reg_write ( tc3589x , TC3589x_RSTCTRL ,
TC3589x_RSTCTRL_TIMRST
| TC3589x_RSTCTRL_ROTRST
2010-12-13 07:03:17 +03:00
| TC3589x_RSTCTRL_KBDRST ) ;
2010-05-11 01:39:47 +04:00
if ( ret < 0 )
return ret ;
/* Clear the reset interrupt. */
2010-12-13 07:03:14 +03:00
return tc3589x_reg_write ( tc3589x , TC3589x_RSTINTCLR , 0x1 ) ;
2010-05-11 01:39:47 +04:00
}
2010-12-13 07:03:15 +03:00
static int __devinit tc3589x_device_init ( struct tc3589x * tc3589x )
{
int ret = 0 ;
unsigned int blocks = tc3589x - > pdata - > block ;
if ( blocks & TC3589x_BLOCK_GPIO ) {
ret = mfd_add_devices ( tc3589x - > dev , - 1 , tc3589x_dev_gpio ,
ARRAY_SIZE ( tc3589x_dev_gpio ) , NULL ,
tc3589x - > irq_base ) ;
if ( ret ) {
dev_err ( tc3589x - > dev , " failed to add gpio child \n " ) ;
return ret ;
}
dev_info ( tc3589x - > dev , " added gpio block \n " ) ;
}
2010-12-21 13:23:31 +03:00
if ( blocks & TC3589x_BLOCK_KEYPAD ) {
ret = mfd_add_devices ( tc3589x - > dev , - 1 , tc3589x_dev_keypad ,
ARRAY_SIZE ( tc3589x_dev_keypad ) , NULL ,
tc3589x - > irq_base ) ;
if ( ret ) {
dev_err ( tc3589x - > dev , " failed to keypad child \n " ) ;
return ret ;
}
dev_info ( tc3589x - > dev , " added keypad block \n " ) ;
}
2010-12-13 07:03:15 +03:00
2010-12-21 13:23:31 +03:00
return ret ;
2010-12-13 07:03:15 +03:00
}
2010-12-13 07:03:14 +03:00
static int __devinit tc3589x_probe ( struct i2c_client * i2c ,
2010-05-11 01:39:47 +04:00
const struct i2c_device_id * id )
{
2010-12-13 07:03:14 +03:00
struct tc3589x_platform_data * pdata = i2c - > dev . platform_data ;
struct tc3589x * tc3589x ;
2010-05-11 01:39:47 +04:00
int ret ;
if ( ! i2c_check_functionality ( i2c - > adapter , I2C_FUNC_SMBUS_BYTE_DATA
| I2C_FUNC_SMBUS_I2C_BLOCK ) )
return - EIO ;
2010-12-13 07:03:14 +03:00
tc3589x = kzalloc ( sizeof ( struct tc3589x ) , GFP_KERNEL ) ;
if ( ! tc3589x )
2010-05-11 01:39:47 +04:00
return - ENOMEM ;
2010-12-13 07:03:14 +03:00
mutex_init ( & tc3589x - > lock ) ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:14 +03:00
tc3589x - > dev = & i2c - > dev ;
tc3589x - > i2c = i2c ;
tc3589x - > pdata = pdata ;
tc3589x - > irq_base = pdata - > irq_base ;
tc3589x - > num_gpio = id - > driver_data ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:14 +03:00
i2c_set_clientdata ( i2c , tc3589x ) ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:14 +03:00
ret = tc3589x_chip_init ( tc3589x ) ;
2010-05-11 01:39:47 +04:00
if ( ret )
goto out_free ;
2010-12-13 07:03:14 +03:00
ret = tc3589x_irq_init ( tc3589x ) ;
2010-05-11 01:39:47 +04:00
if ( ret )
goto out_free ;
2010-12-13 07:03:14 +03:00
ret = request_threaded_irq ( tc3589x - > i2c - > irq , NULL , tc3589x_irq ,
2010-05-11 01:39:47 +04:00
IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
2010-12-13 07:03:14 +03:00
" tc3589x " , tc3589x ) ;
2010-05-11 01:39:47 +04:00
if ( ret ) {
2010-12-13 07:03:14 +03:00
dev_err ( tc3589x - > dev , " failed to request IRQ: %d \n " , ret ) ;
2010-05-11 01:39:47 +04:00
goto out_removeirq ;
}
2010-12-13 07:03:15 +03:00
ret = tc3589x_device_init ( tc3589x ) ;
2010-05-11 01:39:47 +04:00
if ( ret ) {
2010-12-13 07:03:15 +03:00
dev_err ( tc3589x - > dev , " failed to add child devices \n " ) ;
2010-05-11 01:39:47 +04:00
goto out_freeirq ;
}
return 0 ;
out_freeirq :
2010-12-13 07:03:14 +03:00
free_irq ( tc3589x - > i2c - > irq , tc3589x ) ;
2010-05-11 01:39:47 +04:00
out_removeirq :
2010-12-13 07:03:14 +03:00
tc3589x_irq_remove ( tc3589x ) ;
2010-05-11 01:39:47 +04:00
out_free :
2010-12-13 07:03:14 +03:00
kfree ( tc3589x ) ;
2010-05-11 01:39:47 +04:00
return ret ;
}
2010-12-13 07:03:14 +03:00
static int __devexit tc3589x_remove ( struct i2c_client * client )
2010-05-11 01:39:47 +04:00
{
2010-12-13 07:03:14 +03:00
struct tc3589x * tc3589x = i2c_get_clientdata ( client ) ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:14 +03:00
mfd_remove_devices ( tc3589x - > dev ) ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:14 +03:00
free_irq ( tc3589x - > i2c - > irq , tc3589x ) ;
tc3589x_irq_remove ( tc3589x ) ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:14 +03:00
kfree ( tc3589x ) ;
2010-05-11 01:39:47 +04:00
return 0 ;
}
2011-08-09 22:37:17 +04:00
# ifdef CONFIG_PM
2010-12-13 07:03:18 +03:00
static int tc3589x_suspend ( struct device * dev )
{
struct tc3589x * tc3589x = dev_get_drvdata ( dev ) ;
struct i2c_client * client = tc3589x - > i2c ;
int ret = 0 ;
/* put the system to sleep mode */
if ( ! device_may_wakeup ( & client - > dev ) )
ret = tc3589x_reg_write ( tc3589x , TC3589x_CLKMODE ,
TC3589x_CLKMODE_MODCTL_SLEEP ) ;
return ret ;
}
static int tc3589x_resume ( struct device * dev )
{
struct tc3589x * tc3589x = dev_get_drvdata ( dev ) ;
struct i2c_client * client = tc3589x - > i2c ;
int ret = 0 ;
/* enable the system into operation */
if ( ! device_may_wakeup ( & client - > dev ) )
ret = tc3589x_reg_write ( tc3589x , TC3589x_CLKMODE ,
TC3589x_CLKMODE_MODCTL_OPERATION ) ;
return ret ;
}
static const SIMPLE_DEV_PM_OPS ( tc3589x_dev_pm_ops , tc3589x_suspend ,
tc3589x_resume ) ;
2011-08-09 22:37:17 +04:00
# endif
2010-12-13 07:03:18 +03:00
2010-12-13 07:03:14 +03:00
static const struct i2c_device_id tc3589x_id [ ] = {
{ " tc3589x " , 24 } ,
2010-05-11 01:39:47 +04:00
{ }
} ;
2010-12-13 07:03:14 +03:00
MODULE_DEVICE_TABLE ( i2c , tc3589x_id ) ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:14 +03:00
static struct i2c_driver tc3589x_driver = {
. driver . name = " tc3589x " ,
2010-05-11 01:39:47 +04:00
. driver . owner = THIS_MODULE ,
2010-12-13 07:03:18 +03:00
# ifdef CONFIG_PM
. driver . pm = & tc3589x_dev_pm_ops ,
# endif
2010-12-13 07:03:14 +03:00
. probe = tc3589x_probe ,
. remove = __devexit_p ( tc3589x_remove ) ,
. id_table = tc3589x_id ,
2010-05-11 01:39:47 +04:00
} ;
2010-12-13 07:03:14 +03:00
static int __init tc3589x_init ( void )
2010-05-11 01:39:47 +04:00
{
2010-12-13 07:03:14 +03:00
return i2c_add_driver ( & tc3589x_driver ) ;
2010-05-11 01:39:47 +04:00
}
2010-12-13 07:03:14 +03:00
subsys_initcall ( tc3589x_init ) ;
2010-05-11 01:39:47 +04:00
2010-12-13 07:03:14 +03:00
static void __exit tc3589x_exit ( void )
2010-05-11 01:39:47 +04:00
{
2010-12-13 07:03:14 +03:00
i2c_del_driver ( & tc3589x_driver ) ;
2010-05-11 01:39:47 +04:00
}
2010-12-13 07:03:14 +03:00
module_exit ( tc3589x_exit ) ;
2010-05-11 01:39:47 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2010-12-13 07:03:14 +03:00
MODULE_DESCRIPTION ( " TC3589x MFD core driver " ) ;
2010-05-11 01:39:47 +04:00
MODULE_AUTHOR ( " Hanumath Prasad, Rabin Vincent " ) ;