2013-06-25 20:29:57 +04:00
/*
* Abilis Systems interrupt controller driver
*
* Copyright ( C ) Abilis Systems 2012
*
* Author : Christian Ruppert < christian . ruppert @ abilis . com >
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation .
*
* This program is distributed in the hope that 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 , write to the Free Software
* Foundation , Inc . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*/
# include <linux/interrupt.h>
# include <linux/irqdomain.h>
# include <linux/irq.h>
2015-07-08 00:11:46 +03:00
# include <linux/irqchip.h>
2013-06-25 20:29:57 +04:00
# include <linux/of_irq.h>
# include <linux/of_address.h>
# include <linux/of_platform.h>
# include <linux/io.h>
# include <linux/slab.h>
# include <linux/bitops.h>
# define AB_IRQCTL_INT_ENABLE 0x00
# define AB_IRQCTL_INT_STATUS 0x04
# define AB_IRQCTL_SRC_MODE 0x08
# define AB_IRQCTL_SRC_POLARITY 0x0C
# define AB_IRQCTL_INT_MODE 0x10
# define AB_IRQCTL_INT_POLARITY 0x14
# define AB_IRQCTL_INT_FORCE 0x18
# define AB_IRQCTL_MAXIRQ 32
static inline void ab_irqctl_writereg ( struct irq_chip_generic * gc , u32 reg ,
u32 val )
{
2014-11-07 09:44:17 +03:00
irq_reg_writel ( gc , val , reg ) ;
2013-06-25 20:29:57 +04:00
}
static inline u32 ab_irqctl_readreg ( struct irq_chip_generic * gc , u32 reg )
{
2014-11-07 09:44:17 +03:00
return irq_reg_readl ( gc , reg ) ;
2013-06-25 20:29:57 +04:00
}
static int tb10x_irq_set_type ( struct irq_data * data , unsigned int flow_type )
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( data ) ;
uint32_t im , mod , pol ;
im = data - > mask ;
irq_gc_lock ( gc ) ;
mod = ab_irqctl_readreg ( gc , AB_IRQCTL_SRC_MODE ) | im ;
pol = ab_irqctl_readreg ( gc , AB_IRQCTL_SRC_POLARITY ) | im ;
switch ( flow_type & IRQF_TRIGGER_MASK ) {
case IRQ_TYPE_EDGE_FALLING :
pol ^ = im ;
break ;
case IRQ_TYPE_LEVEL_HIGH :
mod ^ = im ;
break ;
case IRQ_TYPE_NONE :
flow_type = IRQ_TYPE_LEVEL_LOW ;
case IRQ_TYPE_LEVEL_LOW :
mod ^ = im ;
pol ^ = im ;
break ;
case IRQ_TYPE_EDGE_RISING :
break ;
default :
irq_gc_unlock ( gc ) ;
pr_err ( " %s: Cannot assign multiple trigger modes to IRQ %d. \n " ,
__func__ , data - > irq ) ;
return - EBADR ;
}
irqd_set_trigger_type ( data , flow_type ) ;
irq_setup_alt_chip ( data , flow_type ) ;
ab_irqctl_writereg ( gc , AB_IRQCTL_SRC_MODE , mod ) ;
ab_irqctl_writereg ( gc , AB_IRQCTL_SRC_POLARITY , pol ) ;
ab_irqctl_writereg ( gc , AB_IRQCTL_INT_STATUS , im ) ;
irq_gc_unlock ( gc ) ;
return IRQ_SET_MASK_OK ;
}
2015-09-14 11:42:37 +03:00
static void tb10x_irq_cascade ( struct irq_desc * desc )
2013-06-25 20:29:57 +04:00
{
struct irq_domain * domain = irq_desc_get_handler_data ( desc ) ;
2015-07-16 23:40:58 +03:00
unsigned int irq = irq_desc_get_irq ( desc ) ;
2013-06-25 20:29:57 +04:00
generic_handle_irq ( irq_find_mapping ( domain , irq ) ) ;
}
static int __init of_tb10x_init_irq ( struct device_node * ictl ,
struct device_node * parent )
{
int i , ret , nrirqs = of_irq_count ( ictl ) ;
struct resource mem ;
struct irq_chip_generic * gc ;
struct irq_domain * domain ;
void __iomem * reg_base ;
if ( of_address_to_resource ( ictl , 0 , & mem ) ) {
pr_err ( " %s: No registers declared in DeviceTree. \n " ,
ictl - > name ) ;
return - EINVAL ;
}
if ( ! request_mem_region ( mem . start , resource_size ( & mem ) ,
ictl - > name ) ) {
pr_err ( " %s: Request mem region failed. \n " , ictl - > name ) ;
return - EBUSY ;
}
reg_base = ioremap ( mem . start , resource_size ( & mem ) ) ;
if ( ! reg_base ) {
ret = - EBUSY ;
pr_err ( " %s: ioremap failed. \n " , ictl - > name ) ;
goto ioremap_fail ;
}
domain = irq_domain_add_linear ( ictl , AB_IRQCTL_MAXIRQ ,
& irq_generic_chip_ops , NULL ) ;
if ( ! domain ) {
ret = - ENOMEM ;
pr_err ( " %s: Could not register interrupt domain. \n " ,
ictl - > name ) ;
goto irq_domain_add_fail ;
}
ret = irq_alloc_domain_generic_chips ( domain , AB_IRQCTL_MAXIRQ ,
2 , ictl - > name , handle_level_irq ,
IRQ_NOREQUEST , IRQ_NOPROBE ,
IRQ_GC_INIT_MASK_CACHE ) ;
if ( ret ) {
pr_err ( " %s: Could not allocate generic interrupt chip. \n " ,
ictl - > name ) ;
goto gc_alloc_fail ;
}
gc = domain - > gc - > gc [ 0 ] ;
gc - > reg_base = reg_base ;
gc - > chip_types [ 0 ] . type = IRQ_TYPE_LEVEL_MASK ;
gc - > chip_types [ 0 ] . chip . irq_mask = irq_gc_mask_clr_bit ;
gc - > chip_types [ 0 ] . chip . irq_unmask = irq_gc_mask_set_bit ;
gc - > chip_types [ 0 ] . chip . irq_set_type = tb10x_irq_set_type ;
gc - > chip_types [ 0 ] . regs . mask = AB_IRQCTL_INT_ENABLE ;
gc - > chip_types [ 1 ] . type = IRQ_TYPE_EDGE_BOTH ;
gc - > chip_types [ 1 ] . chip . name = gc - > chip_types [ 0 ] . chip . name ;
gc - > chip_types [ 1 ] . chip . irq_ack = irq_gc_ack_set_bit ;
gc - > chip_types [ 1 ] . chip . irq_mask = irq_gc_mask_clr_bit ;
gc - > chip_types [ 1 ] . chip . irq_unmask = irq_gc_mask_set_bit ;
gc - > chip_types [ 1 ] . chip . irq_set_type = tb10x_irq_set_type ;
gc - > chip_types [ 1 ] . regs . ack = AB_IRQCTL_INT_STATUS ;
gc - > chip_types [ 1 ] . regs . mask = AB_IRQCTL_INT_ENABLE ;
gc - > chip_types [ 1 ] . handler = handle_edge_irq ;
for ( i = 0 ; i < nrirqs ; i + + ) {
unsigned int irq = irq_of_parse_and_map ( ictl , i ) ;
irqchip/tb10x: Consolidate chained IRQ handler install/remove
Chained irq handlers usually set up handler data as well. We now have
a function to set both under irq_desc->lock. Replace the two calls
with one.
Search and conversion was done with coccinelle:
@@
expression E1, E2, E3;
@@
(
-if (irq_set_handler_data(E1, E2) != 0)
- BUG();
|
-irq_set_handler_data(E1, E2);
)
-irq_set_chained_handler(E1, E3);
+irq_set_chained_handler_and_data(E1, E3, E2);
@@
expression E1, E2, E3;
@@
(
-if (irq_set_handler_data(E1, E2) != 0)
- BUG();
...
|
-irq_set_handler_data(E1, E2);
...
)
-irq_set_chained_handler(E1, E3);
+irq_set_chained_handler_and_data(E1, E3, E2);
Reported-by: Russell King <rmk+kernel@arm.linux.org.uk>
Signed-off-by: Thomas Gleixner <tglx@linutronix.de>
Cc: Julia Lawall <Julia.Lawall@lip6.fr>
Cc: Thomas Gleixner <tglx@linutronix.de>
Cc: Jason Cooper <jason@lakedaemon.net>
2015-06-21 22:10:58 +03:00
irq_set_chained_handler_and_data ( irq , tb10x_irq_cascade ,
domain ) ;
2013-06-25 20:29:57 +04:00
}
ab_irqctl_writereg ( gc , AB_IRQCTL_INT_ENABLE , 0 ) ;
ab_irqctl_writereg ( gc , AB_IRQCTL_INT_MODE , 0 ) ;
ab_irqctl_writereg ( gc , AB_IRQCTL_INT_POLARITY , 0 ) ;
ab_irqctl_writereg ( gc , AB_IRQCTL_INT_STATUS , ~ 0UL ) ;
return 0 ;
gc_alloc_fail :
irq_domain_remove ( domain ) ;
irq_domain_add_fail :
iounmap ( reg_base ) ;
ioremap_fail :
release_mem_region ( mem . start , resource_size ( & mem ) ) ;
return ret ;
}
IRQCHIP_DECLARE ( tb10x_intc , " abilis,tb10x-ictl " , of_tb10x_init_irq ) ;