2010-08-11 03:11:04 +04:00
/*
* Core driver for TI TPS6586x PMIC family
*
* Copyright ( c ) 2010 CompuLab Ltd .
* Mike Rapoport < mike @ compulab . co . il >
*
* Based on da903x . c .
* Copyright ( C ) 2008 Compulab , Ltd .
* Mike Rapoport < mike @ compulab . co . il >
* Copyright ( C ) 2006 - 2008 Marvell International Ltd .
* Eric Miao < eric . miao @ marvell . com >
*
* 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 .
*/
2010-09-20 02:18:27 +04:00
# include <linux/interrupt.h>
# include <linux/irq.h>
2010-08-11 03:11:04 +04:00
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/slab.h>
2012-07-18 10:20:46 +04:00
# include <linux/err.h>
2010-08-11 03:11:04 +04:00
# include <linux/i2c.h>
2012-07-18 10:20:46 +04:00
# include <linux/regmap.h>
2012-04-26 18:52:21 +04:00
# include <linux/regulator/of_regulator.h>
2012-09-05 00:43:39 +04:00
# include <linux/regulator/machine.h>
2010-08-11 03:11:04 +04:00
# include <linux/mfd/core.h>
# include <linux/mfd/tps6586x.h>
2012-08-20 05:07:55 +04:00
# define TPS6586X_SUPPLYENE 0x14
# define EXITSLREQ_BIT BIT(1)
# define SLEEP_MODE_BIT BIT(3)
2010-09-20 02:18:27 +04:00
/* interrupt control registers */
# define TPS6586X_INT_ACK1 0xb5
# define TPS6586X_INT_ACK2 0xb6
# define TPS6586X_INT_ACK3 0xb7
# define TPS6586X_INT_ACK4 0xb8
/* interrupt mask registers */
# define TPS6586X_INT_MASK1 0xb0
# define TPS6586X_INT_MASK2 0xb1
# define TPS6586X_INT_MASK3 0xb2
# define TPS6586X_INT_MASK4 0xb3
# define TPS6586X_INT_MASK5 0xb4
2010-08-11 03:11:04 +04:00
/* device id */
# define TPS6586X_VERSIONCRC 0xcd
2012-07-18 10:20:46 +04:00
/* Maximum register */
# define TPS6586X_MAX_REGISTER (TPS6586X_VERSIONCRC + 1)
2010-09-20 02:18:27 +04:00
struct tps6586x_irq_data {
u8 mask_reg ;
u8 mask_mask ;
} ;
# define TPS6586X_IRQ(_reg, _mask) \
{ \
. mask_reg = ( _reg ) - TPS6586X_INT_MASK1 , \
. mask_mask = ( _mask ) , \
}
static const struct tps6586x_irq_data tps6586x_irqs [ ] = {
[ TPS6586X_INT_PLDO_0 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK1 , 1 < < 0 ) ,
[ TPS6586X_INT_PLDO_1 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK1 , 1 < < 1 ) ,
[ TPS6586X_INT_PLDO_2 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK1 , 1 < < 2 ) ,
[ TPS6586X_INT_PLDO_3 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK1 , 1 < < 3 ) ,
[ TPS6586X_INT_PLDO_4 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK1 , 1 < < 4 ) ,
[ TPS6586X_INT_PLDO_5 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK1 , 1 < < 5 ) ,
[ TPS6586X_INT_PLDO_6 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK1 , 1 < < 6 ) ,
[ TPS6586X_INT_PLDO_7 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK1 , 1 < < 7 ) ,
[ TPS6586X_INT_COMP_DET ] = TPS6586X_IRQ ( TPS6586X_INT_MASK4 , 1 < < 0 ) ,
[ TPS6586X_INT_ADC ] = TPS6586X_IRQ ( TPS6586X_INT_MASK2 , 1 < < 1 ) ,
[ TPS6586X_INT_PLDO_8 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK2 , 1 < < 2 ) ,
[ TPS6586X_INT_PLDO_9 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK2 , 1 < < 3 ) ,
[ TPS6586X_INT_PSM_0 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK2 , 1 < < 4 ) ,
[ TPS6586X_INT_PSM_1 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK2 , 1 < < 5 ) ,
[ TPS6586X_INT_PSM_2 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK2 , 1 < < 6 ) ,
[ TPS6586X_INT_PSM_3 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK2 , 1 < < 7 ) ,
[ TPS6586X_INT_RTC_ALM1 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK5 , 1 < < 4 ) ,
[ TPS6586X_INT_ACUSB_OVP ] = TPS6586X_IRQ ( TPS6586X_INT_MASK5 , 0x03 ) ,
[ TPS6586X_INT_USB_DET ] = TPS6586X_IRQ ( TPS6586X_INT_MASK5 , 1 < < 2 ) ,
[ TPS6586X_INT_AC_DET ] = TPS6586X_IRQ ( TPS6586X_INT_MASK5 , 1 < < 3 ) ,
[ TPS6586X_INT_BAT_DET ] = TPS6586X_IRQ ( TPS6586X_INT_MASK3 , 1 < < 0 ) ,
[ TPS6586X_INT_CHG_STAT ] = TPS6586X_IRQ ( TPS6586X_INT_MASK4 , 0xfc ) ,
[ TPS6586X_INT_CHG_TEMP ] = TPS6586X_IRQ ( TPS6586X_INT_MASK3 , 0x06 ) ,
[ TPS6586X_INT_PP ] = TPS6586X_IRQ ( TPS6586X_INT_MASK3 , 0xf0 ) ,
[ TPS6586X_INT_RESUME ] = TPS6586X_IRQ ( TPS6586X_INT_MASK5 , 1 < < 5 ) ,
[ TPS6586X_INT_LOW_SYS ] = TPS6586X_IRQ ( TPS6586X_INT_MASK5 , 1 < < 6 ) ,
[ TPS6586X_INT_RTC_ALM2 ] = TPS6586X_IRQ ( TPS6586X_INT_MASK4 , 1 < < 1 ) ,
} ;
2012-07-18 10:20:50 +04:00
static struct mfd_cell tps6586x_cell [ ] = {
{
. name = " tps6586x-gpio " ,
} ,
{
. name = " tps6586x-rtc " ,
} ,
{
. name = " tps6586x-onkey " ,
} ,
} ;
2010-08-11 03:11:04 +04:00
struct tps6586x {
struct device * dev ;
struct i2c_client * client ;
2012-07-18 10:20:46 +04:00
struct regmap * regmap ;
2010-08-11 03:11:04 +04:00
2010-09-20 02:18:27 +04:00
struct irq_chip irq_chip ;
struct mutex irq_lock ;
int irq_base ;
u32 irq_en ;
u8 mask_reg [ 5 ] ;
2010-08-11 03:11:04 +04:00
} ;
2012-07-18 10:20:46 +04:00
static inline struct tps6586x * dev_to_tps6586x ( struct device * dev )
2010-08-11 03:11:04 +04:00
{
2012-07-18 10:20:46 +04:00
return i2c_get_clientdata ( to_i2c_client ( dev ) ) ;
2010-08-11 03:11:04 +04:00
}
int tps6586x_write ( struct device * dev , int reg , uint8_t val )
{
2012-07-18 10:20:46 +04:00
struct tps6586x * tps6586x = dev_to_tps6586x ( dev ) ;
return regmap_write ( tps6586x - > regmap , reg , val ) ;
2010-08-11 03:11:04 +04:00
}
EXPORT_SYMBOL_GPL ( tps6586x_write ) ;
int tps6586x_writes ( struct device * dev , int reg , int len , uint8_t * val )
{
2012-07-18 10:20:46 +04:00
struct tps6586x * tps6586x = dev_to_tps6586x ( dev ) ;
return regmap_bulk_write ( tps6586x - > regmap , reg , val , len ) ;
2010-08-11 03:11:04 +04:00
}
EXPORT_SYMBOL_GPL ( tps6586x_writes ) ;
int tps6586x_read ( struct device * dev , int reg , uint8_t * val )
{
2012-07-18 10:20:46 +04:00
struct tps6586x * tps6586x = dev_to_tps6586x ( dev ) ;
unsigned int rval ;
int ret ;
ret = regmap_read ( tps6586x - > regmap , reg , & rval ) ;
if ( ! ret )
* val = rval ;
return ret ;
2010-08-11 03:11:04 +04:00
}
EXPORT_SYMBOL_GPL ( tps6586x_read ) ;
int tps6586x_reads ( struct device * dev , int reg , int len , uint8_t * val )
{
2012-07-18 10:20:46 +04:00
struct tps6586x * tps6586x = dev_to_tps6586x ( dev ) ;
return regmap_bulk_read ( tps6586x - > regmap , reg , val , len ) ;
2010-08-11 03:11:04 +04:00
}
EXPORT_SYMBOL_GPL ( tps6586x_reads ) ;
int tps6586x_set_bits ( struct device * dev , int reg , uint8_t bit_mask )
{
2012-07-18 10:20:46 +04:00
struct tps6586x * tps6586x = dev_to_tps6586x ( dev ) ;
2010-08-11 03:11:04 +04:00
2012-07-18 10:20:46 +04:00
return regmap_update_bits ( tps6586x - > regmap , reg , bit_mask , bit_mask ) ;
2010-08-11 03:11:04 +04:00
}
EXPORT_SYMBOL_GPL ( tps6586x_set_bits ) ;
int tps6586x_clr_bits ( struct device * dev , int reg , uint8_t bit_mask )
{
2012-07-18 10:20:46 +04:00
struct tps6586x * tps6586x = dev_to_tps6586x ( dev ) ;
2010-08-11 03:11:04 +04:00
2012-07-18 10:20:46 +04:00
return regmap_update_bits ( tps6586x - > regmap , reg , bit_mask , 0 ) ;
2010-08-11 03:11:04 +04:00
}
EXPORT_SYMBOL_GPL ( tps6586x_clr_bits ) ;
int tps6586x_update ( struct device * dev , int reg , uint8_t val , uint8_t mask )
{
2012-07-18 10:20:46 +04:00
struct tps6586x * tps6586x = dev_to_tps6586x ( dev ) ;
2010-08-11 03:11:04 +04:00
2012-07-18 10:20:46 +04:00
return regmap_update_bits ( tps6586x - > regmap , reg , mask , val ) ;
2010-08-11 03:11:04 +04:00
}
EXPORT_SYMBOL_GPL ( tps6586x_update ) ;
static int __remove_subdev ( struct device * dev , void * unused )
{
platform_device_unregister ( to_platform_device ( dev ) ) ;
return 0 ;
}
static int tps6586x_remove_subdevs ( struct tps6586x * tps6586x )
{
return device_for_each_child ( tps6586x - > dev , NULL , __remove_subdev ) ;
}
2010-12-12 15:39:28 +03:00
static void tps6586x_irq_lock ( struct irq_data * data )
2010-09-20 02:18:27 +04:00
{
2010-12-12 15:39:28 +03:00
struct tps6586x * tps6586x = irq_data_get_irq_chip_data ( data ) ;
2010-09-20 02:18:27 +04:00
mutex_lock ( & tps6586x - > irq_lock ) ;
}
2010-12-12 15:39:28 +03:00
static void tps6586x_irq_enable ( struct irq_data * irq_data )
2010-09-20 02:18:27 +04:00
{
2010-12-12 15:39:28 +03:00
struct tps6586x * tps6586x = irq_data_get_irq_chip_data ( irq_data ) ;
unsigned int __irq = irq_data - > irq - tps6586x - > irq_base ;
2010-09-20 02:18:27 +04:00
const struct tps6586x_irq_data * data = & tps6586x_irqs [ __irq ] ;
tps6586x - > mask_reg [ data - > mask_reg ] & = ~ data - > mask_mask ;
tps6586x - > irq_en | = ( 1 < < __irq ) ;
}
2010-12-12 15:39:28 +03:00
static void tps6586x_irq_disable ( struct irq_data * irq_data )
2010-09-20 02:18:27 +04:00
{
2010-12-12 15:39:28 +03:00
struct tps6586x * tps6586x = irq_data_get_irq_chip_data ( irq_data ) ;
2010-09-20 02:18:27 +04:00
2010-12-12 15:39:28 +03:00
unsigned int __irq = irq_data - > irq - tps6586x - > irq_base ;
2010-09-20 02:18:27 +04:00
const struct tps6586x_irq_data * data = & tps6586x_irqs [ __irq ] ;
tps6586x - > mask_reg [ data - > mask_reg ] | = data - > mask_mask ;
tps6586x - > irq_en & = ~ ( 1 < < __irq ) ;
}
2010-12-12 15:39:28 +03:00
static void tps6586x_irq_sync_unlock ( struct irq_data * data )
2010-09-20 02:18:27 +04:00
{
2010-12-12 15:39:28 +03:00
struct tps6586x * tps6586x = irq_data_get_irq_chip_data ( data ) ;
2010-09-20 02:18:27 +04:00
int i ;
for ( i = 0 ; i < ARRAY_SIZE ( tps6586x - > mask_reg ) ; i + + ) {
2012-07-18 10:20:47 +04:00
int ret ;
ret = tps6586x_write ( tps6586x - > dev ,
TPS6586X_INT_MASK1 + i ,
tps6586x - > mask_reg [ i ] ) ;
WARN_ON ( ret ) ;
2010-09-20 02:18:27 +04:00
}
mutex_unlock ( & tps6586x - > irq_lock ) ;
}
static irqreturn_t tps6586x_irq ( int irq , void * data )
{
struct tps6586x * tps6586x = data ;
u32 acks ;
int ret = 0 ;
ret = tps6586x_reads ( tps6586x - > dev , TPS6586X_INT_ACK1 ,
sizeof ( acks ) , ( uint8_t * ) & acks ) ;
if ( ret < 0 ) {
dev_err ( tps6586x - > dev , " failed to read interrupt status \n " ) ;
return IRQ_NONE ;
}
acks = le32_to_cpu ( acks ) ;
while ( acks ) {
int i = __ffs ( acks ) ;
if ( tps6586x - > irq_en & ( 1 < < i ) )
handle_nested_irq ( tps6586x - > irq_base + i ) ;
acks & = ~ ( 1 < < i ) ;
}
return IRQ_HANDLED ;
}
static int __devinit tps6586x_irq_init ( struct tps6586x * tps6586x , int irq ,
int irq_base )
{
int i , ret ;
u8 tmp [ 4 ] ;
if ( ! irq_base ) {
dev_warn ( tps6586x - > dev , " No interrupt support on IRQ base \n " ) ;
return - EINVAL ;
}
mutex_init ( & tps6586x - > irq_lock ) ;
for ( i = 0 ; i < 5 ; i + + ) {
tps6586x - > mask_reg [ i ] = 0xff ;
tps6586x_write ( tps6586x - > dev , TPS6586X_INT_MASK1 + i , 0xff ) ;
}
tps6586x_reads ( tps6586x - > dev , TPS6586X_INT_ACK1 , sizeof ( tmp ) , tmp ) ;
tps6586x - > irq_base = irq_base ;
tps6586x - > irq_chip . name = " tps6586x " ;
2010-12-12 15:39:28 +03:00
tps6586x - > irq_chip . irq_enable = tps6586x_irq_enable ;
tps6586x - > irq_chip . irq_disable = tps6586x_irq_disable ;
tps6586x - > irq_chip . irq_bus_lock = tps6586x_irq_lock ;
tps6586x - > irq_chip . irq_bus_sync_unlock = tps6586x_irq_sync_unlock ;
2010-09-20 02:18:27 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( tps6586x_irqs ) ; i + + ) {
int __irq = i + tps6586x - > irq_base ;
2011-03-25 14:12:32 +03:00
irq_set_chip_data ( __irq , tps6586x ) ;
irq_set_chip_and_handler ( __irq , & tps6586x - > irq_chip ,
2010-09-20 02:18:27 +04:00
handle_simple_irq ) ;
2011-03-25 14:12:32 +03:00
irq_set_nested_thread ( __irq , 1 ) ;
2010-09-20 02:18:27 +04:00
# ifdef CONFIG_ARM
set_irq_flags ( __irq , IRQF_VALID ) ;
# endif
}
ret = request_threaded_irq ( irq , NULL , tps6586x_irq , IRQF_ONESHOT ,
" tps6586x " , tps6586x ) ;
if ( ! ret ) {
device_init_wakeup ( tps6586x - > dev , 1 ) ;
enable_irq_wake ( irq ) ;
}
return ret ;
}
2010-08-11 03:11:04 +04:00
static int __devinit tps6586x_add_subdevs ( struct tps6586x * tps6586x ,
struct tps6586x_platform_data * pdata )
{
struct tps6586x_subdev_info * subdev ;
struct platform_device * pdev ;
int i , ret = 0 ;
for ( i = 0 ; i < pdata - > num_subdevs ; i + + ) {
subdev = & pdata - > subdevs [ i ] ;
pdev = platform_device_alloc ( subdev - > name , subdev - > id ) ;
2010-08-24 09:47:22 +04:00
if ( ! pdev ) {
ret = - ENOMEM ;
goto failed ;
}
2010-08-11 03:11:04 +04:00
pdev - > dev . parent = tps6586x - > dev ;
pdev - > dev . platform_data = subdev - > platform_data ;
2012-04-26 18:52:21 +04:00
pdev - > dev . of_node = subdev - > of_node ;
2010-08-11 03:11:04 +04:00
ret = platform_device_add ( pdev ) ;
2010-08-24 09:47:22 +04:00
if ( ret ) {
platform_device_put ( pdev ) ;
2010-08-11 03:11:04 +04:00
goto failed ;
2010-08-24 09:47:22 +04:00
}
2010-08-11 03:11:04 +04:00
}
return 0 ;
failed :
tps6586x_remove_subdevs ( tps6586x ) ;
return ret ;
}
2012-04-26 18:52:21 +04:00
# ifdef CONFIG_OF
static struct of_regulator_match tps6586x_matches [ ] = {
2012-09-05 00:43:39 +04:00
{ . name = " sys " , . driver_data = ( void * ) TPS6586X_ID_SYS } ,
2012-04-26 18:52:21 +04:00
{ . name = " sm0 " , . driver_data = ( void * ) TPS6586X_ID_SM_0 } ,
{ . name = " sm1 " , . driver_data = ( void * ) TPS6586X_ID_SM_1 } ,
{ . name = " sm2 " , . driver_data = ( void * ) TPS6586X_ID_SM_2 } ,
{ . name = " ldo0 " , . driver_data = ( void * ) TPS6586X_ID_LDO_0 } ,
{ . name = " ldo1 " , . driver_data = ( void * ) TPS6586X_ID_LDO_1 } ,
{ . name = " ldo2 " , . driver_data = ( void * ) TPS6586X_ID_LDO_2 } ,
{ . name = " ldo3 " , . driver_data = ( void * ) TPS6586X_ID_LDO_3 } ,
{ . name = " ldo4 " , . driver_data = ( void * ) TPS6586X_ID_LDO_4 } ,
{ . name = " ldo5 " , . driver_data = ( void * ) TPS6586X_ID_LDO_5 } ,
{ . name = " ldo6 " , . driver_data = ( void * ) TPS6586X_ID_LDO_6 } ,
{ . name = " ldo7 " , . driver_data = ( void * ) TPS6586X_ID_LDO_7 } ,
{ . name = " ldo8 " , . driver_data = ( void * ) TPS6586X_ID_LDO_8 } ,
{ . name = " ldo9 " , . driver_data = ( void * ) TPS6586X_ID_LDO_9 } ,
{ . name = " ldo_rtc " , . driver_data = ( void * ) TPS6586X_ID_LDO_RTC } ,
} ;
static struct tps6586x_platform_data * tps6586x_parse_dt ( struct i2c_client * client )
{
const unsigned int num = ARRAY_SIZE ( tps6586x_matches ) ;
struct device_node * np = client - > dev . of_node ;
struct tps6586x_platform_data * pdata ;
struct tps6586x_subdev_info * devs ;
struct device_node * regs ;
2012-09-05 00:43:39 +04:00
const char * sys_rail_name = NULL ;
2012-04-26 18:52:21 +04:00
unsigned int count ;
unsigned int i , j ;
int err ;
regs = of_find_node_by_name ( np , " regulators " ) ;
if ( ! regs )
return NULL ;
err = of_regulator_match ( & client - > dev , regs , tps6586x_matches , num ) ;
if ( err < 0 ) {
of_node_put ( regs ) ;
return NULL ;
}
of_node_put ( regs ) ;
count = err ;
devs = devm_kzalloc ( & client - > dev , count * sizeof ( * devs ) , GFP_KERNEL ) ;
if ( ! devs )
return NULL ;
for ( i = 0 , j = 0 ; i < num & & j < count ; i + + ) {
2012-09-05 00:43:39 +04:00
struct regulator_init_data * reg_idata ;
2012-04-26 18:52:21 +04:00
if ( ! tps6586x_matches [ i ] . init_data )
continue ;
2012-09-05 00:43:39 +04:00
reg_idata = tps6586x_matches [ i ] . init_data ;
2012-04-26 18:52:21 +04:00
devs [ j ] . name = " tps6586x-regulator " ;
devs [ j ] . platform_data = tps6586x_matches [ i ] . init_data ;
devs [ j ] . id = ( int ) tps6586x_matches [ i ] . driver_data ;
2012-09-05 00:43:39 +04:00
if ( devs [ j ] . id = = TPS6586X_ID_SYS )
sys_rail_name = reg_idata - > constraints . name ;
if ( ( devs [ j ] . id = = TPS6586X_ID_LDO_5 ) | |
( devs [ j ] . id = = TPS6586X_ID_LDO_RTC ) )
reg_idata - > supply_regulator = sys_rail_name ;
2012-04-26 18:52:21 +04:00
devs [ j ] . of_node = tps6586x_matches [ i ] . of_node ;
j + + ;
}
pdata = devm_kzalloc ( & client - > dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return NULL ;
pdata - > num_subdevs = count ;
pdata - > subdevs = devs ;
pdata - > gpio_base = - 1 ;
pdata - > irq_base = - 1 ;
2012-08-20 05:07:55 +04:00
pdata - > pm_off = of_property_read_bool ( np , " ti,system-power-controller " ) ;
2012-04-26 18:52:21 +04:00
return pdata ;
}
static struct of_device_id tps6586x_of_match [ ] = {
{ . compatible = " ti,tps6586x " , } ,
{ } ,
} ;
# else
static struct tps6586x_platform_data * tps6586x_parse_dt ( struct i2c_client * client )
{
return NULL ;
}
# endif
2012-07-18 10:20:47 +04:00
static bool is_volatile_reg ( struct device * dev , unsigned int reg )
{
/* Cache all interrupt mask register */
if ( ( reg > = TPS6586X_INT_MASK1 ) & & ( reg < = TPS6586X_INT_MASK5 ) )
return false ;
return true ;
}
2012-07-18 10:20:46 +04:00
static const struct regmap_config tps6586x_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = TPS6586X_MAX_REGISTER - 1 ,
2012-07-18 10:20:47 +04:00
. volatile_reg = is_volatile_reg ,
. cache_type = REGCACHE_RBTREE ,
2012-07-18 10:20:46 +04:00
} ;
2012-08-20 05:07:55 +04:00
static struct device * tps6586x_dev ;
static void tps6586x_power_off ( void )
{
if ( tps6586x_clr_bits ( tps6586x_dev , TPS6586X_SUPPLYENE , EXITSLREQ_BIT ) )
return ;
tps6586x_set_bits ( tps6586x_dev , TPS6586X_SUPPLYENE , SLEEP_MODE_BIT ) ;
}
2010-08-11 03:11:04 +04:00
static int __devinit tps6586x_i2c_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct tps6586x_platform_data * pdata = client - > dev . platform_data ;
struct tps6586x * tps6586x ;
int ret ;
2012-04-26 18:52:21 +04:00
if ( ! pdata & & client - > dev . of_node )
pdata = tps6586x_parse_dt ( client ) ;
2010-08-11 03:11:04 +04:00
if ( ! pdata ) {
dev_err ( & client - > dev , " tps6586x requires platform data \n " ) ;
return - ENOTSUPP ;
}
ret = i2c_smbus_read_byte_data ( client , TPS6586X_VERSIONCRC ) ;
if ( ret < 0 ) {
dev_err ( & client - > dev , " Chip ID read failed: %d \n " , ret ) ;
return - EIO ;
}
2010-12-09 20:30:11 +03:00
dev_info ( & client - > dev , " VERSIONCRC is %02x \n " , ret ) ;
2010-08-11 03:11:04 +04:00
2012-07-18 10:20:45 +04:00
tps6586x = devm_kzalloc ( & client - > dev , sizeof ( * tps6586x ) , GFP_KERNEL ) ;
if ( tps6586x = = NULL ) {
dev_err ( & client - > dev , " memory for tps6586x alloc failed \n " ) ;
2010-08-11 03:11:04 +04:00
return - ENOMEM ;
2012-07-18 10:20:45 +04:00
}
2010-08-11 03:11:04 +04:00
tps6586x - > client = client ;
tps6586x - > dev = & client - > dev ;
i2c_set_clientdata ( client , tps6586x ) ;
2012-07-18 10:20:46 +04:00
tps6586x - > regmap = devm_regmap_init_i2c ( client ,
& tps6586x_regmap_config ) ;
if ( IS_ERR ( tps6586x - > regmap ) ) {
ret = PTR_ERR ( tps6586x - > regmap ) ;
dev_err ( & client - > dev , " regmap init failed: %d \n " , ret ) ;
return ret ;
}
2010-08-11 03:11:04 +04:00
2010-09-20 02:18:27 +04:00
if ( client - > irq ) {
ret = tps6586x_irq_init ( tps6586x , client - > irq ,
pdata - > irq_base ) ;
if ( ret ) {
dev_err ( & client - > dev , " IRQ init failed: %d \n " , ret ) ;
2012-07-18 10:20:45 +04:00
return ret ;
2010-09-20 02:18:27 +04:00
}
}
2012-07-18 10:20:50 +04:00
ret = mfd_add_devices ( tps6586x - > dev , - 1 ,
2012-09-11 11:16:36 +04:00
tps6586x_cell , ARRAY_SIZE ( tps6586x_cell ) ,
NULL , 0 , NULL ) ;
2012-07-18 10:20:50 +04:00
if ( ret < 0 ) {
dev_err ( & client - > dev , " mfd_add_devices failed: %d \n " , ret ) ;
goto err_mfd_add ;
2011-01-31 19:31:49 +03:00
}
2010-08-11 03:11:04 +04:00
ret = tps6586x_add_subdevs ( tps6586x , pdata ) ;
if ( ret ) {
dev_err ( & client - > dev , " add devices failed: %d \n " , ret ) ;
goto err_add_devs ;
}
2012-08-20 05:07:55 +04:00
if ( pdata - > pm_off & & ! pm_power_off ) {
tps6586x_dev = & client - > dev ;
pm_power_off = tps6586x_power_off ;
}
2010-08-11 03:11:04 +04:00
return 0 ;
err_add_devs :
2012-07-18 10:20:50 +04:00
mfd_remove_devices ( tps6586x - > dev ) ;
err_mfd_add :
2010-09-20 02:18:27 +04:00
if ( client - > irq )
free_irq ( client - > irq , tps6586x ) ;
2010-08-11 03:11:04 +04:00
return ret ;
}
static int __devexit tps6586x_i2c_remove ( struct i2c_client * client )
{
2010-08-24 11:18:58 +04:00
struct tps6586x * tps6586x = i2c_get_clientdata ( client ) ;
2012-07-18 10:20:50 +04:00
tps6586x_remove_subdevs ( tps6586x ) ;
mfd_remove_devices ( tps6586x - > dev ) ;
2010-09-20 02:18:27 +04:00
if ( client - > irq )
free_irq ( client - > irq , tps6586x ) ;
2010-08-11 03:11:04 +04:00
return 0 ;
}
static const struct i2c_device_id tps6586x_id_table [ ] = {
{ " tps6586x " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , tps6586x_id_table ) ;
static struct i2c_driver tps6586x_driver = {
. driver = {
. name = " tps6586x " ,
. owner = THIS_MODULE ,
2012-04-26 18:52:21 +04:00
. of_match_table = of_match_ptr ( tps6586x_of_match ) ,
2010-08-11 03:11:04 +04:00
} ,
. probe = tps6586x_i2c_probe ,
. remove = __devexit_p ( tps6586x_i2c_remove ) ,
. id_table = tps6586x_id_table ,
} ;
static int __init tps6586x_init ( void )
{
return i2c_add_driver ( & tps6586x_driver ) ;
}
subsys_initcall ( tps6586x_init ) ;
static void __exit tps6586x_exit ( void )
{
i2c_del_driver ( & tps6586x_driver ) ;
}
module_exit ( tps6586x_exit ) ;
MODULE_DESCRIPTION ( " TPS6586X core driver " ) ;
MODULE_AUTHOR ( " Mike Rapoport <mike@compulab.co.il> " ) ;
MODULE_LICENSE ( " GPL " ) ;