2010-05-03 09:24:30 +01:00
/*
* SPEAr platform shared irq layer source file
*
2012-11-12 22:56:03 +05:30
* Copyright ( C ) 2009 - 2012 ST Microelectronics
2015-07-17 16:23:50 -07:00
* Viresh Kumar < vireshk @ kernel . org >
2010-05-03 09:24:30 +01:00
*
2012-11-12 22:56:03 +05:30
* Copyright ( C ) 2012 ST Microelectronics
2014-04-18 15:07:16 -07:00
* Shiraz Hashim < shiraz . linux . kernel @ gmail . com >
2012-11-12 22:56:03 +05:30
*
2010-05-03 09:24:30 +01:00
* This file is licensed under the terms of the GNU General Public
* License version 2. This program is licensed " as is " without any
* warranty of any kind , whether express or implied .
*/
2012-08-03 15:33:10 +05:30
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-05-03 09:24:30 +01:00
# include <linux/err.h>
2012-08-03 15:33:10 +05:30
# include <linux/export.h>
# include <linux/interrupt.h>
2010-05-03 09:24:30 +01:00
# include <linux/io.h>
# include <linux/irq.h>
2015-07-07 17:11:46 -04:00
# include <linux/irqchip.h>
2012-08-03 15:33:10 +05:30
# include <linux/irqdomain.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2010-05-03 09:24:30 +01:00
# include <linux/spinlock.h>
2014-06-19 21:34:38 +00:00
/*
* struct spear_shirq : shared irq structure
*
2014-06-19 21:34:39 +00:00
* base : Base register address
2014-06-19 21:34:44 +00:00
* status_reg : Status register offset for chained interrupt handler
* mask_reg : Mask register offset for irq chip
2014-06-19 21:34:41 +00:00
* mask : Mask to apply to the status register
2014-06-19 21:34:39 +00:00
* virq_base : Base virtual interrupt number
* nr_irqs : Number of interrupts handled by this block
* offset : Bit offset of the first interrupt
2014-06-19 21:34:43 +00:00
* irq_chip : Interrupt controller chip used for this instance ,
* if NULL group is disabled , but accounted
2014-06-19 21:34:38 +00:00
*/
struct spear_shirq {
2014-06-19 21:34:39 +00:00
void __iomem * base ;
2014-06-19 21:34:44 +00:00
u32 status_reg ;
u32 mask_reg ;
2014-06-19 21:34:41 +00:00
u32 mask ;
2014-06-19 21:34:39 +00:00
u32 virq_base ;
u32 nr_irqs ;
u32 offset ;
2014-06-19 21:34:43 +00:00
struct irq_chip * irq_chip ;
2014-06-19 21:34:38 +00:00
} ;
2012-08-03 15:33:10 +05:30
/* spear300 shared irq registers offsets and masks */
# define SPEAR300_INT_ENB_MASK_REG 0x54
# define SPEAR300_INT_STS_MASK_REG 0x58
2014-06-19 21:34:43 +00:00
static DEFINE_RAW_SPINLOCK ( shirq_lock ) ;
static void shirq_irq_mask ( struct irq_data * d )
{
struct spear_shirq * shirq = irq_data_get_irq_chip_data ( d ) ;
u32 val , shift = d - > irq - shirq - > virq_base + shirq - > offset ;
2014-06-19 21:34:44 +00:00
u32 __iomem * reg = shirq - > base + shirq - > mask_reg ;
2014-06-19 21:34:43 +00:00
raw_spin_lock ( & shirq_lock ) ;
val = readl ( reg ) & ~ ( 0x1 < < shift ) ;
writel ( val , reg ) ;
raw_spin_unlock ( & shirq_lock ) ;
}
static void shirq_irq_unmask ( struct irq_data * d )
{
struct spear_shirq * shirq = irq_data_get_irq_chip_data ( d ) ;
u32 val , shift = d - > irq - shirq - > virq_base + shirq - > offset ;
2014-06-19 21:34:44 +00:00
u32 __iomem * reg = shirq - > base + shirq - > mask_reg ;
2014-06-19 21:34:43 +00:00
raw_spin_lock ( & shirq_lock ) ;
val = readl ( reg ) | ( 0x1 < < shift ) ;
writel ( val , reg ) ;
raw_spin_unlock ( & shirq_lock ) ;
}
static struct irq_chip shirq_chip = {
. name = " spear-shirq " ,
. irq_mask = shirq_irq_mask ,
. irq_unmask = shirq_irq_unmask ,
} ;
2012-08-03 15:33:10 +05:30
static struct spear_shirq spear300_shirq_ras1 = {
2014-06-19 21:34:39 +00:00
. offset = 0 ,
. nr_irqs = 9 ,
2014-06-19 21:34:41 +00:00
. mask = ( ( 0x1 < < 9 ) - 1 ) < < 0 ,
2014-06-19 21:34:43 +00:00
. irq_chip = & shirq_chip ,
2014-06-19 21:34:44 +00:00
. status_reg = SPEAR300_INT_STS_MASK_REG ,
. mask_reg = SPEAR300_INT_ENB_MASK_REG ,
2012-08-03 15:33:10 +05:30
} ;
static struct spear_shirq * spear300_shirq_blocks [ ] = {
& spear300_shirq_ras1 ,
} ;
/* spear310 shared irq registers offsets and masks */
# define SPEAR310_INT_STS_MASK_REG 0x04
static struct spear_shirq spear310_shirq_ras1 = {
2014-06-19 21:34:39 +00:00
. offset = 0 ,
. nr_irqs = 8 ,
2014-06-19 21:34:41 +00:00
. mask = ( ( 0x1 < < 8 ) - 1 ) < < 0 ,
2014-06-19 21:34:43 +00:00
. irq_chip = & dummy_irq_chip ,
2014-06-19 21:34:44 +00:00
. status_reg = SPEAR310_INT_STS_MASK_REG ,
2012-08-03 15:33:10 +05:30
} ;
static struct spear_shirq spear310_shirq_ras2 = {
2014-06-19 21:34:39 +00:00
. offset = 8 ,
. nr_irqs = 5 ,
2014-06-19 21:34:41 +00:00
. mask = ( ( 0x1 < < 5 ) - 1 ) < < 8 ,
2014-06-19 21:34:43 +00:00
. irq_chip = & dummy_irq_chip ,
2014-06-19 21:34:44 +00:00
. status_reg = SPEAR310_INT_STS_MASK_REG ,
2012-08-03 15:33:10 +05:30
} ;
static struct spear_shirq spear310_shirq_ras3 = {
2014-06-19 21:34:39 +00:00
. offset = 13 ,
. nr_irqs = 1 ,
2014-06-19 21:34:41 +00:00
. mask = ( ( 0x1 < < 1 ) - 1 ) < < 13 ,
2014-06-19 21:34:43 +00:00
. irq_chip = & dummy_irq_chip ,
2014-06-19 21:34:44 +00:00
. status_reg = SPEAR310_INT_STS_MASK_REG ,
2012-08-03 15:33:10 +05:30
} ;
static struct spear_shirq spear310_shirq_intrcomm_ras = {
2014-06-19 21:34:39 +00:00
. offset = 14 ,
. nr_irqs = 3 ,
2014-06-19 21:34:41 +00:00
. mask = ( ( 0x1 < < 3 ) - 1 ) < < 14 ,
2014-06-19 21:34:43 +00:00
. irq_chip = & dummy_irq_chip ,
2014-06-19 21:34:44 +00:00
. status_reg = SPEAR310_INT_STS_MASK_REG ,
2012-08-03 15:33:10 +05:30
} ;
static struct spear_shirq * spear310_shirq_blocks [ ] = {
& spear310_shirq_ras1 ,
& spear310_shirq_ras2 ,
& spear310_shirq_ras3 ,
& spear310_shirq_intrcomm_ras ,
} ;
/* spear320 shared irq registers offsets and masks */
# define SPEAR320_INT_STS_MASK_REG 0x04
# define SPEAR320_INT_CLR_MASK_REG 0x04
# define SPEAR320_INT_ENB_MASK_REG 0x08
2014-06-19 21:34:40 +00:00
static struct spear_shirq spear320_shirq_ras3 = {
. offset = 0 ,
. nr_irqs = 7 ,
2014-06-19 21:34:41 +00:00
. mask = ( ( 0x1 < < 7 ) - 1 ) < < 0 ,
2012-08-03 15:33:10 +05:30
} ;
2014-06-19 21:34:40 +00:00
static struct spear_shirq spear320_shirq_ras1 = {
. offset = 7 ,
. nr_irqs = 3 ,
2014-06-19 21:34:41 +00:00
. mask = ( ( 0x1 < < 3 ) - 1 ) < < 7 ,
2014-06-19 21:34:43 +00:00
. irq_chip = & dummy_irq_chip ,
2014-06-19 21:34:44 +00:00
. status_reg = SPEAR320_INT_STS_MASK_REG ,
2012-08-03 15:33:10 +05:30
} ;
2014-06-19 21:34:40 +00:00
static struct spear_shirq spear320_shirq_ras2 = {
. offset = 10 ,
. nr_irqs = 1 ,
2014-06-19 21:34:41 +00:00
. mask = ( ( 0x1 < < 1 ) - 1 ) < < 10 ,
2014-06-19 21:34:43 +00:00
. irq_chip = & dummy_irq_chip ,
2014-06-19 21:34:44 +00:00
. status_reg = SPEAR320_INT_STS_MASK_REG ,
2012-08-03 15:33:10 +05:30
} ;
static struct spear_shirq spear320_shirq_intrcomm_ras = {
2014-06-19 21:34:39 +00:00
. offset = 11 ,
. nr_irqs = 11 ,
2014-06-19 21:34:41 +00:00
. mask = ( ( 0x1 < < 11 ) - 1 ) < < 11 ,
2014-06-19 21:34:43 +00:00
. irq_chip = & dummy_irq_chip ,
2014-06-19 21:34:44 +00:00
. status_reg = SPEAR320_INT_STS_MASK_REG ,
2012-08-03 15:33:10 +05:30
} ;
static struct spear_shirq * spear320_shirq_blocks [ ] = {
& spear320_shirq_ras3 ,
& spear320_shirq_ras1 ,
& spear320_shirq_ras2 ,
& spear320_shirq_intrcomm_ras ,
} ;
2015-07-16 22:42:21 +02:00
static void shirq_handler ( unsigned __irq , struct irq_desc * desc )
2010-05-03 09:24:30 +01:00
{
2015-06-04 12:13:20 +08:00
struct spear_shirq * shirq = irq_desc_get_handler_data ( desc ) ;
2014-06-19 21:34:42 +00:00
u32 pend ;
2010-05-03 09:24:30 +01:00
2014-06-19 21:34:44 +00:00
pend = readl ( shirq - > base + shirq - > status_reg ) & shirq - > mask ;
2014-06-19 21:34:42 +00:00
pend > > = shirq - > offset ;
2012-08-03 15:33:10 +05:30
2014-06-19 21:34:42 +00:00
while ( pend ) {
int irq = __ffs ( pend ) ;
2012-08-03 15:33:10 +05:30
2014-06-19 21:34:42 +00:00
pend & = ~ ( 0x1 < < irq ) ;
generic_handle_irq ( shirq - > virq_base + irq ) ;
2010-05-03 09:24:30 +01:00
}
}
2014-06-19 21:34:39 +00:00
static void __init spear_shirq_register ( struct spear_shirq * shirq ,
int parent_irq )
2010-05-03 09:24:30 +01:00
{
int i ;
2014-06-19 21:34:43 +00:00
if ( ! shirq - > irq_chip )
2012-08-03 15:33:10 +05:30
return ;
2010-05-03 09:24:30 +01:00
2015-06-16 23:07:01 +01:00
irq_set_chained_handler_and_data ( parent_irq , shirq_handler , shirq ) ;
2014-06-19 21:34:39 +00:00
2014-06-19 21:34:39 +00:00
for ( i = 0 ; i < shirq - > nr_irqs ; i + + ) {
irq_set_chip_and_handler ( shirq - > virq_base + i ,
2014-06-19 21:34:43 +00:00
shirq - > irq_chip , handle_simple_irq ) ;
2014-06-19 21:34:39 +00:00
set_irq_flags ( shirq - > virq_base + i , IRQF_VALID ) ;
irq_set_chip_data ( shirq - > virq_base + i , shirq ) ;
2010-05-03 09:24:30 +01:00
}
2012-08-03 15:33:10 +05:30
}
static int __init shirq_init ( struct spear_shirq * * shirq_blocks , int block_nr ,
struct device_node * np )
{
2014-06-19 21:34:39 +00:00
int i , parent_irq , virq_base , hwirq = 0 , nr_irqs = 0 ;
2014-06-19 21:34:37 +00:00
struct irq_domain * shirq_domain ;
2012-08-03 15:33:10 +05:30
void __iomem * base ;
base = of_iomap ( np , 0 ) ;
if ( ! base ) {
pr_err ( " %s: failed to map shirq registers \n " , __func__ ) ;
return - ENXIO ;
}
for ( i = 0 ; i < block_nr ; i + + )
2014-06-19 21:34:39 +00:00
nr_irqs + = shirq_blocks [ i ] - > nr_irqs ;
2012-08-03 15:33:10 +05:30
2014-06-19 21:34:39 +00:00
virq_base = irq_alloc_descs ( - 1 , 0 , nr_irqs , 0 ) ;
if ( IS_ERR_VALUE ( virq_base ) ) {
2012-08-03 15:33:10 +05:30
pr_err ( " %s: irq desc alloc failed \n " , __func__ ) ;
goto err_unmap ;
}
2014-06-19 21:34:39 +00:00
shirq_domain = irq_domain_add_legacy ( np , nr_irqs , virq_base , 0 ,
2012-08-03 15:33:10 +05:30
& irq_domain_simple_ops , NULL ) ;
if ( WARN_ON ( ! shirq_domain ) ) {
pr_warn ( " %s: irq domain init failed \n " , __func__ ) ;
goto err_free_desc ;
}
for ( i = 0 ; i < block_nr ; i + + ) {
shirq_blocks [ i ] - > base = base ;
2014-06-19 21:34:39 +00:00
shirq_blocks [ i ] - > virq_base = irq_find_mapping ( shirq_domain ,
2012-08-03 15:33:10 +05:30
hwirq ) ;
2014-06-19 21:34:39 +00:00
parent_irq = irq_of_parse_and_map ( np , i ) ;
spear_shirq_register ( shirq_blocks [ i ] , parent_irq ) ;
2014-06-19 21:34:39 +00:00
hwirq + = shirq_blocks [ i ] - > nr_irqs ;
2012-08-03 15:33:10 +05:30
}
2010-05-03 09:24:30 +01:00
return 0 ;
2012-08-03 15:33:10 +05:30
err_free_desc :
2014-06-19 21:34:39 +00:00
irq_free_descs ( virq_base , nr_irqs ) ;
2012-08-03 15:33:10 +05:30
err_unmap :
iounmap ( base ) ;
return - ENXIO ;
}
2014-06-19 21:34:38 +00:00
static int __init spear300_shirq_of_init ( struct device_node * np ,
struct device_node * parent )
2012-08-03 15:33:10 +05:30
{
return shirq_init ( spear300_shirq_blocks ,
ARRAY_SIZE ( spear300_shirq_blocks ) , np ) ;
}
2013-01-02 09:37:56 -06:00
IRQCHIP_DECLARE ( spear300_shirq , " st,spear300-shirq " , spear300_shirq_of_init ) ;
2012-08-03 15:33:10 +05:30
2014-06-19 21:34:38 +00:00
static int __init spear310_shirq_of_init ( struct device_node * np ,
struct device_node * parent )
2012-08-03 15:33:10 +05:30
{
return shirq_init ( spear310_shirq_blocks ,
ARRAY_SIZE ( spear310_shirq_blocks ) , np ) ;
}
2013-01-02 09:37:56 -06:00
IRQCHIP_DECLARE ( spear310_shirq , " st,spear310-shirq " , spear310_shirq_of_init ) ;
2012-08-03 15:33:10 +05:30
2014-06-19 21:34:38 +00:00
static int __init spear320_shirq_of_init ( struct device_node * np ,
struct device_node * parent )
2012-08-03 15:33:10 +05:30
{
return shirq_init ( spear320_shirq_blocks ,
ARRAY_SIZE ( spear320_shirq_blocks ) , np ) ;
2010-05-03 09:24:30 +01:00
}
2013-01-02 09:37:56 -06:00
IRQCHIP_DECLARE ( spear320_shirq , " st,spear320-shirq " , spear320_shirq_of_init ) ;