2012-03-16 09:40:19 +04:00
/*
* Core driver for TI TPS65090 PMIC family
*
* Copyright ( c ) 2012 , NVIDIA CORPORATION . All rights reserved .
* This program is free software ; you can redistribute it and / or modify it
* under the terms and conditions of the GNU General Public License ,
* version 2 , as published by the Free Software Foundation .
* This program is distributed in the hope it will be useful , but WITHOUT
* ANY WARRANTY ; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE . See the GNU General Public License for
* more details .
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/interrupt.h>
# include <linux/irq.h>
# include <linux/kernel.h>
# include <linux/module.h>
# include <linux/mutex.h>
# include <linux/slab.h>
# include <linux/i2c.h>
# include <linux/mfd/core.h>
# include <linux/mfd/tps65090.h>
2013-01-29 13:05:15 +04:00
# include <linux/of.h>
# include <linux/of_device.h>
2012-03-16 09:40:19 +04:00
# include <linux/err.h>
# define NUM_INT_REG 2
2012-11-20 07:14:49 +04:00
# define TPS65090_INT1_MASK_VAC_STATUS_CHANGE 1
# define TPS65090_INT1_MASK_VSYS_STATUS_CHANGE 2
# define TPS65090_INT1_MASK_BAT_STATUS_CHANGE 3
# define TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE 4
# define TPS65090_INT1_MASK_CHARGING_COMPLETE 5
# define TPS65090_INT1_MASK_OVERLOAD_DCDC1 6
# define TPS65090_INT1_MASK_OVERLOAD_DCDC2 7
# define TPS65090_INT2_MASK_OVERLOAD_DCDC3 0
# define TPS65090_INT2_MASK_OVERLOAD_FET1 1
# define TPS65090_INT2_MASK_OVERLOAD_FET2 2
# define TPS65090_INT2_MASK_OVERLOAD_FET3 3
# define TPS65090_INT2_MASK_OVERLOAD_FET4 4
# define TPS65090_INT2_MASK_OVERLOAD_FET5 5
# define TPS65090_INT2_MASK_OVERLOAD_FET6 6
# define TPS65090_INT2_MASK_OVERLOAD_FET7 7
2012-03-16 09:40:19 +04:00
2013-03-13 02:08:07 +04:00
static struct resource charger_resources [ ] = {
{
. start = TPS65090_IRQ_VAC_STATUS_CHANGE ,
. end = TPS65090_IRQ_VAC_STATUS_CHANGE ,
. flags = IORESOURCE_IRQ ,
}
} ;
2014-04-17 03:12:25 +04:00
enum tps65090_cells {
PMIC = 0 ,
CHARGER = 1 ,
} ;
static struct mfd_cell tps65090s [ ] = {
[ PMIC ] = {
2012-07-18 17:03:42 +04:00
. name = " tps65090-pmic " ,
2012-03-16 09:40:19 +04:00
} ,
2014-04-17 03:12:25 +04:00
[ CHARGER ] = {
2012-11-20 07:14:45 +04:00
. name = " tps65090-charger " ,
2013-03-13 02:08:07 +04:00
. num_resources = ARRAY_SIZE ( charger_resources ) ,
. resources = & charger_resources [ 0 ] ,
2013-04-10 21:14:55 +04:00
. of_compatible = " ti,tps65090-charger " ,
2012-03-16 09:40:19 +04:00
} ,
} ;
2012-11-20 07:14:49 +04:00
static const struct regmap_irq tps65090_irqs [ ] = {
/* INT1 IRQs*/
[ TPS65090_IRQ_VAC_STATUS_CHANGE ] = {
2014-11-10 15:28:36 +03:00
. mask = TPS65090_INT1_MASK_VAC_STATUS_CHANGE ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_VSYS_STATUS_CHANGE ] = {
2014-11-10 15:28:36 +03:00
. mask = TPS65090_INT1_MASK_VSYS_STATUS_CHANGE ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_BAT_STATUS_CHANGE ] = {
2014-11-10 15:28:36 +03:00
. mask = TPS65090_INT1_MASK_BAT_STATUS_CHANGE ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_CHARGING_STATUS_CHANGE ] = {
2014-11-10 15:28:36 +03:00
. mask = TPS65090_INT1_MASK_CHARGING_STATUS_CHANGE ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_CHARGING_COMPLETE ] = {
2014-11-10 15:28:36 +03:00
. mask = TPS65090_INT1_MASK_CHARGING_COMPLETE ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_OVERLOAD_DCDC1 ] = {
2014-11-10 15:28:36 +03:00
. mask = TPS65090_INT1_MASK_OVERLOAD_DCDC1 ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_OVERLOAD_DCDC2 ] = {
2014-11-10 15:28:36 +03:00
. mask = TPS65090_INT1_MASK_OVERLOAD_DCDC2 ,
2012-11-20 07:14:49 +04:00
} ,
/* INT2 IRQs*/
[ TPS65090_IRQ_OVERLOAD_DCDC3 ] = {
2014-11-10 15:28:36 +03:00
. reg_offset = 1 ,
. mask = TPS65090_INT2_MASK_OVERLOAD_DCDC3 ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_OVERLOAD_FET1 ] = {
2014-11-10 15:28:36 +03:00
. reg_offset = 1 ,
. mask = TPS65090_INT2_MASK_OVERLOAD_FET1 ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_OVERLOAD_FET2 ] = {
2014-11-10 15:28:36 +03:00
. reg_offset = 1 ,
. mask = TPS65090_INT2_MASK_OVERLOAD_FET2 ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_OVERLOAD_FET3 ] = {
2014-11-10 15:28:36 +03:00
. reg_offset = 1 ,
. mask = TPS65090_INT2_MASK_OVERLOAD_FET3 ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_OVERLOAD_FET4 ] = {
2014-11-10 15:28:36 +03:00
. reg_offset = 1 ,
. mask = TPS65090_INT2_MASK_OVERLOAD_FET4 ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_OVERLOAD_FET5 ] = {
2014-11-10 15:28:36 +03:00
. reg_offset = 1 ,
. mask = TPS65090_INT2_MASK_OVERLOAD_FET5 ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_OVERLOAD_FET6 ] = {
2014-11-10 15:28:36 +03:00
. reg_offset = 1 ,
. mask = TPS65090_INT2_MASK_OVERLOAD_FET6 ,
2012-11-20 07:14:49 +04:00
} ,
[ TPS65090_IRQ_OVERLOAD_FET7 ] = {
2014-11-10 15:28:36 +03:00
. reg_offset = 1 ,
. mask = TPS65090_INT2_MASK_OVERLOAD_FET7 ,
2012-11-20 07:14:49 +04:00
} ,
} ;
2012-03-16 09:40:19 +04:00
2012-11-20 07:14:49 +04:00
static struct regmap_irq_chip tps65090_irq_chip = {
. name = " tps65090 " ,
. irqs = tps65090_irqs ,
. num_irqs = ARRAY_SIZE ( tps65090_irqs ) ,
. num_regs = NUM_INT_REG ,
2014-04-17 03:12:27 +04:00
. status_base = TPS65090_REG_INTR_STS ,
. mask_base = TPS65090_REG_INTR_MASK ,
2012-11-20 07:14:49 +04:00
. mask_invert = true ,
} ;
2012-03-16 09:40:19 +04:00
static bool is_volatile_reg ( struct device * dev , unsigned int reg )
{
2014-04-17 03:12:27 +04:00
/* Nearly all registers have status bits mixed in, except a few */
switch ( reg ) {
case TPS65090_REG_INTR_MASK :
case TPS65090_REG_INTR_MASK2 :
case TPS65090_REG_CG_CTRL0 :
case TPS65090_REG_CG_CTRL1 :
case TPS65090_REG_CG_CTRL2 :
case TPS65090_REG_CG_CTRL3 :
case TPS65090_REG_CG_CTRL4 :
case TPS65090_REG_CG_CTRL5 :
2012-03-16 09:40:19 +04:00
return false ;
2014-04-17 03:12:27 +04:00
}
return true ;
2012-03-16 09:40:19 +04:00
}
static const struct regmap_config tps65090_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
2016-02-01 01:00:06 +03:00
. max_register = TPS65090_MAX_REG ,
. num_reg_defaults_raw = TPS65090_NUM_REGS ,
2012-03-16 09:40:19 +04:00
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = is_volatile_reg ,
} ;
2013-01-29 13:05:15 +04:00
# ifdef CONFIG_OF
static const struct of_device_id tps65090_of_match [ ] = {
{ . compatible = " ti,tps65090 " , } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , tps65090_of_match ) ;
# endif
2012-11-19 22:23:04 +04:00
static int tps65090_i2c_probe ( struct i2c_client * client ,
2014-11-10 15:28:36 +03:00
const struct i2c_device_id * id )
2012-03-16 09:40:19 +04:00
{
2013-07-30 12:10:05 +04:00
struct tps65090_platform_data * pdata = dev_get_platdata ( & client - > dev ) ;
2013-01-29 13:05:15 +04:00
int irq_base = 0 ;
2012-03-16 09:40:19 +04:00
struct tps65090 * tps65090 ;
int ret ;
2013-01-29 13:05:15 +04:00
if ( ! pdata & & ! client - > dev . of_node ) {
dev_err ( & client - > dev ,
" tps65090 requires platform data or of_node \n " ) ;
2012-03-16 09:40:19 +04:00
return - EINVAL ;
}
2013-01-29 13:05:15 +04:00
if ( pdata )
irq_base = pdata - > irq_base ;
2012-11-20 07:14:46 +04:00
tps65090 = devm_kzalloc ( & client - > dev , sizeof ( * tps65090 ) , GFP_KERNEL ) ;
if ( ! tps65090 ) {
dev_err ( & client - > dev , " mem alloc for tps65090 failed \n " ) ;
2012-03-16 09:40:19 +04:00
return - ENOMEM ;
2012-11-20 07:14:46 +04:00
}
2012-03-16 09:40:19 +04:00
tps65090 - > dev = & client - > dev ;
i2c_set_clientdata ( client , tps65090 ) ;
2012-11-20 07:14:47 +04:00
tps65090 - > rmap = devm_regmap_init_i2c ( client , & tps65090_regmap_config ) ;
2012-03-16 09:40:19 +04:00
if ( IS_ERR ( tps65090 - > rmap ) ) {
2012-04-25 05:30:36 +04:00
ret = PTR_ERR ( tps65090 - > rmap ) ;
dev_err ( & client - > dev , " regmap_init failed with err: %d \n " , ret ) ;
2012-11-20 07:14:49 +04:00
return ret ;
}
if ( client - > irq ) {
ret = regmap_add_irq_chip ( tps65090 - > rmap , client - > irq ,
2014-11-10 15:28:36 +03:00
IRQF_ONESHOT | IRQF_TRIGGER_LOW , irq_base ,
& tps65090_irq_chip , & tps65090 - > irq_data ) ;
if ( ret ) {
dev_err ( & client - > dev ,
" IRQ init failed with err: %d \n " , ret ) ;
2012-11-20 07:14:49 +04:00
return ret ;
}
2014-04-17 03:12:25 +04:00
} else {
/* Don't tell children they have an IRQ that'll never fire */
tps65090s [ CHARGER ] . num_resources = 0 ;
2012-04-25 05:30:36 +04:00
}
2012-03-16 09:40:19 +04:00
ret = mfd_add_devices ( tps65090 - > dev , - 1 , tps65090s ,
2014-11-10 15:28:36 +03:00
ARRAY_SIZE ( tps65090s ) , NULL ,
0 , regmap_irq_get_domain ( tps65090 - > irq_data ) ) ;
2012-03-16 09:40:19 +04:00
if ( ret ) {
dev_err ( & client - > dev , " add mfd devices failed with err: %d \n " ,
ret ) ;
2012-04-25 06:04:58 +04:00
goto err_irq_exit ;
2012-03-16 09:40:19 +04:00
}
return 0 ;
err_irq_exit :
if ( client - > irq )
2012-11-20 07:14:49 +04:00
regmap_del_irq_chip ( client - > irq , tps65090 - > irq_data ) ;
2012-03-16 09:40:19 +04:00
return ret ;
}
2012-11-19 22:26:01 +04:00
static int tps65090_i2c_remove ( struct i2c_client * client )
2012-03-16 09:40:19 +04:00
{
struct tps65090 * tps65090 = i2c_get_clientdata ( client ) ;
mfd_remove_devices ( tps65090 - > dev ) ;
if ( client - > irq )
2012-11-20 07:14:49 +04:00
regmap_del_irq_chip ( client - > irq , tps65090 - > irq_data ) ;
2012-03-16 09:40:19 +04:00
return 0 ;
}
static const struct i2c_device_id tps65090_id_table [ ] = {
{ " tps65090 " , 0 } ,
{ } ,
} ;
MODULE_DEVICE_TABLE ( i2c , tps65090_id_table ) ;
static struct i2c_driver tps65090_driver = {
. driver = {
. name = " tps65090 " ,
2013-01-29 13:05:15 +04:00
. of_match_table = of_match_ptr ( tps65090_of_match ) ,
2012-03-16 09:40:19 +04:00
} ,
. probe = tps65090_i2c_probe ,
2012-11-19 22:20:24 +04:00
. remove = tps65090_i2c_remove ,
2012-03-16 09:40:19 +04:00
. id_table = tps65090_id_table ,
} ;
static int __init tps65090_init ( void )
{
return i2c_add_driver ( & tps65090_driver ) ;
}
subsys_initcall ( tps65090_init ) ;
static void __exit tps65090_exit ( void )
{
i2c_del_driver ( & tps65090_driver ) ;
}
module_exit ( tps65090_exit ) ;
MODULE_DESCRIPTION ( " TPS65090 core driver " ) ;
MODULE_AUTHOR ( " Venu Byravarasu <vbyravarasu@nvidia.com> " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;