2010-05-03 12:24:30 +04:00
/*
* SPEAr platform shared irq layer source file
*
2012-11-12 21:26:03 +04:00
* Copyright ( C ) 2009 - 2012 ST Microelectronics
2012-06-20 23:53:02 +04:00
* Viresh Kumar < viresh . linux @ gmail . com >
2010-05-03 12:24:30 +04:00
*
2012-11-12 21:26:03 +04:00
* Copyright ( C ) 2012 ST Microelectronics
2014-04-19 02:07:16 +04:00
* Shiraz Hashim < shiraz . linux . kernel @ gmail . com >
2012-11-12 21:26:03 +04:00
*
2010-05-03 12:24:30 +04: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 14:03:10 +04:00
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
2010-05-03 12:24:30 +04:00
# include <linux/err.h>
2012-08-03 14:03:10 +04:00
# include <linux/export.h>
# include <linux/interrupt.h>
2010-05-03 12:24:30 +04:00
# include <linux/io.h>
# include <linux/irq.h>
2012-08-03 14:03:10 +04:00
# include <linux/irqdomain.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/of_irq.h>
2010-05-03 12:24:30 +04:00
# include <linux/spinlock.h>
2013-01-02 19:37:56 +04:00
# include "irqchip.h"
2014-06-20 01:34:38 +04:00
/*
* struct shirq_regs : shared irq register configuration
*
* enb_reg : enable register offset
* reset_to_enb : val 1 indicates , we need to clear bit for enabling interrupt
* status_reg : status register offset
* status_reg_mask : status register valid mask
*/
struct shirq_regs {
u32 enb_reg ;
u32 reset_to_enb ;
u32 status_reg ;
} ;
/*
* struct spear_shirq : shared irq structure
*
2014-06-20 01:34:39 +04:00
* base : Base register address
* regs : Register configuration for shared irq block
2014-06-20 01:34:41 +04:00
* mask : Mask to apply to the status register
2014-06-20 01:34:39 +04: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-20 01:34:43 +04:00
* irq_chip : Interrupt controller chip used for this instance ,
* if NULL group is disabled , but accounted
2014-06-20 01:34:38 +04:00
*/
struct spear_shirq {
2014-06-20 01:34:39 +04:00
void __iomem * base ;
struct shirq_regs regs ;
2014-06-20 01:34:41 +04:00
u32 mask ;
2014-06-20 01:34:39 +04:00
u32 virq_base ;
u32 nr_irqs ;
u32 offset ;
2014-06-20 01:34:43 +04:00
struct irq_chip * irq_chip ;
2014-06-20 01:34:38 +04:00
} ;
2012-08-03 14:03:10 +04:00
/* spear300 shared irq registers offsets and masks */
# define SPEAR300_INT_ENB_MASK_REG 0x54
# define SPEAR300_INT_STS_MASK_REG 0x58
2014-06-20 01:34:43 +04: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 ;
u32 __iomem * reg = shirq - > base + shirq - > regs . enb_reg ;
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 ;
u32 __iomem * reg = shirq - > base + shirq - > regs . enb_reg ;
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 14:03:10 +04:00
static struct spear_shirq spear300_shirq_ras1 = {
2014-06-20 01:34:39 +04:00
. offset = 0 ,
. nr_irqs = 9 ,
2014-06-20 01:34:41 +04:00
. mask = ( ( 0x1 < < 9 ) - 1 ) < < 0 ,
2014-06-20 01:34:43 +04:00
. irq_chip = & shirq_chip ,
2012-08-03 14:03:10 +04:00
. regs = {
. enb_reg = SPEAR300_INT_ENB_MASK_REG ,
. status_reg = SPEAR300_INT_STS_MASK_REG ,
} ,
} ;
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-20 01:34:39 +04:00
. offset = 0 ,
. nr_irqs = 8 ,
2014-06-20 01:34:41 +04:00
. mask = ( ( 0x1 < < 8 ) - 1 ) < < 0 ,
2014-06-20 01:34:43 +04:00
. irq_chip = & dummy_irq_chip ,
2012-08-03 14:03:10 +04:00
. regs = {
. status_reg = SPEAR310_INT_STS_MASK_REG ,
} ,
} ;
static struct spear_shirq spear310_shirq_ras2 = {
2014-06-20 01:34:39 +04:00
. offset = 8 ,
. nr_irqs = 5 ,
2014-06-20 01:34:41 +04:00
. mask = ( ( 0x1 < < 5 ) - 1 ) < < 8 ,
2014-06-20 01:34:43 +04:00
. irq_chip = & dummy_irq_chip ,
2012-08-03 14:03:10 +04:00
. regs = {
. enb_reg = - 1 ,
. status_reg = SPEAR310_INT_STS_MASK_REG ,
} ,
} ;
static struct spear_shirq spear310_shirq_ras3 = {
2014-06-20 01:34:39 +04:00
. offset = 13 ,
. nr_irqs = 1 ,
2014-06-20 01:34:41 +04:00
. mask = ( ( 0x1 < < 1 ) - 1 ) < < 13 ,
2014-06-20 01:34:43 +04:00
. irq_chip = & dummy_irq_chip ,
2012-08-03 14:03:10 +04:00
. regs = {
. status_reg = SPEAR310_INT_STS_MASK_REG ,
} ,
} ;
static struct spear_shirq spear310_shirq_intrcomm_ras = {
2014-06-20 01:34:39 +04:00
. offset = 14 ,
. nr_irqs = 3 ,
2014-06-20 01:34:41 +04:00
. mask = ( ( 0x1 < < 3 ) - 1 ) < < 14 ,
2014-06-20 01:34:43 +04:00
. irq_chip = & dummy_irq_chip ,
2012-08-03 14:03:10 +04:00
. regs = {
. status_reg = SPEAR310_INT_STS_MASK_REG ,
} ,
} ;
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-20 01:34:40 +04:00
static struct spear_shirq spear320_shirq_ras3 = {
. offset = 0 ,
. nr_irqs = 7 ,
2014-06-20 01:34:41 +04:00
. mask = ( ( 0x1 < < 7 ) - 1 ) < < 0 ,
2012-08-03 14:03:10 +04:00
} ;
2014-06-20 01:34:40 +04:00
static struct spear_shirq spear320_shirq_ras1 = {
. offset = 7 ,
. nr_irqs = 3 ,
2014-06-20 01:34:41 +04:00
. mask = ( ( 0x1 < < 3 ) - 1 ) < < 7 ,
2014-06-20 01:34:43 +04:00
. irq_chip = & dummy_irq_chip ,
2012-08-03 14:03:10 +04:00
. regs = {
. status_reg = SPEAR320_INT_STS_MASK_REG ,
} ,
} ;
2014-06-20 01:34:40 +04:00
static struct spear_shirq spear320_shirq_ras2 = {
. offset = 10 ,
. nr_irqs = 1 ,
2014-06-20 01:34:41 +04:00
. mask = ( ( 0x1 < < 1 ) - 1 ) < < 10 ,
2014-06-20 01:34:43 +04:00
. irq_chip = & dummy_irq_chip ,
2012-08-03 14:03:10 +04:00
. regs = {
. status_reg = SPEAR320_INT_STS_MASK_REG ,
} ,
} ;
static struct spear_shirq spear320_shirq_intrcomm_ras = {
2014-06-20 01:34:39 +04:00
. offset = 11 ,
. nr_irqs = 11 ,
2014-06-20 01:34:41 +04:00
. mask = ( ( 0x1 < < 11 ) - 1 ) < < 11 ,
2014-06-20 01:34:43 +04:00
. irq_chip = & dummy_irq_chip ,
2012-08-03 14:03:10 +04:00
. regs = {
. status_reg = SPEAR320_INT_STS_MASK_REG ,
} ,
} ;
static struct spear_shirq * spear320_shirq_blocks [ ] = {
& spear320_shirq_ras3 ,
& spear320_shirq_ras1 ,
& spear320_shirq_ras2 ,
& spear320_shirq_intrcomm_ras ,
} ;
2010-05-03 12:24:30 +04:00
static void shirq_handler ( unsigned irq , struct irq_desc * desc )
{
2011-03-24 15:25:22 +03:00
struct spear_shirq * shirq = irq_get_handler_data ( irq ) ;
2014-06-20 01:34:42 +04:00
u32 pend ;
2010-05-03 12:24:30 +04:00
2014-06-20 01:34:42 +04:00
pend = readl ( shirq - > base + shirq - > regs . status_reg ) & shirq - > mask ;
pend > > = shirq - > offset ;
2012-08-03 14:03:10 +04:00
2014-06-20 01:34:42 +04:00
while ( pend ) {
int irq = __ffs ( pend ) ;
2012-08-03 14:03:10 +04:00
2014-06-20 01:34:42 +04:00
pend & = ~ ( 0x1 < < irq ) ;
generic_handle_irq ( shirq - > virq_base + irq ) ;
2010-05-03 12:24:30 +04:00
}
}
2014-06-20 01:34:39 +04:00
static void __init spear_shirq_register ( struct spear_shirq * shirq ,
int parent_irq )
2010-05-03 12:24:30 +04:00
{
int i ;
2014-06-20 01:34:43 +04:00
if ( ! shirq - > irq_chip )
2012-08-03 14:03:10 +04:00
return ;
2010-05-03 12:24:30 +04:00
2014-06-20 01:34:39 +04:00
irq_set_chained_handler ( parent_irq , shirq_handler ) ;
irq_set_handler_data ( parent_irq , shirq ) ;
2014-06-20 01:34:39 +04:00
for ( i = 0 ; i < shirq - > nr_irqs ; i + + ) {
irq_set_chip_and_handler ( shirq - > virq_base + i ,
2014-06-20 01:34:43 +04:00
shirq - > irq_chip , handle_simple_irq ) ;
2014-06-20 01:34:39 +04:00
set_irq_flags ( shirq - > virq_base + i , IRQF_VALID ) ;
irq_set_chip_data ( shirq - > virq_base + i , shirq ) ;
2010-05-03 12:24:30 +04:00
}
2012-08-03 14:03:10 +04:00
}
static int __init shirq_init ( struct spear_shirq * * shirq_blocks , int block_nr ,
struct device_node * np )
{
2014-06-20 01:34:39 +04:00
int i , parent_irq , virq_base , hwirq = 0 , nr_irqs = 0 ;
2014-06-20 01:34:37 +04:00
struct irq_domain * shirq_domain ;
2012-08-03 14:03:10 +04:00
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-20 01:34:39 +04:00
nr_irqs + = shirq_blocks [ i ] - > nr_irqs ;
2012-08-03 14:03:10 +04:00
2014-06-20 01:34:39 +04:00
virq_base = irq_alloc_descs ( - 1 , 0 , nr_irqs , 0 ) ;
if ( IS_ERR_VALUE ( virq_base ) ) {
2012-08-03 14:03:10 +04:00
pr_err ( " %s: irq desc alloc failed \n " , __func__ ) ;
goto err_unmap ;
}
2014-06-20 01:34:39 +04:00
shirq_domain = irq_domain_add_legacy ( np , nr_irqs , virq_base , 0 ,
2012-08-03 14:03:10 +04:00
& 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-20 01:34:39 +04:00
shirq_blocks [ i ] - > virq_base = irq_find_mapping ( shirq_domain ,
2012-08-03 14:03:10 +04:00
hwirq ) ;
2014-06-20 01:34:39 +04:00
parent_irq = irq_of_parse_and_map ( np , i ) ;
spear_shirq_register ( shirq_blocks [ i ] , parent_irq ) ;
2014-06-20 01:34:39 +04:00
hwirq + = shirq_blocks [ i ] - > nr_irqs ;
2012-08-03 14:03:10 +04:00
}
2010-05-03 12:24:30 +04:00
return 0 ;
2012-08-03 14:03:10 +04:00
err_free_desc :
2014-06-20 01:34:39 +04:00
irq_free_descs ( virq_base , nr_irqs ) ;
2012-08-03 14:03:10 +04:00
err_unmap :
iounmap ( base ) ;
return - ENXIO ;
}
2014-06-20 01:34:38 +04:00
static int __init spear300_shirq_of_init ( struct device_node * np ,
struct device_node * parent )
2012-08-03 14:03:10 +04:00
{
return shirq_init ( spear300_shirq_blocks ,
ARRAY_SIZE ( spear300_shirq_blocks ) , np ) ;
}
2013-01-02 19:37:56 +04:00
IRQCHIP_DECLARE ( spear300_shirq , " st,spear300-shirq " , spear300_shirq_of_init ) ;
2012-08-03 14:03:10 +04:00
2014-06-20 01:34:38 +04:00
static int __init spear310_shirq_of_init ( struct device_node * np ,
struct device_node * parent )
2012-08-03 14:03:10 +04:00
{
return shirq_init ( spear310_shirq_blocks ,
ARRAY_SIZE ( spear310_shirq_blocks ) , np ) ;
}
2013-01-02 19:37:56 +04:00
IRQCHIP_DECLARE ( spear310_shirq , " st,spear310-shirq " , spear310_shirq_of_init ) ;
2012-08-03 14:03:10 +04:00
2014-06-20 01:34:38 +04:00
static int __init spear320_shirq_of_init ( struct device_node * np ,
struct device_node * parent )
2012-08-03 14:03:10 +04:00
{
return shirq_init ( spear320_shirq_blocks ,
ARRAY_SIZE ( spear320_shirq_blocks ) , np ) ;
2010-05-03 12:24:30 +04:00
}
2013-01-02 19:37:56 +04:00
IRQCHIP_DECLARE ( spear320_shirq , " st,spear320-shirq " , spear320_shirq_of_init ) ;