2015-06-06 23:30:40 +03:00
# include <linux/kernel.h>
# include <linux/init.h>
2015-07-31 15:48:57 +03:00
# include <linux/gpio/driver.h>
2015-06-06 23:30:40 +03:00
# include <linux/of_gpio.h>
# include <linux/io.h>
2015-07-31 15:48:57 +03:00
# include <linux/interrupt.h>
2015-06-06 23:30:40 +03:00
# include <linux/platform_device.h>
# define ETRAX_FS_rw_pa_dout 0
# define ETRAX_FS_r_pa_din 4
# define ETRAX_FS_rw_pa_oe 8
# define ETRAX_FS_rw_intr_cfg 12
# define ETRAX_FS_rw_intr_mask 16
# define ETRAX_FS_rw_ack_intr 20
# define ETRAX_FS_r_intr 24
2015-07-31 15:48:57 +03:00
# define ETRAX_FS_r_masked_intr 28
2015-06-06 23:30:40 +03:00
# define ETRAX_FS_rw_pb_dout 32
# define ETRAX_FS_r_pb_din 36
# define ETRAX_FS_rw_pb_oe 40
# define ETRAX_FS_rw_pc_dout 48
# define ETRAX_FS_r_pc_din 52
# define ETRAX_FS_rw_pc_oe 56
# define ETRAX_FS_rw_pd_dout 64
# define ETRAX_FS_r_pd_din 68
# define ETRAX_FS_rw_pd_oe 72
# define ETRAX_FS_rw_pe_dout 80
# define ETRAX_FS_r_pe_din 84
# define ETRAX_FS_rw_pe_oe 88
2015-07-22 16:05:19 +03:00
# define ARTPEC3_r_pa_din 0
# define ARTPEC3_rw_pa_dout 4
# define ARTPEC3_rw_pa_oe 8
# define ARTPEC3_r_pb_din 44
# define ARTPEC3_rw_pb_dout 48
# define ARTPEC3_rw_pb_oe 52
# define ARTPEC3_r_pc_din 88
# define ARTPEC3_rw_pc_dout 92
# define ARTPEC3_rw_pc_oe 96
# define ARTPEC3_r_pd_din 116
2015-07-31 15:48:57 +03:00
# define ARTPEC3_rw_intr_cfg 120
# define ARTPEC3_rw_intr_pins 124
# define ARTPEC3_rw_intr_mask 128
# define ARTPEC3_rw_ack_intr 132
# define ARTPEC3_r_masked_intr 140
# define GIO_CFG_OFF 0
# define GIO_CFG_HI 1
# define GIO_CFG_LO 2
# define GIO_CFG_SET 3
# define GIO_CFG_POSEDGE 5
# define GIO_CFG_NEGEDGE 6
# define GIO_CFG_ANYEDGE 7
struct etraxfs_gpio_info ;
struct etraxfs_gpio_block {
2017-03-09 19:21:55 +03:00
raw_spinlock_t lock ;
2015-07-31 15:48:57 +03:00
u32 mask ;
u32 cfg ;
u32 pins ;
unsigned int group [ 8 ] ;
void __iomem * regs ;
const struct etraxfs_gpio_info * info ;
} ;
struct etraxfs_gpio_chip {
2015-12-04 16:02:58 +03:00
struct gpio_chip gc ;
2015-07-31 15:48:57 +03:00
struct etraxfs_gpio_block * block ;
} ;
2015-07-22 16:05:19 +03:00
2015-06-06 23:30:40 +03:00
struct etraxfs_gpio_port {
const char * label ;
unsigned int oe ;
unsigned int dout ;
unsigned int din ;
unsigned int ngpio ;
} ;
struct etraxfs_gpio_info {
unsigned int num_ports ;
const struct etraxfs_gpio_port * ports ;
2015-07-31 15:48:57 +03:00
unsigned int rw_ack_intr ;
unsigned int rw_intr_mask ;
unsigned int rw_intr_cfg ;
unsigned int rw_intr_pins ;
unsigned int r_masked_intr ;
2015-06-06 23:30:40 +03:00
} ;
static const struct etraxfs_gpio_port etraxfs_gpio_etraxfs_ports [ ] = {
{
. label = " A " ,
. ngpio = 8 ,
. oe = ETRAX_FS_rw_pa_oe ,
. dout = ETRAX_FS_rw_pa_dout ,
. din = ETRAX_FS_r_pa_din ,
} ,
{
. label = " B " ,
. ngpio = 18 ,
. oe = ETRAX_FS_rw_pb_oe ,
. dout = ETRAX_FS_rw_pb_dout ,
. din = ETRAX_FS_r_pb_din ,
} ,
{
. label = " C " ,
. ngpio = 18 ,
. oe = ETRAX_FS_rw_pc_oe ,
. dout = ETRAX_FS_rw_pc_dout ,
. din = ETRAX_FS_r_pc_din ,
} ,
{
. label = " D " ,
. ngpio = 18 ,
. oe = ETRAX_FS_rw_pd_oe ,
. dout = ETRAX_FS_rw_pd_dout ,
. din = ETRAX_FS_r_pd_din ,
} ,
{
. label = " E " ,
. ngpio = 18 ,
. oe = ETRAX_FS_rw_pe_oe ,
. dout = ETRAX_FS_rw_pe_dout ,
. din = ETRAX_FS_r_pe_din ,
} ,
} ;
static const struct etraxfs_gpio_info etraxfs_gpio_etraxfs = {
. num_ports = ARRAY_SIZE ( etraxfs_gpio_etraxfs_ports ) ,
. ports = etraxfs_gpio_etraxfs_ports ,
2015-07-31 15:48:57 +03:00
. rw_ack_intr = ETRAX_FS_rw_ack_intr ,
. rw_intr_mask = ETRAX_FS_rw_intr_mask ,
. rw_intr_cfg = ETRAX_FS_rw_intr_cfg ,
. r_masked_intr = ETRAX_FS_r_masked_intr ,
2015-06-06 23:30:40 +03:00
} ;
2015-07-22 16:05:19 +03:00
static const struct etraxfs_gpio_port etraxfs_gpio_artpec3_ports [ ] = {
{
. label = " A " ,
. ngpio = 32 ,
. oe = ARTPEC3_rw_pa_oe ,
. dout = ARTPEC3_rw_pa_dout ,
. din = ARTPEC3_r_pa_din ,
} ,
{
. label = " B " ,
. ngpio = 32 ,
. oe = ARTPEC3_rw_pb_oe ,
. dout = ARTPEC3_rw_pb_dout ,
. din = ARTPEC3_r_pb_din ,
} ,
{
. label = " C " ,
. ngpio = 16 ,
. oe = ARTPEC3_rw_pc_oe ,
. dout = ARTPEC3_rw_pc_dout ,
. din = ARTPEC3_r_pc_din ,
} ,
{
. label = " D " ,
. ngpio = 32 ,
. din = ARTPEC3_r_pd_din ,
} ,
} ;
static const struct etraxfs_gpio_info etraxfs_gpio_artpec3 = {
. num_ports = ARRAY_SIZE ( etraxfs_gpio_artpec3_ports ) ,
. ports = etraxfs_gpio_artpec3_ports ,
2015-07-31 15:48:57 +03:00
. rw_ack_intr = ARTPEC3_rw_ack_intr ,
. rw_intr_mask = ARTPEC3_rw_intr_mask ,
. rw_intr_cfg = ARTPEC3_rw_intr_cfg ,
. r_masked_intr = ARTPEC3_r_masked_intr ,
. rw_intr_pins = ARTPEC3_rw_intr_pins ,
2015-07-22 16:05:19 +03:00
} ;
2015-07-31 15:48:57 +03:00
static unsigned int etraxfs_gpio_chip_to_port ( struct gpio_chip * gc )
{
return gc - > label [ 0 ] - ' A ' ;
}
2015-06-06 23:30:40 +03:00
static int etraxfs_gpio_of_xlate ( struct gpio_chip * gc ,
const struct of_phandle_args * gpiospec ,
u32 * flags )
{
/*
* Port numbers are A to E , and the properties are integers , so we
* specify them as 0xA - 0xE .
*/
2015-07-31 15:48:57 +03:00
if ( etraxfs_gpio_chip_to_port ( gc ) + 0xA ! = gpiospec - > args [ 2 ] )
2015-06-06 23:30:40 +03:00
return - EINVAL ;
return of_gpio_simple_xlate ( gc , gpiospec , flags ) ;
}
static const struct of_device_id etraxfs_gpio_of_table [ ] = {
{
. compatible = " axis,etraxfs-gio " ,
. data = & etraxfs_gpio_etraxfs ,
} ,
2015-07-22 16:05:19 +03:00
{
. compatible = " axis,artpec3-gio " ,
. data = & etraxfs_gpio_artpec3 ,
} ,
2015-06-06 23:30:40 +03:00
{ } ,
} ;
2015-07-31 15:48:57 +03:00
static unsigned int etraxfs_gpio_to_group_irq ( unsigned int gpio )
{
return gpio % 8 ;
}
static unsigned int etraxfs_gpio_to_group_pin ( struct etraxfs_gpio_chip * chip ,
unsigned int gpio )
{
2015-12-04 16:02:58 +03:00
return 4 * etraxfs_gpio_chip_to_port ( & chip - > gc ) + gpio / 8 ;
2015-07-31 15:48:57 +03:00
}
static void etraxfs_gpio_irq_ack ( struct irq_data * d )
{
2015-08-25 11:40:23 +03:00
struct etraxfs_gpio_chip * chip =
2015-12-04 16:02:58 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2015-07-31 15:48:57 +03:00
struct etraxfs_gpio_block * block = chip - > block ;
unsigned int grpirq = etraxfs_gpio_to_group_irq ( d - > hwirq ) ;
writel ( BIT ( grpirq ) , block - > regs + block - > info - > rw_ack_intr ) ;
}
static void etraxfs_gpio_irq_mask ( struct irq_data * d )
{
2015-08-25 11:40:23 +03:00
struct etraxfs_gpio_chip * chip =
2015-12-04 16:02:58 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2015-07-31 15:48:57 +03:00
struct etraxfs_gpio_block * block = chip - > block ;
unsigned int grpirq = etraxfs_gpio_to_group_irq ( d - > hwirq ) ;
2017-03-09 19:21:55 +03:00
raw_spin_lock ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
block - > mask & = ~ BIT ( grpirq ) ;
writel ( block - > mask , block - > regs + block - > info - > rw_intr_mask ) ;
2017-03-09 19:21:55 +03:00
raw_spin_unlock ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
}
static void etraxfs_gpio_irq_unmask ( struct irq_data * d )
{
2015-08-25 11:40:23 +03:00
struct etraxfs_gpio_chip * chip =
2015-12-04 16:02:58 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2015-07-31 15:48:57 +03:00
struct etraxfs_gpio_block * block = chip - > block ;
unsigned int grpirq = etraxfs_gpio_to_group_irq ( d - > hwirq ) ;
2017-03-09 19:21:55 +03:00
raw_spin_lock ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
block - > mask | = BIT ( grpirq ) ;
writel ( block - > mask , block - > regs + block - > info - > rw_intr_mask ) ;
2017-03-09 19:21:55 +03:00
raw_spin_unlock ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
}
static int etraxfs_gpio_irq_set_type ( struct irq_data * d , u32 type )
{
2015-08-25 11:40:23 +03:00
struct etraxfs_gpio_chip * chip =
2015-12-04 16:02:58 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2015-07-31 15:48:57 +03:00
struct etraxfs_gpio_block * block = chip - > block ;
unsigned int grpirq = etraxfs_gpio_to_group_irq ( d - > hwirq ) ;
u32 cfg ;
switch ( type ) {
case IRQ_TYPE_EDGE_RISING :
cfg = GIO_CFG_POSEDGE ;
break ;
case IRQ_TYPE_EDGE_FALLING :
cfg = GIO_CFG_NEGEDGE ;
break ;
case IRQ_TYPE_EDGE_BOTH :
cfg = GIO_CFG_ANYEDGE ;
break ;
case IRQ_TYPE_LEVEL_LOW :
cfg = GIO_CFG_LO ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
cfg = GIO_CFG_HI ;
break ;
default :
return - EINVAL ;
}
2017-03-09 19:21:55 +03:00
raw_spin_lock ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
block - > cfg & = ~ ( 0x7 < < ( grpirq * 3 ) ) ;
block - > cfg | = ( cfg < < ( grpirq * 3 ) ) ;
writel ( block - > cfg , block - > regs + block - > info - > rw_intr_cfg ) ;
2017-03-09 19:21:55 +03:00
raw_spin_unlock ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
return 0 ;
}
static int etraxfs_gpio_irq_request_resources ( struct irq_data * d )
{
2015-08-25 11:40:23 +03:00
struct etraxfs_gpio_chip * chip =
2015-12-04 16:02:58 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2015-07-31 15:48:57 +03:00
struct etraxfs_gpio_block * block = chip - > block ;
unsigned int grpirq = etraxfs_gpio_to_group_irq ( d - > hwirq ) ;
2015-08-31 09:56:04 +03:00
int ret = - EBUSY ;
2015-07-31 15:48:57 +03:00
2017-03-09 19:21:55 +03:00
raw_spin_lock ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
if ( block - > group [ grpirq ] )
goto out ;
2015-12-04 16:02:58 +03:00
ret = gpiochip_lock_as_irq ( & chip - > gc , d - > hwirq ) ;
2015-07-31 15:48:57 +03:00
if ( ret )
goto out ;
block - > group [ grpirq ] = d - > irq ;
if ( block - > info - > rw_intr_pins ) {
unsigned int pin = etraxfs_gpio_to_group_pin ( chip , d - > hwirq ) ;
block - > pins & = ~ ( 0xf < < ( grpirq * 4 ) ) ;
block - > pins | = ( pin < < ( grpirq * 4 ) ) ;
writel ( block - > pins , block - > regs + block - > info - > rw_intr_pins ) ;
}
out :
2017-03-09 19:21:55 +03:00
raw_spin_unlock ( & block - > lock ) ;
2015-08-31 09:56:04 +03:00
return ret ;
2015-07-31 15:48:57 +03:00
}
static void etraxfs_gpio_irq_release_resources ( struct irq_data * d )
{
2015-08-25 11:40:23 +03:00
struct etraxfs_gpio_chip * chip =
2015-12-04 16:02:58 +03:00
gpiochip_get_data ( irq_data_get_irq_chip_data ( d ) ) ;
2015-07-31 15:48:57 +03:00
struct etraxfs_gpio_block * block = chip - > block ;
unsigned int grpirq = etraxfs_gpio_to_group_irq ( d - > hwirq ) ;
2017-03-09 19:21:55 +03:00
raw_spin_lock ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
block - > group [ grpirq ] = 0 ;
2015-12-04 16:02:58 +03:00
gpiochip_unlock_as_irq ( & chip - > gc , d - > hwirq ) ;
2017-03-09 19:21:55 +03:00
raw_spin_unlock ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
}
static struct irq_chip etraxfs_gpio_irq_chip = {
. name = " gpio-etraxfs " ,
. irq_ack = etraxfs_gpio_irq_ack ,
. irq_mask = etraxfs_gpio_irq_mask ,
. irq_unmask = etraxfs_gpio_irq_unmask ,
. irq_set_type = etraxfs_gpio_irq_set_type ,
. irq_request_resources = etraxfs_gpio_irq_request_resources ,
. irq_release_resources = etraxfs_gpio_irq_release_resources ,
} ;
static irqreturn_t etraxfs_gpio_interrupt ( int irq , void * dev_id )
{
struct etraxfs_gpio_block * block = dev_id ;
unsigned long intr = readl ( block - > regs + block - > info - > r_masked_intr ) ;
int bit ;
for_each_set_bit ( bit , & intr , 8 )
generic_handle_irq ( block - > group [ bit ] ) ;
return IRQ_RETVAL ( intr & 0xff ) ;
}
2015-06-06 23:30:40 +03:00
static int etraxfs_gpio_probe ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
const struct etraxfs_gpio_info * info ;
const struct of_device_id * match ;
2015-07-31 15:48:57 +03:00
struct etraxfs_gpio_block * block ;
struct etraxfs_gpio_chip * chips ;
struct resource * res , * irq ;
bool allportsirq = false ;
2015-06-06 23:30:40 +03:00
void __iomem * regs ;
int ret ;
int i ;
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
regs = devm_ioremap_resource ( dev , res ) ;
2015-07-09 16:19:53 +03:00
if ( IS_ERR ( regs ) )
return PTR_ERR ( regs ) ;
2015-06-06 23:30:40 +03:00
match = of_match_node ( etraxfs_gpio_of_table , dev - > of_node ) ;
if ( ! match )
return - EINVAL ;
info = match - > data ;
chips = devm_kzalloc ( dev , sizeof ( * chips ) * info - > num_ports , GFP_KERNEL ) ;
if ( ! chips )
return - ENOMEM ;
2015-07-31 15:48:57 +03:00
irq = platform_get_resource ( pdev , IORESOURCE_IRQ , 0 ) ;
if ( ! irq )
return - EINVAL ;
block = devm_kzalloc ( dev , sizeof ( * block ) , GFP_KERNEL ) ;
if ( ! block )
return - ENOMEM ;
2017-03-09 19:21:55 +03:00
raw_spin_lock_init ( & block - > lock ) ;
2015-07-31 15:48:57 +03:00
block - > regs = regs ;
block - > info = info ;
writel ( 0 , block - > regs + info - > rw_intr_mask ) ;
writel ( 0 , block - > regs + info - > rw_intr_cfg ) ;
if ( info - > rw_intr_pins ) {
allportsirq = true ;
writel ( 0 , block - > regs + info - > rw_intr_pins ) ;
}
ret = devm_request_irq ( dev , irq - > start , etraxfs_gpio_interrupt ,
IRQF_SHARED , dev_name ( dev ) , block ) ;
if ( ret ) {
dev_err ( dev , " Unable to request irq %d \n " , ret ) ;
return ret ;
}
2015-06-06 23:30:40 +03:00
for ( i = 0 ; i < info - > num_ports ; i + + ) {
2015-07-31 15:48:57 +03:00
struct etraxfs_gpio_chip * chip = & chips [ i ] ;
2015-12-04 16:02:58 +03:00
struct gpio_chip * gc = & chip - > gc ;
2015-06-06 23:30:40 +03:00
const struct etraxfs_gpio_port * port = & info - > ports [ i ] ;
2015-07-22 16:05:19 +03:00
unsigned long flags = BGPIOF_READ_OUTPUT_REG_SET ;
void __iomem * dat = regs + port - > din ;
void __iomem * set = regs + port - > dout ;
void __iomem * dirout = regs + port - > oe ;
2015-07-31 15:48:57 +03:00
chip - > block = block ;
2015-07-22 16:05:19 +03:00
if ( dirout = = set ) {
dirout = set = NULL ;
flags = BGPIOF_NO_OUTPUT ;
}
2015-06-06 23:30:40 +03:00
2015-12-04 16:02:58 +03:00
ret = bgpio_init ( gc , dev , 4 ,
2015-07-22 16:05:19 +03:00
dat , set , NULL , dirout , NULL ,
flags ) ;
2015-07-31 15:48:57 +03:00
if ( ret ) {
dev_err ( dev , " Unable to init port %s \n " ,
port - > label ) ;
continue ;
}
2015-06-06 23:30:40 +03:00
2015-12-04 16:02:58 +03:00
gc - > ngpio = port - > ngpio ;
gc - > label = port - > label ;
2015-06-06 23:30:40 +03:00
2015-12-04 16:02:58 +03:00
gc - > of_node = dev - > of_node ;
gc - > of_gpio_n_cells = 3 ;
gc - > of_xlate = etraxfs_gpio_of_xlate ;
2015-06-06 23:30:40 +03:00
2015-12-04 16:02:58 +03:00
ret = gpiochip_add_data ( gc , chip ) ;
2015-07-31 15:48:57 +03:00
if ( ret ) {
2015-06-06 23:30:40 +03:00
dev_err ( dev , " Unable to register port %s \n " ,
2015-12-04 16:02:58 +03:00
gc - > label ) ;
2015-07-31 15:48:57 +03:00
continue ;
}
if ( i > 0 & & ! allportsirq )
continue ;
2015-12-04 16:02:58 +03:00
ret = gpiochip_irqchip_add ( gc , & etraxfs_gpio_irq_chip , 0 ,
2015-07-31 15:48:57 +03:00
handle_level_irq , IRQ_TYPE_NONE ) ;
if ( ret ) {
dev_err ( dev , " Unable to add irqchip to port %s \n " ,
2015-12-04 16:02:58 +03:00
gc - > label ) ;
2015-07-31 15:48:57 +03:00
}
2015-06-06 23:30:40 +03:00
}
return 0 ;
}
static struct platform_driver etraxfs_gpio_driver = {
. driver = {
. name = " etraxfs-gpio " ,
. of_match_table = of_match_ptr ( etraxfs_gpio_of_table ) ,
} ,
. probe = etraxfs_gpio_probe ,
} ;
2016-11-18 17:12:27 +03:00
builtin_platform_driver ( etraxfs_gpio_driver ) ;