Commit | Line | Data |
---|---|---|
2d242aa2 BG |
1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | // | |
3 | // Copyright (C) 2006, 2019 Texas Instruments. | |
4 | // | |
5 | // Interrupt handler for DaVinci boards. | |
6 | ||
7c6337e2 KH |
7 | #include <linux/kernel.h> |
8 | #include <linux/init.h> | |
9 | #include <linux/interrupt.h> | |
10 | #include <linux/irq.h> | |
06a28716 | 11 | #include <linux/irqchip/irq-davinci-aintc.h> |
fced80c7 | 12 | #include <linux/io.h> |
74b0eac2 | 13 | #include <linux/irqdomain.h> |
7c6337e2 | 14 | |
d0064594 | 15 | #include <asm/exception.h> |
7c6337e2 | 16 | |
919da6f1 BG |
17 | #define DAVINCI_AINTC_FIQ_REG0 0x00 |
18 | #define DAVINCI_AINTC_FIQ_REG1 0x04 | |
19 | #define DAVINCI_AINTC_IRQ_REG0 0x08 | |
20 | #define DAVINCI_AINTC_IRQ_REG1 0x0c | |
21 | #define DAVINCI_AINTC_IRQ_IRQENTRY 0x14 | |
22 | #define DAVINCI_AINTC_IRQ_ENT_REG0 0x18 | |
23 | #define DAVINCI_AINTC_IRQ_ENT_REG1 0x1c | |
24 | #define DAVINCI_AINTC_IRQ_INCTL_REG 0x20 | |
25 | #define DAVINCI_AINTC_IRQ_EABASE_REG 0x24 | |
26 | #define DAVINCI_AINTC_IRQ_INTPRI0_REG 0x30 | |
27 | #define DAVINCI_AINTC_IRQ_INTPRI7_REG 0x4c | |
2b6a2e74 BG |
28 | |
29 | static void __iomem *davinci_aintc_base; | |
30 | static struct irq_domain *davinci_aintc_irq_domain; | |
31 | ||
32 | static inline void davinci_aintc_writel(unsigned long value, int offset) | |
7c6337e2 | 33 | { |
f412384e | 34 | writel_relaxed(value, davinci_aintc_base + offset); |
7c6337e2 KH |
35 | } |
36 | ||
2b6a2e74 | 37 | static inline unsigned long davinci_aintc_readl(int offset) |
d0064594 | 38 | { |
2b6a2e74 | 39 | return readl_relaxed(davinci_aintc_base + offset); |
d0064594 BG |
40 | } |
41 | ||
aac4dd1d | 42 | static __init void |
2b6a2e74 BG |
43 | davinci_aintc_setup_gc(void __iomem *base, |
44 | unsigned int irq_start, unsigned int num) | |
7c6337e2 | 45 | { |
aac4dd1d TG |
46 | struct irq_chip_generic *gc; |
47 | struct irq_chip_type *ct; | |
48 | ||
2b6a2e74 | 49 | gc = irq_get_domain_generic_chip(davinci_aintc_irq_domain, irq_start); |
74b0eac2 BG |
50 | gc->reg_base = base; |
51 | gc->irq_base = irq_start; | |
33e1e5e3 | 52 | |
aac4dd1d | 53 | ct = gc->chip_types; |
659fb32d | 54 | ct->chip.irq_ack = irq_gc_ack_set_bit; |
aac4dd1d TG |
55 | ct->chip.irq_mask = irq_gc_mask_clr_bit; |
56 | ct->chip.irq_unmask = irq_gc_mask_set_bit; | |
57 | ||
2b6a2e74 BG |
58 | ct->regs.ack = DAVINCI_AINTC_IRQ_REG0; |
59 | ct->regs.mask = DAVINCI_AINTC_IRQ_ENT_REG0; | |
aac4dd1d TG |
60 | irq_setup_generic_chip(gc, IRQ_MSK(num), IRQ_GC_INIT_MASK_CACHE, |
61 | IRQ_NOREQUEST | IRQ_NOPROBE, 0); | |
7c6337e2 KH |
62 | } |
63 | ||
d0064594 | 64 | static asmlinkage void __exception_irq_entry |
2b6a2e74 | 65 | davinci_aintc_handle_irq(struct pt_regs *regs) |
d0064594 | 66 | { |
2b6a2e74 | 67 | int irqnr = davinci_aintc_readl(DAVINCI_AINTC_IRQ_IRQENTRY); |
d0064594 BG |
68 | |
69 | /* | |
70 | * Use the formula for entry vector index generation from section | |
71 | * 8.3.3 of the manual. | |
72 | */ | |
73 | irqnr >>= 2; | |
74 | irqnr -= 1; | |
75 | ||
2b6a2e74 | 76 | handle_domain_irq(davinci_aintc_irq_domain, irqnr, regs); |
d0064594 BG |
77 | } |
78 | ||
7c6337e2 | 79 | /* ARM Interrupt Controller Initialization */ |
06a28716 | 80 | void __init davinci_aintc_init(const struct davinci_aintc_config *config) |
7c6337e2 | 81 | { |
06a28716 | 82 | unsigned int irq_off, reg_off, prio, shift; |
882bed72 | 83 | void __iomem *req; |
74b0eac2 | 84 | int ret, irq_base; |
06a28716 | 85 | const u8 *prios; |
7c6337e2 | 86 | |
882bed72 BG |
87 | req = request_mem_region(config->reg.start, |
88 | resource_size(&config->reg), | |
89 | "davinci-cp-intc"); | |
90 | if (!req) { | |
91 | pr_err("%s: register range busy\n", __func__); | |
92 | return; | |
93 | } | |
94 | ||
06a28716 BG |
95 | davinci_aintc_base = ioremap(config->reg.start, |
96 | resource_size(&config->reg)); | |
a6c0bba1 BG |
97 | if (!davinci_aintc_base) { |
98 | pr_err("%s: unable to ioremap register range\n", __func__); | |
bd808947 | 99 | return; |
a6c0bba1 | 100 | } |
bd808947 | 101 | |
7c6337e2 | 102 | /* Clear all interrupt requests */ |
2b6a2e74 BG |
103 | davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG0); |
104 | davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG1); | |
105 | davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG0); | |
106 | davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG1); | |
7c6337e2 KH |
107 | |
108 | /* Disable all interrupts */ | |
2b6a2e74 BG |
109 | davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_ENT_REG0); |
110 | davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_ENT_REG1); | |
7c6337e2 KH |
111 | |
112 | /* Interrupts disabled immediately, IRQ entry reflects all */ | |
2b6a2e74 | 113 | davinci_aintc_writel(0x0, DAVINCI_AINTC_IRQ_INCTL_REG); |
7c6337e2 KH |
114 | |
115 | /* we don't use the hardware vector table, just its entry addresses */ | |
2b6a2e74 | 116 | davinci_aintc_writel(0, DAVINCI_AINTC_IRQ_EABASE_REG); |
7c6337e2 KH |
117 | |
118 | /* Clear all interrupt requests */ | |
2b6a2e74 BG |
119 | davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG0); |
120 | davinci_aintc_writel(~0x0, DAVINCI_AINTC_FIQ_REG1); | |
121 | davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG0); | |
122 | davinci_aintc_writel(~0x0, DAVINCI_AINTC_IRQ_REG1); | |
7c6337e2 | 123 | |
06a28716 BG |
124 | prios = config->prios; |
125 | for (reg_off = DAVINCI_AINTC_IRQ_INTPRI0_REG; | |
126 | reg_off <= DAVINCI_AINTC_IRQ_INTPRI7_REG; reg_off += 4) { | |
127 | for (shift = 0, prio = 0; shift < 32; shift += 4, prios++) | |
128 | prio |= (*prios & 0x07) << shift; | |
129 | davinci_aintc_writel(prio, reg_off); | |
7c6337e2 KH |
130 | } |
131 | ||
06a28716 | 132 | irq_base = irq_alloc_descs(-1, 0, config->num_irqs, 0); |
a6c0bba1 BG |
133 | if (irq_base < 0) { |
134 | pr_err("%s: unable to allocate interrupt descriptors: %d\n", | |
135 | __func__, irq_base); | |
74b0eac2 | 136 | return; |
a6c0bba1 | 137 | } |
74b0eac2 | 138 | |
2b6a2e74 | 139 | davinci_aintc_irq_domain = irq_domain_add_legacy(NULL, |
06a28716 BG |
140 | config->num_irqs, irq_base, 0, |
141 | &irq_domain_simple_ops, NULL); | |
a6c0bba1 BG |
142 | if (!davinci_aintc_irq_domain) { |
143 | pr_err("%s: unable to create interrupt domain\n", __func__); | |
74b0eac2 | 144 | return; |
a6c0bba1 | 145 | } |
74b0eac2 | 146 | |
2b6a2e74 BG |
147 | ret = irq_alloc_domain_generic_chips(davinci_aintc_irq_domain, 32, 1, |
148 | "AINTC", handle_edge_irq, | |
149 | IRQ_NOREQUEST | IRQ_NOPROBE, 0, 0); | |
a6c0bba1 BG |
150 | if (ret) { |
151 | pr_err("%s: unable to allocate generic irq chips for domain\n", | |
152 | __func__); | |
74b0eac2 | 153 | return; |
a6c0bba1 | 154 | } |
74b0eac2 | 155 | |
06a28716 BG |
156 | for (irq_off = 0, reg_off = 0; |
157 | irq_off < config->num_irqs; | |
158 | irq_off += 32, reg_off += 0x04) | |
159 | davinci_aintc_setup_gc(davinci_aintc_base + reg_off, | |
160 | irq_base + irq_off, 32); | |
aac4dd1d | 161 | |
2b6a2e74 | 162 | set_handle_irq(davinci_aintc_handle_irq); |
7c6337e2 | 163 | } |