2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2013-06-26 11:18:48 +04:00
/*
* drivers / irq / irq - nvic . c
*
* Copyright ( C ) 2008 ARM Limited , All Rights Reserved .
* Copyright ( C ) 2013 Pengutronix
*
* Support for the Nested Vectored Interrupt Controller found on the
* ARMv7 - M CPUs ( Cortex - M3 / M4 )
*/
# define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/slab.h>
# include <linux/err.h>
# include <linux/io.h>
# include <linux/of.h>
# include <linux/of_address.h>
# include <linux/irq.h>
2015-07-08 00:11:46 +03:00
# include <linux/irqchip.h>
2013-06-26 11:18:48 +04:00
# include <linux/irqdomain.h>
# include <asm/v7m.h>
# include <asm/exception.h>
# define NVIC_ISER 0x000
# define NVIC_ICER 0x080
# define NVIC_IPR 0x300
# define NVIC_MAX_BANKS 16
/*
* Each bank handles 32 irqs . Only the 16 th ( = last ) bank handles only
* 16 irqs .
*/
# define NVIC_MAX_IRQ ((NVIC_MAX_BANKS - 1) * 32 + 16)
static struct irq_domain * nvic_irq_domain ;
asmlinkage void __exception_irq_entry
nvic_handle_irq ( irq_hw_number_t hwirq , struct pt_regs * regs )
{
unsigned int irq = irq_linear_revmap ( nvic_irq_domain , hwirq ) ;
handle_IRQ ( irq , regs ) ;
}
2015-05-16 12:44:16 +03:00
static int nvic_irq_domain_alloc ( struct irq_domain * domain , unsigned int virq ,
unsigned int nr_irqs , void * arg )
{
int i , ret ;
irq_hw_number_t hwirq ;
unsigned int type = IRQ_TYPE_NONE ;
2015-10-13 14:51:33 +03:00
struct irq_fwspec * fwspec = arg ;
2015-05-16 12:44:16 +03:00
2019-12-10 14:11:10 +03:00
ret = irq_domain_translate_onecell ( domain , fwspec , & hwirq , & type ) ;
2015-05-16 12:44:16 +03:00
if ( ret )
return ret ;
for ( i = 0 ; i < nr_irqs ; i + + )
irq_map_generic_chip ( domain , virq + i , hwirq + i ) ;
return 0 ;
}
static const struct irq_domain_ops nvic_irq_domain_ops = {
2019-12-10 14:11:10 +03:00
. translate = irq_domain_translate_onecell ,
2015-05-16 12:44:16 +03:00
. alloc = nvic_irq_domain_alloc ,
. free = irq_domain_free_irqs_top ,
} ;
2013-06-26 11:18:48 +04:00
static int __init nvic_of_init ( struct device_node * node ,
struct device_node * parent )
{
unsigned int clr = IRQ_NOREQUEST | IRQ_NOPROBE | IRQ_NOAUTOEN ;
unsigned int irqs , i , ret , numbanks ;
void __iomem * nvic_base ;
numbanks = ( readl_relaxed ( V7M_SCS_ICTR ) &
V7M_SCS_ICTR_INTLINESNUM_MASK ) + 1 ;
nvic_base = of_iomap ( node , 0 ) ;
if ( ! nvic_base ) {
pr_warn ( " unable to map nvic registers \n " ) ;
return - ENOMEM ;
}
irqs = numbanks * 32 ;
if ( irqs > NVIC_MAX_IRQ )
irqs = NVIC_MAX_IRQ ;
nvic_irq_domain =
2015-05-16 12:44:16 +03:00
irq_domain_add_linear ( node , irqs , & nvic_irq_domain_ops , NULL ) ;
2013-06-26 11:18:48 +04:00
if ( ! nvic_irq_domain ) {
pr_warn ( " Failed to allocate irq domain \n " ) ;
return - ENOMEM ;
}
2013-07-05 11:39:11 +04:00
ret = irq_alloc_domain_generic_chips ( nvic_irq_domain , 32 , 1 ,
2013-06-26 11:18:48 +04:00
" nvic_irq " , handle_fasteoi_irq ,
clr , 0 , IRQ_GC_INIT_MASK_CACHE ) ;
if ( ret ) {
pr_warn ( " Failed to allocate irq chips \n " ) ;
irq_domain_remove ( nvic_irq_domain ) ;
return ret ;
}
for ( i = 0 ; i < numbanks ; + + i ) {
struct irq_chip_generic * gc ;
gc = irq_get_domain_generic_chip ( nvic_irq_domain , 32 * i ) ;
gc - > reg_base = nvic_base + 4 * i ;
gc - > chip_types [ 0 ] . regs . enable = NVIC_ISER ;
gc - > chip_types [ 0 ] . regs . disable = NVIC_ICER ;
gc - > chip_types [ 0 ] . chip . irq_mask = irq_gc_mask_disable_reg ;
gc - > chip_types [ 0 ] . chip . irq_unmask = irq_gc_unmask_enable_reg ;
2014-06-04 19:01:52 +04:00
/* This is a no-op as end of interrupt is signaled by the
* exception return sequence .
*/
gc - > chip_types [ 0 ] . chip . irq_eoi = irq_gc_noop ;
2013-06-26 11:18:48 +04:00
/* disable interrupts */
writel_relaxed ( ~ 0 , gc - > reg_base + NVIC_ICER ) ;
}
/* Set priority on all interrupts */
for ( i = 0 ; i < irqs ; i + = 4 )
writel_relaxed ( 0 , nvic_base + NVIC_IPR + i ) ;
return 0 ;
}
IRQCHIP_DECLARE ( armv7m_nvic , " arm,armv7m-nvic " , nvic_of_init ) ;