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>
# include <linux/regmap.h>
# include <linux/err.h>
# define NUM_INT_REG 2
# define TOTAL_NUM_REG 0x18
/* interrupt status registers */
# define TPS65090_INT_STS 0x0
# define TPS65090_INT_STS2 0x1
/* interrupt mask registers */
# define TPS65090_INT_MSK 0x2
# define TPS65090_INT_MSK2 0x3
struct tps65090_irq_data {
u8 mask_reg ;
u8 mask_pos ;
} ;
# define TPS65090_IRQ(_reg, _mask_pos) \
{ \
. mask_reg = ( _reg ) , \
. mask_pos = ( _mask_pos ) , \
}
static const struct tps65090_irq_data tps65090_irqs [ ] = {
[ 0 ] = TPS65090_IRQ ( 0 , 0 ) ,
[ 1 ] = TPS65090_IRQ ( 0 , 1 ) ,
[ 2 ] = TPS65090_IRQ ( 0 , 2 ) ,
[ 3 ] = TPS65090_IRQ ( 0 , 3 ) ,
[ 4 ] = TPS65090_IRQ ( 0 , 4 ) ,
[ 5 ] = TPS65090_IRQ ( 0 , 5 ) ,
[ 6 ] = TPS65090_IRQ ( 0 , 6 ) ,
[ 7 ] = TPS65090_IRQ ( 0 , 7 ) ,
[ 8 ] = TPS65090_IRQ ( 1 , 0 ) ,
[ 9 ] = TPS65090_IRQ ( 1 , 1 ) ,
[ 10 ] = TPS65090_IRQ ( 1 , 2 ) ,
[ 11 ] = TPS65090_IRQ ( 1 , 3 ) ,
[ 12 ] = TPS65090_IRQ ( 1 , 4 ) ,
[ 13 ] = TPS65090_IRQ ( 1 , 5 ) ,
[ 14 ] = TPS65090_IRQ ( 1 , 6 ) ,
[ 15 ] = TPS65090_IRQ ( 1 , 7 ) ,
} ;
static struct mfd_cell tps65090s [ ] = {
{
2012-07-18 17:03:42 +04:00
. name = " tps65090-pmic " ,
2012-03-16 09:40:19 +04:00
} ,
{
2012-07-18 17:03:42 +04:00
. name = " tps65090-regulator " ,
2012-03-16 09:40:19 +04:00
} ,
} ;
int tps65090_write ( struct device * dev , int reg , uint8_t val )
{
struct tps65090 * tps = dev_get_drvdata ( dev ) ;
return regmap_write ( tps - > rmap , reg , val ) ;
}
EXPORT_SYMBOL_GPL ( tps65090_write ) ;
int tps65090_read ( struct device * dev , int reg , uint8_t * val )
{
struct tps65090 * tps = dev_get_drvdata ( dev ) ;
unsigned int temp_val ;
int ret ;
ret = regmap_read ( tps - > rmap , reg , & temp_val ) ;
if ( ! ret )
* val = temp_val ;
return ret ;
}
EXPORT_SYMBOL_GPL ( tps65090_read ) ;
int tps65090_set_bits ( struct device * dev , int reg , uint8_t bit_num )
{
struct tps65090 * tps = dev_get_drvdata ( dev ) ;
return regmap_update_bits ( tps - > rmap , reg , BIT ( bit_num ) , ~ 0u ) ;
}
EXPORT_SYMBOL_GPL ( tps65090_set_bits ) ;
int tps65090_clr_bits ( struct device * dev , int reg , uint8_t bit_num )
{
struct tps65090 * tps = dev_get_drvdata ( dev ) ;
return regmap_update_bits ( tps - > rmap , reg , BIT ( bit_num ) , 0u ) ;
}
EXPORT_SYMBOL_GPL ( tps65090_clr_bits ) ;
static void tps65090_irq_lock ( struct irq_data * data )
{
struct tps65090 * tps65090 = irq_data_get_irq_chip_data ( data ) ;
mutex_lock ( & tps65090 - > irq_lock ) ;
}
static void tps65090_irq_mask ( struct irq_data * irq_data )
{
struct tps65090 * tps65090 = irq_data_get_irq_chip_data ( irq_data ) ;
unsigned int __irq = irq_data - > hwirq ;
const struct tps65090_irq_data * data = & tps65090_irqs [ __irq ] ;
tps65090_set_bits ( tps65090 - > dev , ( TPS65090_INT_MSK + data - > mask_reg ) ,
data - > mask_pos ) ;
}
static void tps65090_irq_unmask ( struct irq_data * irq_data )
{
struct tps65090 * tps65090 = irq_data_get_irq_chip_data ( irq_data ) ;
unsigned int __irq = irq_data - > irq - tps65090 - > irq_base ;
const struct tps65090_irq_data * data = & tps65090_irqs [ __irq ] ;
tps65090_clr_bits ( tps65090 - > dev , ( TPS65090_INT_MSK + data - > mask_reg ) ,
data - > mask_pos ) ;
}
static void tps65090_irq_sync_unlock ( struct irq_data * data )
{
struct tps65090 * tps65090 = irq_data_get_irq_chip_data ( data ) ;
mutex_unlock ( & tps65090 - > irq_lock ) ;
}
static irqreturn_t tps65090_irq ( int irq , void * data )
{
struct tps65090 * tps65090 = data ;
int ret = 0 ;
u8 status , mask ;
unsigned long int acks = 0 ;
int i ;
for ( i = 0 ; i < NUM_INT_REG ; i + + ) {
ret = tps65090_read ( tps65090 - > dev , TPS65090_INT_MSK + i , & mask ) ;
if ( ret < 0 ) {
dev_err ( tps65090 - > dev ,
" failed to read mask reg [addr:%d] \n " ,
TPS65090_INT_MSK + i ) ;
return IRQ_NONE ;
}
ret = tps65090_read ( tps65090 - > dev , TPS65090_INT_STS + i ,
& status ) ;
if ( ret < 0 ) {
dev_err ( tps65090 - > dev ,
" failed to read status reg [addr:%d] \n " ,
TPS65090_INT_STS + i ) ;
return IRQ_NONE ;
}
if ( status ) {
/* Ack only those interrupts which are not masked */
status & = ( ~ mask ) ;
ret = tps65090_write ( tps65090 - > dev ,
TPS65090_INT_STS + i , status ) ;
if ( ret < 0 ) {
dev_err ( tps65090 - > dev ,
" failed to write interrupt status \n " ) ;
return IRQ_NONE ;
}
acks | = ( status < < ( i * 8 ) ) ;
}
}
for_each_set_bit ( i , & acks , ARRAY_SIZE ( tps65090_irqs ) )
handle_nested_irq ( tps65090 - > irq_base + i ) ;
return acks ? IRQ_HANDLED : IRQ_NONE ;
}
static int __devinit tps65090_irq_init ( struct tps65090 * tps65090 , int irq ,
int irq_base )
{
int i , ret ;
if ( ! irq_base ) {
dev_err ( tps65090 - > dev , " IRQ base not set \n " ) ;
return - EINVAL ;
}
mutex_init ( & tps65090 - > irq_lock ) ;
for ( i = 0 ; i < NUM_INT_REG ; i + + )
tps65090_write ( tps65090 - > dev , TPS65090_INT_MSK + i , 0xFF ) ;
for ( i = 0 ; i < NUM_INT_REG ; i + + )
tps65090_write ( tps65090 - > dev , TPS65090_INT_STS + i , 0xff ) ;
tps65090 - > irq_base = irq_base ;
tps65090 - > irq_chip . name = " tps65090 " ;
tps65090 - > irq_chip . irq_mask = tps65090_irq_mask ;
tps65090 - > irq_chip . irq_unmask = tps65090_irq_unmask ;
tps65090 - > irq_chip . irq_bus_lock = tps65090_irq_lock ;
tps65090 - > irq_chip . irq_bus_sync_unlock = tps65090_irq_sync_unlock ;
for ( i = 0 ; i < ARRAY_SIZE ( tps65090_irqs ) ; i + + ) {
int __irq = i + tps65090 - > irq_base ;
irq_set_chip_data ( __irq , tps65090 ) ;
irq_set_chip_and_handler ( __irq , & tps65090 - > irq_chip ,
handle_simple_irq ) ;
irq_set_nested_thread ( __irq , 1 ) ;
# ifdef CONFIG_ARM
set_irq_flags ( __irq , IRQF_VALID ) ;
# endif
}
ret = request_threaded_irq ( irq , NULL , tps65090_irq , IRQF_ONESHOT ,
" tps65090 " , tps65090 ) ;
if ( ! ret ) {
device_init_wakeup ( tps65090 - > dev , 1 ) ;
enable_irq_wake ( irq ) ;
}
return ret ;
}
static bool is_volatile_reg ( struct device * dev , unsigned int reg )
{
2012-09-17 09:30:47 +04:00
if ( reg = = TPS65090_INT_STS )
2012-03-16 09:40:19 +04:00
return true ;
else
return false ;
}
static const struct regmap_config tps65090_regmap_config = {
. reg_bits = 8 ,
. val_bits = 8 ,
. max_register = TOTAL_NUM_REG ,
. num_reg_defaults_raw = TOTAL_NUM_REG ,
. cache_type = REGCACHE_RBTREE ,
. volatile_reg = is_volatile_reg ,
} ;
static int __devinit tps65090_i2c_probe ( struct i2c_client * client ,
const struct i2c_device_id * id )
{
struct tps65090_platform_data * pdata = client - > dev . platform_data ;
struct tps65090 * tps65090 ;
int ret ;
if ( ! pdata ) {
dev_err ( & client - > dev , " tps65090 requires platform data \n " ) ;
return - EINVAL ;
}
tps65090 = devm_kzalloc ( & client - > dev , sizeof ( struct tps65090 ) ,
GFP_KERNEL ) ;
if ( tps65090 = = NULL )
return - ENOMEM ;
tps65090 - > client = client ;
tps65090 - > dev = & client - > dev ;
i2c_set_clientdata ( client , tps65090 ) ;
mutex_init ( & tps65090 - > lock ) ;
if ( client - > irq ) {
ret = tps65090_irq_init ( tps65090 , client - > irq , pdata - > irq_base ) ;
if ( ret ) {
dev_err ( & client - > dev , " IRQ init failed with err: %d \n " ,
ret ) ;
goto err_exit ;
}
}
2012-04-25 06:04:58 +04:00
tps65090 - > rmap = devm_regmap_init_i2c ( tps65090 - > 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-03-16 09:40:19 +04:00
goto err_irq_exit ;
2012-04-25 05:30:36 +04:00
}
2012-03-16 09:40:19 +04:00
ret = mfd_add_devices ( tps65090 - > dev , - 1 , tps65090s ,
2012-09-11 11:16:36 +04:00
ARRAY_SIZE ( tps65090s ) , NULL , 0 , NULL ) ;
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 )
free_irq ( client - > irq , tps65090 ) ;
err_exit :
return ret ;
}
static int __devexit tps65090_i2c_remove ( struct i2c_client * client )
{
struct tps65090 * tps65090 = i2c_get_clientdata ( client ) ;
mfd_remove_devices ( tps65090 - > dev ) ;
if ( client - > irq )
free_irq ( client - > irq , tps65090 ) ;
return 0 ;
}
2012-05-07 13:03:17 +04:00
# ifdef CONFIG_PM_SLEEP
2012-05-07 13:03:18 +04:00
static int tps65090_suspend ( struct device * dev )
2012-03-16 09:40:19 +04:00
{
2012-05-07 13:03:18 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
2012-03-16 09:40:19 +04:00
if ( client - > irq )
disable_irq ( client - > irq ) ;
return 0 ;
}
2012-05-07 13:03:18 +04:00
static int tps65090_resume ( struct device * dev )
2012-03-16 09:40:19 +04:00
{
2012-05-07 13:03:18 +04:00
struct i2c_client * client = to_i2c_client ( dev ) ;
2012-03-16 09:40:19 +04:00
if ( client - > irq )
enable_irq ( client - > irq ) ;
return 0 ;
}
# endif
2012-05-07 13:03:18 +04:00
static const struct dev_pm_ops tps65090_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( tps65090_suspend , tps65090_resume )
} ;
2012-03-16 09:40:19 +04:00
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 " ,
. owner = THIS_MODULE ,
2012-05-07 13:03:18 +04:00
. pm = & tps65090_pm_ops ,
2012-03-16 09:40:19 +04:00
} ,
. probe = tps65090_i2c_probe ,
. remove = __devexit_p ( tps65090_i2c_remove ) ,
. 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 " ) ;