2010-07-17 15:08:43 +04:00
/*
* Copyright ( C ) 2009 - 2010 , Lars - Peter Clausen < lars @ metafoo . de >
* JZ4740 platform IRQ support
*
* This program is free software ; you can redistribute it and / or modify it
2013-01-22 15:59:30 +04:00
* under the terms of the GNU General Public License as published by the
2010-07-17 15:08:43 +04:00
* Free Software Foundation ; either version 2 of the License , or ( at your
* option ) any later version .
*
* 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 . ,
* 675 Mass Ave , Cambridge , MA 0213 9 , USA .
*
*/
# include <linux/errno.h>
# include <linux/init.h>
# include <linux/types.h>
# include <linux/interrupt.h>
# include <linux/ioport.h>
2015-07-08 00:11:46 +03:00
# include <linux/irqchip.h>
2015-05-24 18:11:31 +03:00
# include <linux/irqchip/ingenic.h>
2015-05-24 18:11:28 +03:00
# include <linux/of_address.h>
2015-05-24 18:11:21 +03:00
# include <linux/of_irq.h>
2010-07-17 15:08:43 +04:00
# include <linux/timex.h>
# include <linux/slab.h>
# include <linux/delay.h>
# include <asm/io.h>
2014-12-18 05:39:01 +03:00
# include <asm/mach-jz4740/irq.h>
2015-05-24 18:11:25 +03:00
struct ingenic_intc_data {
void __iomem * base ;
2015-05-24 18:11:26 +03:00
unsigned num_chips ;
2015-05-24 18:11:25 +03:00
} ;
2010-07-17 15:08:43 +04:00
# define JZ_REG_INTC_STATUS 0x00
# define JZ_REG_INTC_MASK 0x04
# define JZ_REG_INTC_SET_MASK 0x08
# define JZ_REG_INTC_CLEAR_MASK 0x0c
# define JZ_REG_INTC_PENDING 0x10
2015-05-24 18:11:26 +03:00
# define CHIP_SIZE 0x20
2010-07-17 15:08:43 +04:00
2015-05-24 18:11:29 +03:00
static irqreturn_t intc_cascade ( int irq , void * data )
2010-07-17 15:08:43 +04:00
{
2015-05-24 18:11:25 +03:00
struct ingenic_intc_data * intc = irq_get_handler_data ( irq ) ;
2011-09-24 04:29:46 +04:00
uint32_t irq_reg ;
2015-05-24 18:11:26 +03:00
unsigned i ;
2010-07-17 15:08:43 +04:00
2015-05-24 18:11:26 +03:00
for ( i = 0 ; i < intc - > num_chips ; i + + ) {
irq_reg = readl ( intc - > base + ( i * CHIP_SIZE ) +
JZ_REG_INTC_PENDING ) ;
if ( ! irq_reg )
continue ;
2010-07-17 15:08:43 +04:00
2015-05-24 18:11:26 +03:00
generic_handle_irq ( __fls ( irq_reg ) + ( i * 32 ) + JZ4740_IRQ_BASE ) ;
}
2011-09-24 04:29:46 +04:00
return IRQ_HANDLED ;
2011-03-24 00:08:53 +03:00
}
2015-05-24 18:11:29 +03:00
static void intc_irq_set_mask ( struct irq_chip_generic * gc , uint32_t mask )
2010-07-17 15:08:43 +04:00
{
2011-09-24 04:29:46 +04:00
struct irq_chip_regs * regs = & gc - > chip_types - > regs ;
2010-07-17 15:08:43 +04:00
2011-09-24 04:29:46 +04:00
writel ( mask , gc - > reg_base + regs - > enable ) ;
writel ( ~ mask , gc - > reg_base + regs - > disable ) ;
2010-07-17 15:08:43 +04:00
}
2015-05-24 18:11:29 +03:00
void ingenic_intc_irq_suspend ( struct irq_data * data )
2010-07-17 15:08:43 +04:00
{
2011-09-24 04:29:46 +04:00
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( data ) ;
2015-05-24 18:11:29 +03:00
intc_irq_set_mask ( gc , gc - > wake_active ) ;
2011-09-24 04:29:46 +04:00
}
2010-07-17 15:08:43 +04:00
2015-05-24 18:11:29 +03:00
void ingenic_intc_irq_resume ( struct irq_data * data )
2011-09-24 04:29:46 +04:00
{
struct irq_chip_generic * gc = irq_data_get_irq_chip_data ( data ) ;
2015-05-24 18:11:29 +03:00
intc_irq_set_mask ( gc , gc - > mask_cache ) ;
2010-07-17 15:08:43 +04:00
}
2015-05-24 18:11:29 +03:00
static struct irqaction intc_cascade_action = {
. handler = intc_cascade ,
. name = " SoC intc cascade interrupt " ,
2010-07-17 15:08:43 +04:00
} ;
2015-05-24 18:11:26 +03:00
static int __init ingenic_intc_of_init ( struct device_node * node ,
unsigned num_chips )
2010-07-17 15:08:43 +04:00
{
2015-05-24 18:11:25 +03:00
struct ingenic_intc_data * intc ;
2011-09-24 04:29:46 +04:00
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
2015-05-24 18:11:23 +03:00
struct irq_domain * domain ;
2015-05-24 18:11:25 +03:00
int parent_irq , err = 0 ;
2015-05-24 18:11:26 +03:00
unsigned i ;
2015-05-24 18:11:25 +03:00
intc = kzalloc ( sizeof ( * intc ) , GFP_KERNEL ) ;
if ( ! intc ) {
err = - ENOMEM ;
goto out_err ;
}
2015-05-24 18:11:22 +03:00
parent_irq = irq_of_parse_and_map ( node , 0 ) ;
2015-05-24 18:11:25 +03:00
if ( ! parent_irq ) {
err = - EINVAL ;
goto out_free ;
}
2011-09-24 04:29:46 +04:00
2015-05-24 18:11:25 +03:00
err = irq_set_handler_data ( parent_irq , intc ) ;
if ( err )
goto out_unmap_irq ;
2015-05-24 18:11:26 +03:00
intc - > num_chips = num_chips ;
2015-05-24 18:11:28 +03:00
intc - > base = of_iomap ( node , 0 ) ;
if ( ! intc - > base ) {
err = - ENODEV ;
goto out_unmap_irq ;
}
2010-07-17 15:08:43 +04:00
2015-05-24 18:11:26 +03:00
for ( i = 0 ; i < num_chips ; i + + ) {
/* Mask all irqs */
writel ( 0xffffffff , intc - > base + ( i * CHIP_SIZE ) +
JZ_REG_INTC_SET_MASK ) ;
gc = irq_alloc_generic_chip ( " INTC " , 1 ,
JZ4740_IRQ_BASE + ( i * 32 ) ,
intc - > base + ( i * CHIP_SIZE ) ,
handle_level_irq ) ;
gc - > wake_enabled = IRQ_MSK ( 32 ) ;
ct = gc - > chip_types ;
ct - > regs . enable = JZ_REG_INTC_CLEAR_MASK ;
ct - > regs . disable = JZ_REG_INTC_SET_MASK ;
ct - > chip . irq_unmask = irq_gc_unmask_enable_reg ;
ct - > chip . irq_mask = irq_gc_mask_disable_reg ;
ct - > chip . irq_mask_ack = irq_gc_mask_disable_reg ;
ct - > chip . irq_set_wake = irq_gc_set_wake ;
2015-05-24 18:11:29 +03:00
ct - > chip . irq_suspend = ingenic_intc_irq_suspend ;
ct - > chip . irq_resume = ingenic_intc_irq_resume ;
2015-05-24 18:11:26 +03:00
irq_setup_generic_chip ( gc , IRQ_MSK ( 32 ) , 0 , 0 ,
IRQ_NOPROBE | IRQ_LEVEL ) ;
}
2010-07-17 15:08:43 +04:00
2015-05-24 18:11:23 +03:00
domain = irq_domain_add_legacy ( node , num_chips * 32 , JZ4740_IRQ_BASE , 0 ,
& irq_domain_simple_ops , NULL ) ;
if ( ! domain )
pr_warn ( " unable to register IRQ domain \n " ) ;
2015-05-24 18:11:29 +03:00
setup_irq ( parent_irq , & intc_cascade_action ) ;
2015-05-24 18:11:21 +03:00
return 0 ;
2015-05-24 18:11:25 +03:00
out_unmap_irq :
irq_dispose_mapping ( parent_irq ) ;
out_free :
kfree ( intc ) ;
out_err :
return err ;
2010-07-17 15:08:43 +04:00
}
2015-05-24 18:11:26 +03:00
static int __init intc_1chip_of_init ( struct device_node * node ,
struct device_node * parent )
{
return ingenic_intc_of_init ( node , 1 ) ;
}
IRQCHIP_DECLARE ( jz4740_intc , " ingenic,jz4740-intc " , intc_1chip_of_init ) ;
2015-05-24 18:11:30 +03:00
static int __init intc_2chip_of_init ( struct device_node * node ,
struct device_node * parent )
{
return ingenic_intc_of_init ( node , 2 ) ;
}
IRQCHIP_DECLARE ( jz4770_intc , " ingenic,jz4770-intc " , intc_2chip_of_init ) ;
IRQCHIP_DECLARE ( jz4775_intc , " ingenic,jz4775-intc " , intc_2chip_of_init ) ;
IRQCHIP_DECLARE ( jz4780_intc , " ingenic,jz4780-intc " , intc_2chip_of_init ) ;