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
* Shiraz Hashim < shiraz . hashim @ st . com >
*
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>
2012-11-12 21:26:03 +04:00
# include <linux/irqchip/spear-shirq.h>
2012-08-03 14:03:10 +04:00
# 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"
2010-05-03 12:24:30 +04:00
static DEFINE_SPINLOCK ( lock ) ;
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
static struct spear_shirq spear300_shirq_ras1 = {
. irq_nr = 9 ,
. irq_bit_off = 0 ,
. regs = {
. enb_reg = SPEAR300_INT_ENB_MASK_REG ,
. status_reg = SPEAR300_INT_STS_MASK_REG ,
. clear_reg = - 1 ,
} ,
} ;
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 = {
. irq_nr = 8 ,
. irq_bit_off = 0 ,
. regs = {
. enb_reg = - 1 ,
. status_reg = SPEAR310_INT_STS_MASK_REG ,
. clear_reg = - 1 ,
} ,
} ;
static struct spear_shirq spear310_shirq_ras2 = {
. irq_nr = 5 ,
. irq_bit_off = 8 ,
. regs = {
. enb_reg = - 1 ,
. status_reg = SPEAR310_INT_STS_MASK_REG ,
. clear_reg = - 1 ,
} ,
} ;
static struct spear_shirq spear310_shirq_ras3 = {
. irq_nr = 1 ,
. irq_bit_off = 13 ,
. regs = {
. enb_reg = - 1 ,
. status_reg = SPEAR310_INT_STS_MASK_REG ,
. clear_reg = - 1 ,
} ,
} ;
static struct spear_shirq spear310_shirq_intrcomm_ras = {
. irq_nr = 3 ,
. irq_bit_off = 14 ,
. regs = {
. enb_reg = - 1 ,
. status_reg = SPEAR310_INT_STS_MASK_REG ,
. clear_reg = - 1 ,
} ,
} ;
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
static struct spear_shirq spear320_shirq_ras1 = {
. irq_nr = 3 ,
. irq_bit_off = 7 ,
. regs = {
. enb_reg = - 1 ,
. status_reg = SPEAR320_INT_STS_MASK_REG ,
. clear_reg = SPEAR320_INT_CLR_MASK_REG ,
. reset_to_clear = 1 ,
} ,
} ;
static struct spear_shirq spear320_shirq_ras2 = {
. irq_nr = 1 ,
. irq_bit_off = 10 ,
. regs = {
. enb_reg = - 1 ,
. status_reg = SPEAR320_INT_STS_MASK_REG ,
. clear_reg = SPEAR320_INT_CLR_MASK_REG ,
. reset_to_clear = 1 ,
} ,
} ;
static struct spear_shirq spear320_shirq_ras3 = {
. irq_nr = 3 ,
. irq_bit_off = 0 ,
. invalid_irq = 1 ,
. regs = {
. enb_reg = SPEAR320_INT_ENB_MASK_REG ,
. reset_to_enb = 1 ,
. status_reg = SPEAR320_INT_STS_MASK_REG ,
. clear_reg = SPEAR320_INT_CLR_MASK_REG ,
. reset_to_clear = 1 ,
} ,
} ;
static struct spear_shirq spear320_shirq_intrcomm_ras = {
. irq_nr = 11 ,
. irq_bit_off = 11 ,
. regs = {
. enb_reg = - 1 ,
. status_reg = SPEAR320_INT_STS_MASK_REG ,
. clear_reg = SPEAR320_INT_CLR_MASK_REG ,
. reset_to_clear = 1 ,
} ,
} ;
static struct spear_shirq * spear320_shirq_blocks [ ] = {
& spear320_shirq_ras3 ,
& spear320_shirq_ras1 ,
& spear320_shirq_ras2 ,
& spear320_shirq_intrcomm_ras ,
} ;
static void shirq_irq_mask_unmask ( struct irq_data * d , bool mask )
2010-05-03 12:24:30 +04:00
{
2010-11-29 13:22:33 +03:00
struct spear_shirq * shirq = irq_data_get_irq_chip_data ( d ) ;
2012-08-03 14:03:10 +04:00
u32 val , offset = d - > irq - shirq - > irq_base ;
2010-05-03 12:24:30 +04:00
unsigned long flags ;
2012-08-03 14:03:10 +04:00
if ( shirq - > regs . enb_reg = = - 1 )
2010-05-03 12:24:30 +04:00
return ;
spin_lock_irqsave ( & lock , flags ) ;
2012-08-03 14:03:10 +04:00
val = readl ( shirq - > base + shirq - > regs . enb_reg ) ;
if ( mask ^ shirq - > regs . reset_to_enb )
val & = ~ ( 0x1 < < shirq - > irq_bit_off < < offset ) ;
2010-05-03 12:24:30 +04:00
else
2012-08-03 14:03:10 +04:00
val | = 0x1 < < shirq - > irq_bit_off < < offset ;
writel ( val , shirq - > base + shirq - > regs . enb_reg ) ;
2010-05-03 12:24:30 +04:00
spin_unlock_irqrestore ( & lock , flags ) ;
2012-08-03 14:03:10 +04:00
2010-05-03 12:24:30 +04:00
}
2012-08-03 14:03:10 +04:00
static void shirq_irq_mask ( struct irq_data * d )
2010-05-03 12:24:30 +04:00
{
2012-08-03 14:03:10 +04:00
shirq_irq_mask_unmask ( d , 1 ) ;
}
2010-05-03 12:24:30 +04:00
2012-08-03 14:03:10 +04:00
static void shirq_irq_unmask ( struct irq_data * d )
{
shirq_irq_mask_unmask ( d , 0 ) ;
2010-05-03 12:24:30 +04:00
}
static struct irq_chip shirq_chip = {
2012-08-03 14:03:10 +04:00
. name = " spear-shirq " ,
2010-11-29 13:22:33 +03:00
. irq_ack = shirq_irq_mask ,
. irq_mask = shirq_irq_mask ,
. irq_unmask = shirq_irq_unmask ,
2010-05-03 12:24:30 +04:00
} ;
static void shirq_handler ( unsigned irq , struct irq_desc * desc )
{
2012-08-03 14:03:10 +04:00
u32 i , j , val , mask , tmp ;
struct irq_chip * chip ;
2011-03-24 15:25:22 +03:00
struct spear_shirq * shirq = irq_get_handler_data ( irq ) ;
2010-05-03 12:24:30 +04:00
2012-08-03 14:03:10 +04:00
chip = irq_get_chip ( irq ) ;
chip - > irq_ack ( & desc - > irq_data ) ;
mask = ( ( 0x1 < < shirq - > irq_nr ) - 1 ) < < shirq - > irq_bit_off ;
while ( ( val = readl ( shirq - > base + shirq - > regs . status_reg ) &
mask ) ) {
val > > = shirq - > irq_bit_off ;
for ( i = 0 , j = 1 ; i < shirq - > irq_nr ; i + + , j < < = 1 ) {
if ( ! ( j & val ) )
2010-05-03 12:24:30 +04:00
continue ;
2012-08-03 14:03:10 +04:00
generic_handle_irq ( shirq - > irq_base + i ) ;
2010-05-03 12:24:30 +04:00
/* clear interrupt */
2012-08-03 14:03:10 +04:00
if ( shirq - > regs . clear_reg = = - 1 )
2010-05-03 12:24:30 +04:00
continue ;
2012-08-03 14:03:10 +04:00
tmp = readl ( shirq - > base + shirq - > regs . clear_reg ) ;
2010-05-03 12:24:30 +04:00
if ( shirq - > regs . reset_to_clear )
2012-08-03 14:03:10 +04:00
tmp & = ~ ( j < < shirq - > irq_bit_off ) ;
2010-05-03 12:24:30 +04:00
else
2012-08-03 14:03:10 +04:00
tmp | = ( j < < shirq - > irq_bit_off ) ;
writel ( tmp , shirq - > base + shirq - > regs . clear_reg ) ;
2010-05-03 12:24:30 +04:00
}
}
2012-08-03 14:03:10 +04:00
chip - > irq_unmask ( & desc - > irq_data ) ;
2010-05-03 12:24:30 +04:00
}
2012-08-03 14:03:10 +04:00
static void __init spear_shirq_register ( struct spear_shirq * shirq )
2010-05-03 12:24:30 +04:00
{
int i ;
2012-08-03 14:03:10 +04:00
if ( shirq - > invalid_irq )
return ;
2010-05-03 12:24:30 +04:00
2011-03-24 15:25:22 +03:00
irq_set_chained_handler ( shirq - > irq , shirq_handler ) ;
2012-08-03 14:03:10 +04:00
for ( i = 0 ; i < shirq - > irq_nr ; i + + ) {
irq_set_chip_and_handler ( shirq - > irq_base + i ,
2011-03-24 15:35:09 +03:00
& shirq_chip , handle_simple_irq ) ;
2012-08-03 14:03:10 +04:00
set_irq_flags ( shirq - > irq_base + i , IRQF_VALID ) ;
irq_set_chip_data ( shirq - > irq_base + i , shirq ) ;
2010-05-03 12:24:30 +04:00
}
2011-03-24 15:25:22 +03:00
irq_set_handler_data ( shirq - > irq , shirq ) ;
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 )
{
int i , irq_base , hwirq = 0 , irq_nr = 0 ;
static struct irq_domain * shirq_domain ;
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 + + )
irq_nr + = shirq_blocks [ i ] - > irq_nr ;
irq_base = irq_alloc_descs ( - 1 , 0 , irq_nr , 0 ) ;
if ( IS_ERR_VALUE ( irq_base ) ) {
pr_err ( " %s: irq desc alloc failed \n " , __func__ ) ;
goto err_unmap ;
}
shirq_domain = irq_domain_add_legacy ( np , irq_nr , irq_base , 0 ,
& 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 ;
shirq_blocks [ i ] - > irq_base = irq_find_mapping ( shirq_domain ,
hwirq ) ;
shirq_blocks [ i ] - > irq = irq_of_parse_and_map ( np , i ) ;
spear_shirq_register ( shirq_blocks [ i ] ) ;
hwirq + = shirq_blocks [ i ] - > irq_nr ;
}
2010-05-03 12:24:30 +04:00
return 0 ;
2012-08-03 14:03:10 +04:00
err_free_desc :
irq_free_descs ( irq_base , irq_nr ) ;
err_unmap :
iounmap ( base ) ;
return - ENXIO ;
}
int __init spear300_shirq_of_init ( struct device_node * np ,
struct device_node * parent )
{
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
int __init spear310_shirq_of_init ( struct device_node * np ,
struct device_node * parent )
{
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
int __init spear320_shirq_of_init ( struct device_node * np ,
struct device_node * parent )
{
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 ) ;