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/init.h>
# include <linux/platform_device.h>
# include <linux/slab.h>
2016-04-05 16:09:09 +03:00
# include <linux/gpio/driver.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>
2016-04-05 16:09:09 +03:00
# include <linux/bitops.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 ] ;
} ;
2016-09-19 11:14:29 +03:00
static int tc3589x_gpio_get ( struct gpio_chip * chip , unsigned int offset )
2010-05-11 01:43:47 +04:00
{
2015-12-03 17:37:29 +03:00
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( chip ) ;
2010-12-13 07:03:14 +03:00
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
u8 reg = TC3589x_GPIODATA0 + ( offset / 8 ) * 2 ;
2016-04-05 16:09:09 +03:00
u8 mask = BIT ( offset % 8 ) ;
2010-05-11 01:43:47 +04:00
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 ;
2015-12-21 13:42:30 +03:00
return ! ! ( ret & mask ) ;
2010-05-11 01:43:47 +04:00
}
2016-09-19 11:14:29 +03:00
static void tc3589x_gpio_set ( struct gpio_chip * chip , unsigned int offset , int val )
2010-05-11 01:43:47 +04:00
{
2015-12-03 17:37:29 +03:00
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( chip ) ;
2010-12-13 07:03:14 +03:00
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
u8 reg = TC3589x_GPIODATA0 + ( offset / 8 ) * 2 ;
2016-09-19 11:14:29 +03:00
unsigned int pos = offset % 8 ;
2016-04-05 16:09:09 +03:00
u8 data [ ] = { val ? BIT ( pos ) : 0 , BIT ( pos ) } ;
2010-05-11 01:43:47 +04:00
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 ,
2016-09-19 11:14:29 +03:00
unsigned int offset , int val )
2010-05-11 01:43:47 +04:00
{
2015-12-03 17:37:29 +03:00
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( chip ) ;
2010-12-13 07:03:14 +03:00
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
u8 reg = TC3589x_GPIODIR0 + offset / 8 ;
2016-09-19 11:14:29 +03:00
unsigned int pos = offset % 8 ;
2010-05-11 01:43:47 +04:00
2010-12-13 07:03:14 +03:00
tc3589x_gpio_set ( chip , offset , val ) ;
2010-05-11 01:43:47 +04:00
2016-04-05 16:09:09 +03:00
return tc3589x_set_bits ( tc3589x , reg , BIT ( pos ) , BIT ( 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 ,
2016-09-19 11:14:29 +03:00
unsigned int offset )
2010-05-11 01:43:47 +04:00
{
2015-12-03 17:37:29 +03:00
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( chip ) ;
2010-12-13 07:03:14 +03:00
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
u8 reg = TC3589x_GPIODIR0 + offset / 8 ;
2016-09-19 11:14:29 +03:00
unsigned int pos = offset % 8 ;
2010-05-11 01:43:47 +04:00
2016-04-05 16:09:09 +03:00
return tc3589x_set_bits ( tc3589x , reg , BIT ( pos ) , 0 ) ;
2010-05-11 01:43:47 +04:00
}
2016-09-19 11:08:56 +03:00
static int tc3589x_gpio_get_direction ( struct gpio_chip * chip ,
2016-09-19 11:14:29 +03:00
unsigned int offset )
2016-09-19 11:08:56 +03:00
{
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( chip ) ;
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
u8 reg = TC3589x_GPIODIR0 + offset / 8 ;
2016-09-19 11:14:29 +03:00
unsigned int pos = offset % 8 ;
2016-09-19 11:08:56 +03:00
int ret ;
ret = tc3589x_reg_read ( tc3589x , reg ) ;
if ( ret < 0 )
return ret ;
2016-11-14 17:10:29 +03:00
return ! ( ret & BIT ( pos ) ) ;
2016-09-19 11:08:56 +03:00
}
2017-01-23 15:34:34 +03:00
static int tc3589x_gpio_set_config ( struct gpio_chip * chip , unsigned int offset ,
unsigned long config )
2016-04-05 16:11:11 +03:00
{
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( chip ) ;
struct tc3589x * tc3589x = tc3589x_gpio - > tc3589x ;
/*
* These registers are alterated at each second address
* ODM bit 0 = drive to GND or Hi - Z ( open drain )
* ODM bit 1 = drive to VDD or Hi - Z ( open source )
*/
u8 odmreg = TC3589x_GPIOODM0 + ( offset / 8 ) * 2 ;
u8 odereg = TC3589x_GPIOODE0 + ( offset / 8 ) * 2 ;
2016-09-19 11:14:29 +03:00
unsigned int pos = offset % 8 ;
2016-04-05 16:11:11 +03:00
int ret ;
2017-01-23 15:34:34 +03:00
switch ( pinconf_to_config_param ( config ) ) {
case PIN_CONFIG_DRIVE_OPEN_DRAIN :
2016-04-05 16:11:11 +03:00
/* Set open drain mode */
ret = tc3589x_set_bits ( tc3589x , odmreg , BIT ( pos ) , 0 ) ;
if ( ret )
return ret ;
/* Enable open drain/source mode */
return tc3589x_set_bits ( tc3589x , odereg , BIT ( pos ) , BIT ( pos ) ) ;
2017-01-23 15:34:34 +03:00
case PIN_CONFIG_DRIVE_OPEN_SOURCE :
2016-04-05 16:11:11 +03:00
/* Set open source mode */
ret = tc3589x_set_bits ( tc3589x , odmreg , BIT ( pos ) , BIT ( pos ) ) ;
if ( ret )
return ret ;
/* Enable open drain/source mode */
return tc3589x_set_bits ( tc3589x , odereg , BIT ( pos ) , BIT ( pos ) ) ;
2017-01-23 15:34:34 +03:00
case PIN_CONFIG_DRIVE_PUSH_PULL :
2016-04-05 16:11:11 +03:00
/* Disable open drain/source mode */
return tc3589x_set_bits ( tc3589x , odereg , BIT ( pos ) , 0 ) ;
default :
break ;
}
return - ENOTSUPP ;
}
2016-09-11 15:14:37 +03:00
static const 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
. get = tc3589x_gpio_get ,
. set = tc3589x_gpio_set ,
2016-09-19 11:08:56 +03:00
. direction_output = tc3589x_gpio_direction_output ,
. direction_input = tc3589x_gpio_direction_input ,
. get_direction = tc3589x_gpio_get_direction ,
2017-01-23 15:34:34 +03:00
. set_config = tc3589x_gpio_set_config ,
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 ) ;
2015-12-03 17:37:29 +03:00
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( gc ) ;
2012-09-07 15:14:58 +04:00
int offset = d - > hwirq ;
2010-05-11 01:43:47 +04:00
int regoffset = offset / 8 ;
2016-04-05 16:09:09 +03:00
int mask = BIT ( offset % 8 ) ;
2010-05-11 01:43:47 +04:00
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 ) ;
2015-12-03 17:37:29 +03:00
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( gc ) ;
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 ) ;
2015-12-03 17:37:29 +03:00
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( gc ) ;
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 ) ;
2015-12-03 17:37:29 +03:00
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( gc ) ;
2012-09-07 15:14:58 +04:00
int offset = d - > hwirq ;
2010-05-11 01:43:47 +04:00
int regoffset = offset / 8 ;
2016-04-05 16:09:09 +03:00
int mask = BIT ( offset % 8 ) ;
2010-05-11 01:43:47 +04:00
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 ) ;
2015-12-03 17:37:29 +03:00
struct tc3589x_gpio * tc3589x_gpio = gpiochip_get_data ( gc ) ;
2012-09-07 15:14:58 +04:00
int offset = d - > hwirq ;
2010-05-11 01:43:47 +04:00
int regoffset = offset / 8 ;
2016-04-05 16:09:09 +03:00
int mask = BIT ( offset % 8 ) ;
2010-05-11 01:43:47 +04:00
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 ) ;
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 ;
2014-12-15 12:39:47 +03:00
if ( ! np ) {
dev_err ( & pdev - > dev , " No Device Tree node found \n " ) ;
2012-09-07 15:14:59 +04:00
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 ;
2015-11-04 11:56:26 +03:00
tc3589x_gpio - > chip . parent = & pdev - > dev ;
2014-10-28 13:06:56 +03:00
tc3589x_gpio - > chip . base = - 1 ;
2013-03-20 16:16:02 +04:00
tc3589x_gpio - > chip . of_node = np ;
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
}
2016-02-22 15:13:28 +03:00
ret = devm_gpiochip_add_data ( & pdev - > dev , & tc3589x_gpio - > chip ,
tc3589x_gpio ) ;
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
}
2016-11-24 12:57:25 +03:00
ret = gpiochip_irqchip_add_nested ( & tc3589x_gpio - > chip ,
& tc3589x_gpio_irq_chip ,
0 ,
handle_simple_irq ,
IRQ_TYPE_NONE ) ;
2014-04-09 15:38:33 +04:00
if ( ret ) {
dev_err ( & pdev - > dev ,
" could not connect irqchip to gpiochip \n " ) ;
return ret ;
}
2016-11-24 12:57:25 +03:00
gpiochip_set_nested_irqchip ( & tc3589x_gpio - > chip ,
& tc3589x_gpio_irq_chip ,
irq ) ;
2014-09-26 16:19:52 +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 ;
}
2010-12-13 07:03:14 +03:00
static struct platform_driver tc3589x_gpio_driver = {
. driver . name = " tc3589x-gpio " ,
. probe = tc3589x_gpio_probe ,
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 ) ;