2019-05-19 16:51:34 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2013-03-19 14:21:44 +04:00
/*
* interrupt controller support for CSR SiRFprimaII
*
* Copyright ( c ) 2011 Cambridge Silicon Radio Limited , a CSR plc group company .
*/
# include <linux/init.h>
# include <linux/io.h>
# include <linux/irq.h>
# include <linux/of.h>
# include <linux/of_address.h>
2015-07-08 00:11:46 +03:00
# include <linux/irqchip.h>
2013-03-19 14:21:44 +04:00
# include <linux/irqdomain.h>
# include <linux/syscore_ops.h>
# include <asm/mach/irq.h>
# include <asm/exception.h>
2015-07-06 13:18:29 +03:00
# define SIRFSOC_INT_RISC_MASK0 0x0018
# define SIRFSOC_INT_RISC_MASK1 0x001C
# define SIRFSOC_INT_RISC_LEVEL0 0x0020
# define SIRFSOC_INT_RISC_LEVEL1 0x0024
2013-03-19 14:21:44 +04:00
# define SIRFSOC_INIT_IRQ_ID 0x0038
2015-07-06 13:18:29 +03:00
# define SIRFSOC_INT_BASE_OFFSET 0x0004
2013-03-19 14:21:44 +04:00
2013-08-06 09:37:13 +04:00
# define SIRFSOC_NUM_IRQS 64
2015-07-06 13:18:29 +03:00
# define SIRFSOC_NUM_BANKS (SIRFSOC_NUM_IRQS / 32)
2013-03-19 14:21:44 +04:00
static struct irq_domain * sirfsoc_irqdomain ;
2016-06-07 15:18:30 +03:00
static void __iomem * sirfsoc_irq_get_regbase ( void )
{
return ( void __iomem __force * ) sirfsoc_irqdomain - > host_data ;
}
2015-07-06 13:18:29 +03:00
static __init void sirfsoc_alloc_gc ( void __iomem * base )
2013-03-19 14:21:44 +04:00
{
2013-08-06 09:37:13 +04:00
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN ;
irqchip: sirf: set IRQ_LEVEL status_flags
SiRF internal interrupts are using level trigger. we need to tell the irq
core this information. otherwise, we might get some problems as below
1. disable_irq(n)
here irq core will mark the disabled flag but still keep the irq enabled
due to involved lazy-disable
2. doing someting after disable_irq(n)
in step 2, if one interrupt n comes, irq core will mark it as pending and
mask the HW interrupt really. we name the coming interrupt as "X".
3. enable_irq(n)
this will unmask the interrupt, so the level-trigger HW interrupt will come
again, irq_handler will enter as "E1". after that, irq core will also check
whether irq n is pending, if yes, and pending interrupt is not level-trigger,
irq core will execute the pending irq_handler.
so if we don't set the IRQ_LEVEL flag here, irq core will execute pending
X again as "E2", but actually the pending interrupt has been handled by "E1".
that makes a level-trigger HW interrupt is executed twice.
here we fix the issue to avoid redundant interrupt overload.
Signed-off-by: Barry Song <Baohua.Song@csr.com>
Signed-off-by: Huayi Li <Huayi.Li@csr.com>
Signed-off-by: Olof Johansson <olof@lixom.net>
2014-01-03 07:34:46 +04:00
unsigned int set = IRQ_LEVEL ;
2015-07-06 13:18:29 +03:00
struct irq_chip_generic * gc ;
struct irq_chip_type * ct ;
int i ;
irq_alloc_domain_generic_chips ( sirfsoc_irqdomain , 32 , 1 , " irq_sirfsoc " ,
handle_level_irq , clr , set ,
IRQ_GC_INIT_MASK_CACHE ) ;
for ( i = 0 ; i < SIRFSOC_NUM_BANKS ; i + + ) {
gc = irq_get_domain_generic_chip ( sirfsoc_irqdomain , i * 32 ) ;
gc - > reg_base = base + i * SIRFSOC_INT_BASE_OFFSET ;
ct = gc - > chip_types ;
ct - > chip . irq_mask = irq_gc_mask_clr_bit ;
ct - > chip . irq_unmask = irq_gc_mask_set_bit ;
ct - > regs . mask = SIRFSOC_INT_RISC_MASK0 ;
}
2013-03-19 14:21:44 +04:00
}
2014-03-05 04:40:30 +04:00
static void __exception_irq_entry sirfsoc_handle_irq ( struct pt_regs * regs )
2013-03-19 14:21:44 +04:00
{
2016-06-07 15:18:30 +03:00
void __iomem * base = sirfsoc_irq_get_regbase ( ) ;
2014-08-26 14:03:27 +04:00
u32 irqstat ;
2013-03-19 14:21:44 +04:00
irqstat = readl_relaxed ( base + SIRFSOC_INIT_IRQ_ID ) ;
2014-08-26 14:03:27 +04:00
handle_domain_irq ( sirfsoc_irqdomain , irqstat & 0xff , regs ) ;
2013-03-19 14:21:44 +04:00
}
2014-05-06 18:58:36 +04:00
static int __init sirfsoc_irq_init ( struct device_node * np ,
struct device_node * parent )
2013-03-19 14:21:44 +04:00
{
void __iomem * base = of_iomap ( np , 0 ) ;
if ( ! base )
panic ( " unable to map intc cpu registers \n " ) ;
2013-08-06 09:37:13 +04:00
sirfsoc_irqdomain = irq_domain_add_linear ( np , SIRFSOC_NUM_IRQS ,
2015-07-06 13:18:29 +03:00
& irq_generic_chip_ops , base ) ;
sirfsoc_alloc_gc ( base ) ;
2013-03-19 14:21:44 +04:00
writel_relaxed ( 0 , base + SIRFSOC_INT_RISC_LEVEL0 ) ;
writel_relaxed ( 0 , base + SIRFSOC_INT_RISC_LEVEL1 ) ;
writel_relaxed ( 0 , base + SIRFSOC_INT_RISC_MASK0 ) ;
writel_relaxed ( 0 , base + SIRFSOC_INT_RISC_MASK1 ) ;
set_handle_irq ( sirfsoc_handle_irq ) ;
return 0 ;
}
IRQCHIP_DECLARE ( sirfsoc_intc , " sirf,prima2-intc " , sirfsoc_irq_init ) ;
struct sirfsoc_irq_status {
u32 mask0 ;
u32 mask1 ;
u32 level0 ;
u32 level1 ;
} ;
static struct sirfsoc_irq_status sirfsoc_irq_st ;
static int sirfsoc_irq_suspend ( void )
{
2016-06-07 15:18:30 +03:00
void __iomem * base = sirfsoc_irq_get_regbase ( ) ;
2013-03-19 14:21:44 +04:00
sirfsoc_irq_st . mask0 = readl_relaxed ( base + SIRFSOC_INT_RISC_MASK0 ) ;
sirfsoc_irq_st . mask1 = readl_relaxed ( base + SIRFSOC_INT_RISC_MASK1 ) ;
sirfsoc_irq_st . level0 = readl_relaxed ( base + SIRFSOC_INT_RISC_LEVEL0 ) ;
sirfsoc_irq_st . level1 = readl_relaxed ( base + SIRFSOC_INT_RISC_LEVEL1 ) ;
return 0 ;
}
static void sirfsoc_irq_resume ( void )
{
2016-06-07 15:18:30 +03:00
void __iomem * base = sirfsoc_irq_get_regbase ( ) ;
2013-03-19 14:21:44 +04:00
writel_relaxed ( sirfsoc_irq_st . mask0 , base + SIRFSOC_INT_RISC_MASK0 ) ;
writel_relaxed ( sirfsoc_irq_st . mask1 , base + SIRFSOC_INT_RISC_MASK1 ) ;
writel_relaxed ( sirfsoc_irq_st . level0 , base + SIRFSOC_INT_RISC_LEVEL0 ) ;
writel_relaxed ( sirfsoc_irq_st . level1 , base + SIRFSOC_INT_RISC_LEVEL1 ) ;
}
static struct syscore_ops sirfsoc_irq_syscore_ops = {
. suspend = sirfsoc_irq_suspend ,
. resume = sirfsoc_irq_resume ,
} ;
static int __init sirfsoc_irq_pm_init ( void )
{
if ( ! sirfsoc_irqdomain )
return 0 ;
register_syscore_ops ( & sirfsoc_irq_syscore_ops ) ;
return 0 ;
}
device_initcall ( sirfsoc_irq_pm_init ) ;