2 * drivers/irq/irq-nvic.c
4 * Copyright (C) 2008 ARM Limited, All Rights Reserved.
5 * Copyright (C) 2013 Pengutronix
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License version 2 as
9 * published by the Free Software Foundation.
11 * Support for the Nested Vectored Interrupt Controller found on the
12 * ARMv7-M CPUs (Cortex-M3/M4)
14 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
16 #include <linux/init.h>
17 #include <linux/kernel.h>
18 #include <linux/slab.h>
19 #include <linux/err.h>
22 #include <linux/of_address.h>
23 #include <linux/irq.h>
24 #include <linux/irqchip.h>
25 #include <linux/irqdomain.h>
28 #include <asm/exception.h>
30 #define NVIC_ISER 0x000
31 #define NVIC_ICER 0x080
32 #define NVIC_IPR 0x300
34 #define NVIC_MAX_BANKS 16
36 * Each bank handles 32 irqs. Only the 16th (= last) bank handles only
39 #define NVIC_MAX_IRQ ((NVIC_MAX_BANKS - 1) * 32 + 16)
41 static struct irq_domain
*nvic_irq_domain
;
43 asmlinkage
void __exception_irq_entry
44 nvic_handle_irq(irq_hw_number_t hwirq
, struct pt_regs
*regs
)
46 unsigned int irq
= irq_linear_revmap(nvic_irq_domain
, hwirq
);
48 handle_IRQ(irq
, regs
);
51 static int nvic_irq_domain_translate(struct irq_domain
*d
,
52 struct irq_fwspec
*fwspec
,
53 unsigned long *hwirq
, unsigned int *type
)
55 if (WARN_ON(fwspec
->param_count
< 1))
57 *hwirq
= fwspec
->param
[0];
58 *type
= IRQ_TYPE_NONE
;
62 static int nvic_irq_domain_alloc(struct irq_domain
*domain
, unsigned int virq
,
63 unsigned int nr_irqs
, void *arg
)
66 irq_hw_number_t hwirq
;
67 unsigned int type
= IRQ_TYPE_NONE
;
68 struct irq_fwspec
*fwspec
= arg
;
70 ret
= nvic_irq_domain_translate(domain
, fwspec
, &hwirq
, &type
);
74 for (i
= 0; i
< nr_irqs
; i
++)
75 irq_map_generic_chip(domain
, virq
+ i
, hwirq
+ i
);
80 static const struct irq_domain_ops nvic_irq_domain_ops
= {
81 .translate
= nvic_irq_domain_translate
,
82 .alloc
= nvic_irq_domain_alloc
,
83 .free
= irq_domain_free_irqs_top
,
86 static int __init
nvic_of_init(struct device_node
*node
,
87 struct device_node
*parent
)
89 unsigned int clr
= IRQ_NOREQUEST
| IRQ_NOPROBE
| IRQ_NOAUTOEN
;
90 unsigned int irqs
, i
, ret
, numbanks
;
91 void __iomem
*nvic_base
;
93 numbanks
= (readl_relaxed(V7M_SCS_ICTR
) &
94 V7M_SCS_ICTR_INTLINESNUM_MASK
) + 1;
96 nvic_base
= of_iomap(node
, 0);
98 pr_warn("unable to map nvic registers\n");
102 irqs
= numbanks
* 32;
103 if (irqs
> NVIC_MAX_IRQ
)
107 irq_domain_add_linear(node
, irqs
, &nvic_irq_domain_ops
, NULL
);
109 if (!nvic_irq_domain
) {
110 pr_warn("Failed to allocate irq domain\n");
114 ret
= irq_alloc_domain_generic_chips(nvic_irq_domain
, 32, 1,
115 "nvic_irq", handle_fasteoi_irq
,
116 clr
, 0, IRQ_GC_INIT_MASK_CACHE
);
118 pr_warn("Failed to allocate irq chips\n");
119 irq_domain_remove(nvic_irq_domain
);
123 for (i
= 0; i
< numbanks
; ++i
) {
124 struct irq_chip_generic
*gc
;
126 gc
= irq_get_domain_generic_chip(nvic_irq_domain
, 32 * i
);
127 gc
->reg_base
= nvic_base
+ 4 * i
;
128 gc
->chip_types
[0].regs
.enable
= NVIC_ISER
;
129 gc
->chip_types
[0].regs
.disable
= NVIC_ICER
;
130 gc
->chip_types
[0].chip
.irq_mask
= irq_gc_mask_disable_reg
;
131 gc
->chip_types
[0].chip
.irq_unmask
= irq_gc_unmask_enable_reg
;
132 /* This is a no-op as end of interrupt is signaled by the
133 * exception return sequence.
135 gc
->chip_types
[0].chip
.irq_eoi
= irq_gc_noop
;
137 /* disable interrupts */
138 writel_relaxed(~0, gc
->reg_base
+ NVIC_ICER
);
141 /* Set priority on all interrupts */
142 for (i
= 0; i
< irqs
; i
+= 4)
143 writel_relaxed(0, nvic_base
+ NVIC_IPR
+ i
);
147 IRQCHIP_DECLARE(armv7m_nvic
, "arm,armv7m-nvic", nvic_of_init
);