2010-05-11 01:43: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/init.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
# include <linux/gpio.h>
2012-09-07 15:14:59 +04:00
# include <linux/of.h>
2010-05-11 01:43:47 +04:00
# include <linux/interrupt.h>
2010-12-13 07:03:12 +03:00
# include <linux/mfd/tc3589x.h>
2010-05-11 01:43:47 +04:00
/*
* These registers are modified under the irq bus lock and cached to avoid
* unnecessary writes in bus_sync_unlock .
*/
enum { REG_IBE , REG_IEV , REG_IS , REG_IE } ;
# define CACHE_NR_REGS 4
# define CACHE_NR_BANKS 3
2010-12-13 07:03:14 +03:00
struct tc3589x_gpio {
2010-05-11 01:43:47 +04:00
struct gpio_chip chip ;
2010-12-13 07:03:14 +03:00
struct tc3589x * tc3589x ;
2010-05-11 01:43:47 +04:00
struct device * dev ;
struct mutex irq_lock ;
/* Caches of interrupt control registers for bus_lock */
u8 regs [ CACHE_NR_REGS ] [ CACHE_NR_BANKS ] ;
u8 oldregs [ CACHE_NR_REGS ] [ CACHE_NR_BANKS ] ;
} ;
2010-12-13 07:03:14 +03:00
static inline struct tc3589x_gpio * to_tc3589x_gpio ( struct gpio_chip * chip )
2010-05-11 01:43:47 +04:00
{
2010-12-13 07:03:14 +03:00
return container_of ( chip , struct tc3589x_gpio , chip ) ;
2010-05-11 01:43:47 +04:00
}
2010-12-13 07:03:14 +03:00
static int tc3589x_gpio_get ( struct gpio_chip * chip , unsigned offset )
2010-05-11 01:43:47 +04:00
{
2010-12-13 07:03:14 +03:00
struct tc3589x_gpio * tc3589x_gpio = to_tc3589x_gpio ( chip ) ;
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
u8 reg = TC3589x_GPIODATA0 + ( offset / 8 ) * 2 ;
2010-05-11 01:43:47 +04:00
u8 mask = 1 < < ( offset % 8 ) ;
int ret ;
2010-12-13 07:03:14 +03:00
ret = tc3589x_reg_read ( tc3589x , reg ) ;
2010-05-11 01:43:47 +04:00
if ( ret < 0 )
return ret ;
return ret & mask ;
}
2010-12-13 07:03:14 +03:00
static void tc3589x_gpio_set ( struct gpio_chip * chip , unsigned offset , int val )
2010-05-11 01:43:47 +04:00
{
2010-12-13 07:03:14 +03:00
struct tc3589x_gpio * tc3589x_gpio = to_tc3589x_gpio ( chip ) ;
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
u8 reg = TC3589x_GPIODATA0 + ( offset / 8 ) * 2 ;
2010-05-11 01:43:47 +04:00
unsigned pos = offset % 8 ;
u8 data [ ] = { ! ! val < < pos , 1 < < pos } ;
2010-12-13 07:03:14 +03:00
tc3589x_block_write ( tc3589x , reg , ARRAY_SIZE ( data ) , data ) ;
2010-05-11 01:43:47 +04:00
}
2010-12-13 07:03:14 +03:00
static int tc3589x_gpio_direction_output ( struct gpio_chip * chip ,
2010-05-11 01:43:47 +04:00
unsigned offset , int val )
{
2010-12-13 07:03:14 +03:00
struct tc3589x_gpio * tc3589x_gpio = to_tc3589x_gpio ( chip ) ;
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
u8 reg = TC3589x_GPIODIR0 + offset / 8 ;
2010-05-11 01:43:47 +04:00
unsigned pos = offset % 8 ;
2010-12-13 07:03:14 +03:00
tc3589x_gpio_set ( chip , offset , val ) ;
2010-05-11 01:43:47 +04:00
2010-12-13 07:03:14 +03:00
return tc3589x_set_bits ( tc3589x , reg , 1 < < pos , 1 < < pos ) ;
2010-05-11 01:43:47 +04:00
}
2010-12-13 07:03:14 +03:00
static int tc3589x_gpio_direction_input ( struct gpio_chip * chip ,
2010-05-11 01:43:47 +04:00
unsigned offset )
{
2010-12-13 07:03:14 +03:00
struct tc3589x_gpio * tc3589x_gpio = to_tc3589x_gpio ( chip ) ;
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
u8 reg = TC3589x_GPIODIR0 + offset / 8 ;
2010-05-11 01:43:47 +04:00
unsigned pos = offset % 8 ;
2010-12-13 07:03:14 +03:00
return tc3589x_set_bits ( tc3589x , reg , 1 < < pos , 0 ) ;
2010-05-11 01:43:47 +04:00
}
static struct gpio_chip template_chip = {
2010-12-13 07:03:14 +03:00
. label = " tc3589x " ,
2010-05-11 01:43:47 +04:00
. owner = THIS_MODULE ,
2010-12-13 07:03:14 +03:00
. direction_input = tc3589x_gpio_direction_input ,
. get = tc3589x_gpio_get ,
. direction_output = tc3589x_gpio_direction_output ,
. set = tc3589x_gpio_set ,
2013-12-04 17:42:46 +04:00
. can_sleep = true ,
2010-05-11 01:43:47 +04:00
} ;
2011-01-13 04:00:19 +03:00
static int tc3589x_gpio_irq_set_type ( struct irq_data * d , unsigned int type )
2010-05-11 01:43:47 +04:00
{
2014-04-09 15:38:33 +04:00
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct tc3589x_gpio * tc3589x_gpio = container_of ( gc , struct tc3589x_gpio , chip ) ;
2012-09-07 15:14:58 +04:00
int offset = d - > hwirq ;
2010-05-11 01:43:47 +04:00
int regoffset = offset / 8 ;
int mask = 1 < < ( offset % 8 ) ;
if ( type = = IRQ_TYPE_EDGE_BOTH ) {
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > regs [ REG_IBE ] [ regoffset ] | = mask ;
2010-05-11 01:43:47 +04:00
return 0 ;
}
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > regs [ REG_IBE ] [ regoffset ] & = ~ mask ;
2010-05-11 01:43:47 +04:00
if ( type = = IRQ_TYPE_LEVEL_LOW | | type = = IRQ_TYPE_LEVEL_HIGH )
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > regs [ REG_IS ] [ regoffset ] | = mask ;
2010-05-11 01:43:47 +04:00
else
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > regs [ REG_IS ] [ regoffset ] & = ~ mask ;
2010-05-11 01:43:47 +04:00
if ( type = = IRQ_TYPE_EDGE_RISING | | type = = IRQ_TYPE_LEVEL_HIGH )
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > regs [ REG_IEV ] [ regoffset ] | = mask ;
2010-05-11 01:43:47 +04:00
else
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > regs [ REG_IEV ] [ regoffset ] & = ~ mask ;
2010-05-11 01:43:47 +04:00
return 0 ;
}
2011-01-13 04:00:19 +03:00
static void tc3589x_gpio_irq_lock ( struct irq_data * d )
2010-05-11 01:43:47 +04:00
{
2014-04-09 15:38:33 +04:00
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct tc3589x_gpio * tc3589x_gpio = container_of ( gc , struct tc3589x_gpio , chip ) ;
2010-05-11 01:43:47 +04:00
2010-12-13 07:03:14 +03:00
mutex_lock ( & tc3589x_gpio - > irq_lock ) ;
2010-05-11 01:43:47 +04:00
}
2011-01-13 04:00:19 +03:00
static void tc3589x_gpio_irq_sync_unlock ( struct irq_data * d )
2010-05-11 01:43:47 +04:00
{
2014-04-09 15:38:33 +04:00
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct tc3589x_gpio * tc3589x_gpio = container_of ( gc , struct tc3589x_gpio , chip ) ;
2010-12-13 07:03:14 +03:00
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
2010-05-11 01:43:47 +04:00
static const u8 regmap [ ] = {
2010-12-13 07:03:14 +03:00
[ REG_IBE ] = TC3589x_GPIOIBE0 ,
[ REG_IEV ] = TC3589x_GPIOIEV0 ,
[ REG_IS ] = TC3589x_GPIOIS0 ,
[ REG_IE ] = TC3589x_GPIOIE0 ,
2010-05-11 01:43:47 +04:00
} ;
int i , j ;
for ( i = 0 ; i < CACHE_NR_REGS ; i + + ) {
for ( j = 0 ; j < CACHE_NR_BANKS ; j + + ) {
2010-12-13 07:03:14 +03:00
u8 old = tc3589x_gpio - > oldregs [ i ] [ j ] ;
u8 new = tc3589x_gpio - > regs [ i ] [ j ] ;
2010-05-11 01:43:47 +04:00
if ( new = = old )
continue ;
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > oldregs [ i ] [ j ] = new ;
tc3589x_reg_write ( tc3589x , regmap [ i ] + j * 8 , new ) ;
2010-05-11 01:43:47 +04:00
}
}
2010-12-13 07:03:14 +03:00
mutex_unlock ( & tc3589x_gpio - > irq_lock ) ;
2010-05-11 01:43:47 +04:00
}
2011-01-13 04:00:19 +03:00
static void tc3589x_gpio_irq_mask ( struct irq_data * d )
2010-05-11 01:43:47 +04:00
{
2014-04-09 15:38:33 +04:00
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct tc3589x_gpio * tc3589x_gpio = container_of ( gc , struct tc3589x_gpio , chip ) ;
2012-09-07 15:14:58 +04:00
int offset = d - > hwirq ;
2010-05-11 01:43:47 +04:00
int regoffset = offset / 8 ;
int mask = 1 < < ( offset % 8 ) ;
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > regs [ REG_IE ] [ regoffset ] & = ~ mask ;
2010-05-11 01:43:47 +04:00
}
2011-01-13 04:00:19 +03:00
static void tc3589x_gpio_irq_unmask ( struct irq_data * d )
2010-05-11 01:43:47 +04:00
{
2014-04-09 15:38:33 +04:00
struct gpio_chip * gc = irq_data_get_irq_chip_data ( d ) ;
struct tc3589x_gpio * tc3589x_gpio = container_of ( gc , struct tc3589x_gpio , chip ) ;
2012-09-07 15:14:58 +04:00
int offset = d - > hwirq ;
2010-05-11 01:43:47 +04:00
int regoffset = offset / 8 ;
int mask = 1 < < ( offset % 8 ) ;
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > regs [ REG_IE ] [ regoffset ] | = mask ;
2010-05-11 01:43:47 +04:00
}
2010-12-13 07:03:14 +03:00
static struct irq_chip tc3589x_gpio_irq_chip = {
. name = " tc3589x-gpio " ,
2011-01-13 04:00:19 +03:00
. irq_bus_lock = tc3589x_gpio_irq_lock ,
. irq_bus_sync_unlock = tc3589x_gpio_irq_sync_unlock ,
. irq_mask = tc3589x_gpio_irq_mask ,
. irq_unmask = tc3589x_gpio_irq_unmask ,
. irq_set_type = tc3589x_gpio_irq_set_type ,
2010-05-11 01:43:47 +04:00
} ;
2010-12-13 07:03:14 +03:00
static irqreturn_t tc3589x_gpio_irq ( int irq , void * dev )
2010-05-11 01:43:47 +04:00
{
2010-12-13 07:03:14 +03:00
struct tc3589x_gpio * tc3589x_gpio = dev ;
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
2010-05-11 01:43:47 +04:00
u8 status [ CACHE_NR_BANKS ] ;
int ret ;
int i ;
2010-12-13 07:03:14 +03:00
ret = tc3589x_block_read ( tc3589x , TC3589x_GPIOMIS0 ,
2010-05-11 01:43:47 +04:00
ARRAY_SIZE ( status ) , status ) ;
if ( ret < 0 )
return IRQ_NONE ;
for ( i = 0 ; i < ARRAY_SIZE ( status ) ; i + + ) {
unsigned int stat = status [ i ] ;
if ( ! stat )
continue ;
while ( stat ) {
int bit = __ffs ( stat ) ;
int line = i * 8 + bit ;
2014-04-09 15:38:33 +04:00
int irq = irq_find_mapping ( tc3589x_gpio - > chip . irqdomain ,
line ) ;
2010-05-11 01:43:47 +04:00
2013-10-11 21:06:12 +04:00
handle_nested_irq ( irq ) ;
2010-05-11 01:43:47 +04:00
stat & = ~ ( 1 < < bit ) ;
}
2010-12-13 07:03:14 +03:00
tc3589x_reg_write ( tc3589x , TC3589x_GPIOIC0 + i , status [ i ] ) ;
2010-05-11 01:43:47 +04:00
}
return IRQ_HANDLED ;
}
2012-11-19 22:22:34 +04:00
static int tc3589x_gpio_probe ( struct platform_device * pdev )
2010-05-11 01:43:47 +04:00
{
2010-12-13 07:03:14 +03:00
struct tc3589x * tc3589x = dev_get_drvdata ( pdev - > dev . parent ) ;
struct tc3589x_gpio_platform_data * pdata ;
2012-09-07 15:14:59 +04:00
struct device_node * np = pdev - > dev . of_node ;
2010-12-13 07:03:14 +03:00
struct tc3589x_gpio * tc3589x_gpio ;
2010-05-11 01:43:47 +04:00
int ret ;
int irq ;
2010-12-13 07:03:14 +03:00
pdata = tc3589x - > pdata - > gpio ;
2012-09-07 15:14:59 +04:00
if ( ! ( pdata | | np ) ) {
dev_err ( & pdev - > dev , " No platform data or Device Tree found \n " ) ;
return - EINVAL ;
}
2010-05-11 01:43:47 +04:00
irq = platform_get_irq ( pdev , 0 ) ;
if ( irq < 0 )
return irq ;
2014-04-09 14:38:56 +04:00
tc3589x_gpio = devm_kzalloc ( & pdev - > dev , sizeof ( struct tc3589x_gpio ) ,
GFP_KERNEL ) ;
2010-12-13 07:03:14 +03:00
if ( ! tc3589x_gpio )
2010-05-11 01:43:47 +04:00
return - ENOMEM ;
2010-12-13 07:03:14 +03:00
mutex_init ( & tc3589x_gpio - > irq_lock ) ;
2010-05-11 01:43:47 +04:00
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > dev = & pdev - > dev ;
tc3589x_gpio - > tc3589x = tc3589x ;
2010-05-11 01:43:47 +04:00
2010-12-13 07:03:14 +03:00
tc3589x_gpio - > chip = template_chip ;
tc3589x_gpio - > chip . ngpio = tc3589x - > num_gpio ;
tc3589x_gpio - > chip . dev = & pdev - > dev ;
2014-10-28 13:06:56 +03:00
tc3589x_gpio - > chip . base = - 1 ;
2010-05-11 01:43:47 +04:00
2012-09-07 15:14:59 +04:00
# ifdef CONFIG_OF_GPIO
2013-03-20 16:16:02 +04:00
tc3589x_gpio - > chip . of_node = np ;
2012-09-07 15:14:59 +04:00
# endif
2010-05-11 01:43:47 +04:00
/* Bring the GPIO module out of reset */
2010-12-13 07:03:14 +03:00
ret = tc3589x_set_bits ( tc3589x , TC3589x_RSTCTRL ,
TC3589x_RSTCTRL_GPIRST , 0 ) ;
2010-05-11 01:43:47 +04:00
if ( ret < 0 )
2014-04-09 14:38:56 +04:00
return ret ;
2010-05-11 01:43:47 +04:00
2014-04-09 14:38:56 +04:00
ret = devm_request_threaded_irq ( & pdev - > dev ,
irq , NULL , tc3589x_gpio_irq ,
IRQF_ONESHOT , " tc3589x-gpio " ,
tc3589x_gpio ) ;
2010-05-11 01:43:47 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " unable to get irq: %d \n " , ret ) ;
2014-04-09 14:38:56 +04:00
return ret ;
2010-05-11 01:43:47 +04:00
}
2010-12-13 07:03:14 +03:00
ret = gpiochip_add ( & tc3589x_gpio - > chip ) ;
2010-05-11 01:43:47 +04:00
if ( ret ) {
dev_err ( & pdev - > dev , " unable to add gpiochip: %d \n " , ret ) ;
2014-04-09 14:38:56 +04:00
return ret ;
2010-05-11 01:43:47 +04:00
}
2014-04-09 15:38:33 +04:00
ret = gpiochip_irqchip_add ( & tc3589x_gpio - > chip ,
& tc3589x_gpio_irq_chip ,
0 ,
handle_simple_irq ,
IRQ_TYPE_NONE ) ;
if ( ret ) {
dev_err ( & pdev - > dev ,
" could not connect irqchip to gpiochip \n " ) ;
return ret ;
}
2014-09-26 16:19:52 +04:00
gpiochip_set_chained_irqchip ( & tc3589x_gpio - > chip ,
& tc3589x_gpio_irq_chip ,
irq ,
NULL ) ;
2012-09-07 15:14:59 +04:00
if ( pdata & & pdata - > setup )
2010-12-13 07:03:14 +03:00
pdata - > setup ( tc3589x , tc3589x_gpio - > chip . base ) ;
2010-09-13 16:04:02 +04:00
2010-12-13 07:03:14 +03:00
platform_set_drvdata ( pdev , tc3589x_gpio ) ;
2010-05-11 01:43:47 +04:00
return 0 ;
}
2012-11-19 22:25:50 +04:00
static int tc3589x_gpio_remove ( struct platform_device * pdev )
2010-05-11 01:43:47 +04:00
{
2010-12-13 07:03:14 +03:00
struct tc3589x_gpio * tc3589x_gpio = platform_get_drvdata ( pdev ) ;
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
struct tc3589x_gpio_platform_data * pdata = tc3589x - > pdata - > gpio ;
2010-05-11 01:43:47 +04:00
2012-09-07 15:14:59 +04:00
if ( pdata & & pdata - > remove )
2010-12-13 07:03:14 +03:00
pdata - > remove ( tc3589x , tc3589x_gpio - > chip . base ) ;
2010-09-13 16:04:02 +04:00
2014-07-13 00:30:12 +04:00
gpiochip_remove ( & tc3589x_gpio - > chip ) ;
2010-05-11 01:43:47 +04:00
return 0 ;
}
2010-12-13 07:03:14 +03:00
static struct platform_driver tc3589x_gpio_driver = {
. driver . name = " tc3589x-gpio " ,
2010-05-11 01:43:47 +04:00
. driver . owner = THIS_MODULE ,
2010-12-13 07:03:14 +03:00
. probe = tc3589x_gpio_probe ,
2012-11-19 22:20:08 +04:00
. remove = tc3589x_gpio_remove ,
2010-05-11 01:43:47 +04:00
} ;
2010-12-13 07:03:14 +03:00
static int __init tc3589x_gpio_init ( void )
2010-05-11 01:43:47 +04:00
{
2010-12-13 07:03:14 +03:00
return platform_driver_register ( & tc3589x_gpio_driver ) ;
2010-05-11 01:43:47 +04:00
}
2010-12-13 07:03:14 +03:00
subsys_initcall ( tc3589x_gpio_init ) ;
2010-05-11 01:43:47 +04:00
2010-12-13 07:03:14 +03:00
static void __exit tc3589x_gpio_exit ( void )
2010-05-11 01:43:47 +04:00
{
2010-12-13 07:03:14 +03:00
platform_driver_unregister ( & tc3589x_gpio_driver ) ;
2010-05-11 01:43:47 +04:00
}
2010-12-13 07:03:14 +03:00
module_exit ( tc3589x_gpio_exit ) ;
2010-05-11 01:43:47 +04:00
MODULE_LICENSE ( " GPL v2 " ) ;
2010-12-13 07:03:14 +03:00
MODULE_DESCRIPTION ( " TC3589x GPIO driver " ) ;
2010-05-11 01:43:47 +04:00
MODULE_AUTHOR ( " Hanumath Prasad, Rabin Vincent " ) ;