]>
Commit | Line | Data |
---|---|---|
d4a67d9d GJ |
1 | /* |
2 | * Atheros AR71xx/AR724x/AR913x specific interrupt handling | |
3 | * | |
fce5cc6e | 4 | * Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com> |
4dbcbdf8 | 5 | * Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org> |
d4a67d9d GJ |
6 | * Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org> |
7 | * | |
fce5cc6e | 8 | * Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP |
d4a67d9d GJ |
9 | * |
10 | * This program is free software; you can redistribute it and/or modify it | |
11 | * under the terms of the GNU General Public License version 2 as published | |
12 | * by the Free Software Foundation. | |
13 | */ | |
14 | ||
15 | #include <linux/kernel.h> | |
16 | #include <linux/init.h> | |
17 | #include <linux/interrupt.h> | |
b29e8b87 AB |
18 | #include <linux/irqchip.h> |
19 | #include <linux/of_irq.h> | |
d4a67d9d GJ |
20 | |
21 | #include <asm/irq_cpu.h> | |
22 | #include <asm/mipsregs.h> | |
23 | ||
24 | #include <asm/mach-ath79/ath79.h> | |
25 | #include <asm/mach-ath79/ar71xx_regs.h> | |
26 | #include "common.h" | |
b29e8b87 | 27 | #include "machtypes.h" |
d4a67d9d | 28 | |
bd0b9ac4 | 29 | static void ath79_misc_irq_handler(struct irq_desc *desc) |
d4a67d9d GJ |
30 | { |
31 | void __iomem *base = ath79_reset_base; | |
32 | u32 pending; | |
33 | ||
34 | pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) & | |
35 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
36 | ||
9c099c4e GJ |
37 | if (!pending) { |
38 | spurious_interrupt(); | |
39 | return; | |
40 | } | |
d4a67d9d | 41 | |
9c099c4e GJ |
42 | while (pending) { |
43 | int bit = __ffs(pending); | |
d2b4ac1e | 44 | |
9c099c4e GJ |
45 | generic_handle_irq(ATH79_MISC_IRQ(bit)); |
46 | pending &= ~BIT(bit); | |
47 | } | |
d4a67d9d GJ |
48 | } |
49 | ||
3fb8818b | 50 | static void ar71xx_misc_irq_unmask(struct irq_data *d) |
d4a67d9d | 51 | { |
3fb8818b | 52 | unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE; |
d4a67d9d GJ |
53 | void __iomem *base = ath79_reset_base; |
54 | u32 t; | |
55 | ||
d4a67d9d GJ |
56 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
57 | __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
58 | ||
59 | /* flush write */ | |
60 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
61 | } | |
62 | ||
3fb8818b | 63 | static void ar71xx_misc_irq_mask(struct irq_data *d) |
d4a67d9d | 64 | { |
3fb8818b | 65 | unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE; |
d4a67d9d GJ |
66 | void __iomem *base = ath79_reset_base; |
67 | u32 t; | |
68 | ||
d4a67d9d GJ |
69 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
70 | __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
71 | ||
72 | /* flush write */ | |
73 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
74 | } | |
75 | ||
3fb8818b | 76 | static void ar724x_misc_irq_ack(struct irq_data *d) |
d4a67d9d | 77 | { |
3fb8818b | 78 | unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE; |
d4a67d9d GJ |
79 | void __iomem *base = ath79_reset_base; |
80 | u32 t; | |
81 | ||
d4a67d9d GJ |
82 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); |
83 | __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS); | |
84 | ||
85 | /* flush write */ | |
86 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); | |
87 | } | |
88 | ||
89 | static struct irq_chip ath79_misc_irq_chip = { | |
90 | .name = "MISC", | |
3fb8818b TG |
91 | .irq_unmask = ar71xx_misc_irq_unmask, |
92 | .irq_mask = ar71xx_misc_irq_mask, | |
d4a67d9d GJ |
93 | }; |
94 | ||
95 | static void __init ath79_misc_irq_init(void) | |
96 | { | |
97 | void __iomem *base = ath79_reset_base; | |
98 | int i; | |
99 | ||
100 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
101 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS); | |
102 | ||
103 | if (soc_is_ar71xx() || soc_is_ar913x()) | |
3fb8818b | 104 | ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; |
53330332 GJ |
105 | else if (soc_is_ar724x() || |
106 | soc_is_ar933x() || | |
107 | soc_is_ar934x() || | |
108 | soc_is_qca955x()) | |
3fb8818b | 109 | ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; |
d4a67d9d GJ |
110 | else |
111 | BUG(); | |
112 | ||
113 | for (i = ATH79_MISC_IRQ_BASE; | |
114 | i < ATH79_MISC_IRQ_BASE + ATH79_MISC_IRQ_COUNT; i++) { | |
e4ec7989 | 115 | irq_set_chip_and_handler(i, &ath79_misc_irq_chip, |
d4a67d9d GJ |
116 | handle_level_irq); |
117 | } | |
118 | ||
7e69c10a | 119 | irq_set_chained_handler(ATH79_CPU_IRQ(6), ath79_misc_irq_handler); |
d4a67d9d GJ |
120 | } |
121 | ||
bd0b9ac4 | 122 | static void ar934x_ip2_irq_dispatch(struct irq_desc *desc) |
fce5cc6e GJ |
123 | { |
124 | u32 status; | |
125 | ||
fce5cc6e GJ |
126 | status = ath79_reset_rr(AR934X_RESET_REG_PCIE_WMAC_INT_STATUS); |
127 | ||
128 | if (status & AR934X_PCIE_WMAC_INT_PCIE_ALL) { | |
24b0e3e8 | 129 | ath79_ddr_wb_flush(3); |
fce5cc6e GJ |
130 | generic_handle_irq(ATH79_IP2_IRQ(0)); |
131 | } else if (status & AR934X_PCIE_WMAC_INT_WMAC_ALL) { | |
24b0e3e8 | 132 | ath79_ddr_wb_flush(4); |
fce5cc6e GJ |
133 | generic_handle_irq(ATH79_IP2_IRQ(1)); |
134 | } else { | |
135 | spurious_interrupt(); | |
136 | } | |
fce5cc6e GJ |
137 | } |
138 | ||
139 | static void ar934x_ip2_irq_init(void) | |
140 | { | |
141 | int i; | |
142 | ||
143 | for (i = ATH79_IP2_IRQ_BASE; | |
144 | i < ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT; i++) | |
145 | irq_set_chip_and_handler(i, &dummy_irq_chip, | |
146 | handle_level_irq); | |
147 | ||
7e69c10a | 148 | irq_set_chained_handler(ATH79_CPU_IRQ(2), ar934x_ip2_irq_dispatch); |
fce5cc6e GJ |
149 | } |
150 | ||
bd0b9ac4 | 151 | static void qca955x_ip2_irq_dispatch(struct irq_desc *desc) |
53330332 GJ |
152 | { |
153 | u32 status; | |
154 | ||
53330332 GJ |
155 | status = ath79_reset_rr(QCA955X_RESET_REG_EXT_INT_STATUS); |
156 | status &= QCA955X_EXT_INT_PCIE_RC1_ALL | QCA955X_EXT_INT_WMAC_ALL; | |
157 | ||
158 | if (status == 0) { | |
159 | spurious_interrupt(); | |
9d9a2fa7 | 160 | return; |
53330332 GJ |
161 | } |
162 | ||
163 | if (status & QCA955X_EXT_INT_PCIE_RC1_ALL) { | |
164 | /* TODO: flush DDR? */ | |
165 | generic_handle_irq(ATH79_IP2_IRQ(0)); | |
166 | } | |
167 | ||
168 | if (status & QCA955X_EXT_INT_WMAC_ALL) { | |
169 | /* TODO: flush DDR? */ | |
170 | generic_handle_irq(ATH79_IP2_IRQ(1)); | |
171 | } | |
53330332 GJ |
172 | } |
173 | ||
bd0b9ac4 | 174 | static void qca955x_ip3_irq_dispatch(struct irq_desc *desc) |
53330332 GJ |
175 | { |
176 | u32 status; | |
177 | ||
53330332 GJ |
178 | status = ath79_reset_rr(QCA955X_RESET_REG_EXT_INT_STATUS); |
179 | status &= QCA955X_EXT_INT_PCIE_RC2_ALL | | |
180 | QCA955X_EXT_INT_USB1 | | |
181 | QCA955X_EXT_INT_USB2; | |
182 | ||
183 | if (status == 0) { | |
184 | spurious_interrupt(); | |
9d9a2fa7 | 185 | return; |
53330332 GJ |
186 | } |
187 | ||
188 | if (status & QCA955X_EXT_INT_USB1) { | |
189 | /* TODO: flush DDR? */ | |
190 | generic_handle_irq(ATH79_IP3_IRQ(0)); | |
191 | } | |
192 | ||
193 | if (status & QCA955X_EXT_INT_USB2) { | |
194 | /* TODO: flush DDR? */ | |
195 | generic_handle_irq(ATH79_IP3_IRQ(1)); | |
196 | } | |
197 | ||
198 | if (status & QCA955X_EXT_INT_PCIE_RC2_ALL) { | |
199 | /* TODO: flush DDR? */ | |
200 | generic_handle_irq(ATH79_IP3_IRQ(2)); | |
201 | } | |
53330332 GJ |
202 | } |
203 | ||
204 | static void qca955x_irq_init(void) | |
205 | { | |
206 | int i; | |
207 | ||
208 | for (i = ATH79_IP2_IRQ_BASE; | |
209 | i < ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT; i++) | |
210 | irq_set_chip_and_handler(i, &dummy_irq_chip, | |
211 | handle_level_irq); | |
212 | ||
213 | irq_set_chained_handler(ATH79_CPU_IRQ(2), qca955x_ip2_irq_dispatch); | |
214 | ||
215 | for (i = ATH79_IP3_IRQ_BASE; | |
216 | i < ATH79_IP3_IRQ_BASE + ATH79_IP3_IRQ_COUNT; i++) | |
217 | irq_set_chip_and_handler(i, &dummy_irq_chip, | |
218 | handle_level_irq); | |
219 | ||
220 | irq_set_chained_handler(ATH79_CPU_IRQ(3), qca955x_ip3_irq_dispatch); | |
221 | } | |
222 | ||
4dbcbdf8 GJ |
223 | /* |
224 | * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for | |
225 | * these devices typically allocate coherent DMA memory, however the | |
226 | * DMA controller may still have some unsynchronized data in the FIFO. | |
227 | * Issue a flush in the handlers to ensure that the driver sees | |
228 | * the update. | |
24b0e3e8 AB |
229 | * |
230 | * This array map the interrupt lines to the DDR write buffer channels. | |
4dbcbdf8 | 231 | */ |
53330332 | 232 | |
24b0e3e8 AB |
233 | static unsigned irq_wb_chan[8] = { |
234 | -1, -1, -1, -1, -1, -1, -1, -1, | |
235 | }; | |
4dbcbdf8 | 236 | |
24b0e3e8 | 237 | asmlinkage void plat_irq_dispatch(void) |
4dbcbdf8 | 238 | { |
24b0e3e8 AB |
239 | unsigned long pending; |
240 | int irq; | |
4dbcbdf8 | 241 | |
24b0e3e8 | 242 | pending = read_c0_status() & read_c0_cause() & ST0_IM; |
4dbcbdf8 | 243 | |
24b0e3e8 AB |
244 | if (!pending) { |
245 | spurious_interrupt(); | |
246 | return; | |
247 | } | |
4dbcbdf8 | 248 | |
24b0e3e8 AB |
249 | pending >>= CAUSEB_IP; |
250 | while (pending) { | |
251 | irq = fls(pending) - 1; | |
252 | if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1) | |
253 | ath79_ddr_wb_flush(irq_wb_chan[irq]); | |
254 | do_IRQ(MIPS_CPU_IRQ_BASE + irq); | |
255 | pending &= ~BIT(irq); | |
256 | } | |
fce5cc6e GJ |
257 | } |
258 | ||
b29e8b87 AB |
259 | #ifdef CONFIG_IRQCHIP |
260 | static int misc_map(struct irq_domain *d, unsigned int irq, irq_hw_number_t hw) | |
261 | { | |
262 | irq_set_chip_and_handler(irq, &ath79_misc_irq_chip, handle_level_irq); | |
263 | return 0; | |
264 | } | |
265 | ||
266 | static const struct irq_domain_ops misc_irq_domain_ops = { | |
267 | .xlate = irq_domain_xlate_onecell, | |
268 | .map = misc_map, | |
269 | }; | |
270 | ||
271 | static int __init ath79_misc_intc_of_init( | |
272 | struct device_node *node, struct device_node *parent) | |
273 | { | |
274 | void __iomem *base = ath79_reset_base; | |
275 | struct irq_domain *domain; | |
276 | int irq; | |
277 | ||
278 | irq = irq_of_parse_and_map(node, 0); | |
279 | if (!irq) | |
280 | panic("Failed to get MISC IRQ"); | |
281 | ||
282 | domain = irq_domain_add_legacy(node, ATH79_MISC_IRQ_COUNT, | |
283 | ATH79_MISC_IRQ_BASE, 0, &misc_irq_domain_ops, NULL); | |
284 | if (!domain) | |
285 | panic("Failed to add MISC irqdomain"); | |
286 | ||
287 | /* Disable and clear all interrupts */ | |
288 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
289 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS); | |
290 | ||
291 | ||
292 | irq_set_chained_handler(irq, ath79_misc_irq_handler); | |
293 | ||
294 | return 0; | |
295 | } | |
296 | IRQCHIP_DECLARE(ath79_misc_intc, "qca,ar7100-misc-intc", | |
297 | ath79_misc_intc_of_init); | |
298 | ||
299 | static int __init ar79_cpu_intc_of_init( | |
300 | struct device_node *node, struct device_node *parent) | |
301 | { | |
302 | int err, i, count; | |
303 | ||
304 | /* Fill the irq_wb_chan table */ | |
305 | count = of_count_phandle_with_args( | |
306 | node, "qca,ddr-wb-channels", "#qca,ddr-wb-channel-cells"); | |
307 | ||
308 | for (i = 0; i < count; i++) { | |
309 | struct of_phandle_args args; | |
310 | u32 irq = i; | |
311 | ||
312 | of_property_read_u32_index( | |
313 | node, "qca,ddr-wb-channel-interrupts", i, &irq); | |
314 | if (irq >= ARRAY_SIZE(irq_wb_chan)) | |
315 | continue; | |
316 | ||
317 | err = of_parse_phandle_with_args( | |
318 | node, "qca,ddr-wb-channels", | |
319 | "#qca,ddr-wb-channel-cells", | |
320 | i, &args); | |
321 | if (err) | |
322 | return err; | |
323 | ||
324 | irq_wb_chan[irq] = args.args[0]; | |
325 | pr_info("IRQ: Set flush channel of IRQ%d to %d\n", | |
326 | irq, args.args[0]); | |
327 | } | |
328 | ||
329 | return mips_cpu_irq_of_init(node, parent); | |
330 | } | |
331 | IRQCHIP_DECLARE(ar79_cpu_intc, "qca,ar7100-cpu-intc", | |
332 | ar79_cpu_intc_of_init); | |
333 | ||
334 | #endif | |
335 | ||
d4a67d9d GJ |
336 | void __init arch_init_irq(void) |
337 | { | |
b29e8b87 AB |
338 | if (mips_machtype == ATH79_MACH_GENERIC_OF) { |
339 | irqchip_init(); | |
340 | return; | |
341 | } | |
342 | ||
24b0e3e8 AB |
343 | if (soc_is_ar71xx() || soc_is_ar724x() || |
344 | soc_is_ar913x() || soc_is_ar933x()) { | |
345 | irq_wb_chan[2] = 3; | |
346 | irq_wb_chan[3] = 2; | |
fce5cc6e | 347 | } else if (soc_is_ar934x()) { |
24b0e3e8 | 348 | irq_wb_chan[3] = 2; |
4dbcbdf8 | 349 | } |
d4a67d9d | 350 | |
d4a67d9d GJ |
351 | mips_cpu_irq_init(); |
352 | ath79_misc_irq_init(); | |
fce5cc6e GJ |
353 | |
354 | if (soc_is_ar934x()) | |
355 | ar934x_ip2_irq_init(); | |
53330332 GJ |
356 | else if (soc_is_qca955x()) |
357 | qca955x_irq_init(); | |
d4a67d9d | 358 | } |