]>
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> | |
18 | #include <linux/irq.h> | |
19 | ||
20 | #include <asm/irq_cpu.h> | |
21 | #include <asm/mipsregs.h> | |
22 | ||
23 | #include <asm/mach-ath79/ath79.h> | |
24 | #include <asm/mach-ath79/ar71xx_regs.h> | |
25 | #include "common.h" | |
26 | ||
4dbcbdf8 GJ |
27 | static void (*ath79_ip2_handler)(void); |
28 | static void (*ath79_ip3_handler)(void); | |
d4a67d9d GJ |
29 | |
30 | static void ath79_misc_irq_handler(unsigned int irq, struct irq_desc *desc) | |
31 | { | |
32 | void __iomem *base = ath79_reset_base; | |
33 | u32 pending; | |
34 | ||
35 | pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) & | |
36 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
37 | ||
38 | if (pending & MISC_INT_UART) | |
39 | generic_handle_irq(ATH79_MISC_IRQ_UART); | |
40 | ||
41 | else if (pending & MISC_INT_DMA) | |
42 | generic_handle_irq(ATH79_MISC_IRQ_DMA); | |
43 | ||
44 | else if (pending & MISC_INT_PERFC) | |
45 | generic_handle_irq(ATH79_MISC_IRQ_PERFC); | |
46 | ||
47 | else if (pending & MISC_INT_TIMER) | |
48 | generic_handle_irq(ATH79_MISC_IRQ_TIMER); | |
49 | ||
d2b4ac1e GJ |
50 | else if (pending & MISC_INT_TIMER2) |
51 | generic_handle_irq(ATH79_MISC_IRQ_TIMER2); | |
52 | ||
53 | else if (pending & MISC_INT_TIMER3) | |
54 | generic_handle_irq(ATH79_MISC_IRQ_TIMER3); | |
55 | ||
56 | else if (pending & MISC_INT_TIMER4) | |
57 | generic_handle_irq(ATH79_MISC_IRQ_TIMER4); | |
58 | ||
d4a67d9d GJ |
59 | else if (pending & MISC_INT_OHCI) |
60 | generic_handle_irq(ATH79_MISC_IRQ_OHCI); | |
61 | ||
62 | else if (pending & MISC_INT_ERROR) | |
63 | generic_handle_irq(ATH79_MISC_IRQ_ERROR); | |
64 | ||
65 | else if (pending & MISC_INT_GPIO) | |
66 | generic_handle_irq(ATH79_MISC_IRQ_GPIO); | |
67 | ||
68 | else if (pending & MISC_INT_WDOG) | |
69 | generic_handle_irq(ATH79_MISC_IRQ_WDOG); | |
70 | ||
d2b4ac1e GJ |
71 | else if (pending & MISC_INT_ETHSW) |
72 | generic_handle_irq(ATH79_MISC_IRQ_ETHSW); | |
73 | ||
d4a67d9d GJ |
74 | else |
75 | spurious_interrupt(); | |
76 | } | |
77 | ||
3fb8818b | 78 | static void ar71xx_misc_irq_unmask(struct irq_data *d) |
d4a67d9d | 79 | { |
3fb8818b | 80 | unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE; |
d4a67d9d GJ |
81 | void __iomem *base = ath79_reset_base; |
82 | u32 t; | |
83 | ||
d4a67d9d GJ |
84 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
85 | __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
86 | ||
87 | /* flush write */ | |
88 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
89 | } | |
90 | ||
3fb8818b | 91 | static void ar71xx_misc_irq_mask(struct irq_data *d) |
d4a67d9d | 92 | { |
3fb8818b | 93 | unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE; |
d4a67d9d GJ |
94 | void __iomem *base = ath79_reset_base; |
95 | u32 t; | |
96 | ||
d4a67d9d GJ |
97 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); |
98 | __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
99 | ||
100 | /* flush write */ | |
101 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
102 | } | |
103 | ||
3fb8818b | 104 | static void ar724x_misc_irq_ack(struct irq_data *d) |
d4a67d9d | 105 | { |
3fb8818b | 106 | unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE; |
d4a67d9d GJ |
107 | void __iomem *base = ath79_reset_base; |
108 | u32 t; | |
109 | ||
d4a67d9d GJ |
110 | t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); |
111 | __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS); | |
112 | ||
113 | /* flush write */ | |
114 | __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS); | |
115 | } | |
116 | ||
117 | static struct irq_chip ath79_misc_irq_chip = { | |
118 | .name = "MISC", | |
3fb8818b TG |
119 | .irq_unmask = ar71xx_misc_irq_unmask, |
120 | .irq_mask = ar71xx_misc_irq_mask, | |
d4a67d9d GJ |
121 | }; |
122 | ||
123 | static void __init ath79_misc_irq_init(void) | |
124 | { | |
125 | void __iomem *base = ath79_reset_base; | |
126 | int i; | |
127 | ||
128 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE); | |
129 | __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS); | |
130 | ||
131 | if (soc_is_ar71xx() || soc_is_ar913x()) | |
3fb8818b | 132 | ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask; |
fce5cc6e | 133 | else if (soc_is_ar724x() || soc_is_ar933x() || soc_is_ar934x()) |
3fb8818b | 134 | ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack; |
d4a67d9d GJ |
135 | else |
136 | BUG(); | |
137 | ||
138 | for (i = ATH79_MISC_IRQ_BASE; | |
139 | i < ATH79_MISC_IRQ_BASE + ATH79_MISC_IRQ_COUNT; i++) { | |
e4ec7989 | 140 | irq_set_chip_and_handler(i, &ath79_misc_irq_chip, |
d4a67d9d GJ |
141 | handle_level_irq); |
142 | } | |
143 | ||
e4ec7989 | 144 | irq_set_chained_handler(ATH79_CPU_IRQ_MISC, ath79_misc_irq_handler); |
d4a67d9d GJ |
145 | } |
146 | ||
fce5cc6e GJ |
147 | static void ar934x_ip2_irq_dispatch(unsigned int irq, struct irq_desc *desc) |
148 | { | |
149 | u32 status; | |
150 | ||
151 | disable_irq_nosync(irq); | |
152 | ||
153 | status = ath79_reset_rr(AR934X_RESET_REG_PCIE_WMAC_INT_STATUS); | |
154 | ||
155 | if (status & AR934X_PCIE_WMAC_INT_PCIE_ALL) { | |
156 | ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_PCIE); | |
157 | generic_handle_irq(ATH79_IP2_IRQ(0)); | |
158 | } else if (status & AR934X_PCIE_WMAC_INT_WMAC_ALL) { | |
159 | ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_WMAC); | |
160 | generic_handle_irq(ATH79_IP2_IRQ(1)); | |
161 | } else { | |
162 | spurious_interrupt(); | |
163 | } | |
164 | ||
165 | enable_irq(irq); | |
166 | } | |
167 | ||
168 | static void ar934x_ip2_irq_init(void) | |
169 | { | |
170 | int i; | |
171 | ||
172 | for (i = ATH79_IP2_IRQ_BASE; | |
173 | i < ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT; i++) | |
174 | irq_set_chip_and_handler(i, &dummy_irq_chip, | |
175 | handle_level_irq); | |
176 | ||
177 | irq_set_chained_handler(ATH79_CPU_IRQ_IP2, ar934x_ip2_irq_dispatch); | |
178 | } | |
179 | ||
d4a67d9d GJ |
180 | asmlinkage void plat_irq_dispatch(void) |
181 | { | |
182 | unsigned long pending; | |
183 | ||
184 | pending = read_c0_status() & read_c0_cause() & ST0_IM; | |
185 | ||
186 | if (pending & STATUSF_IP7) | |
187 | do_IRQ(ATH79_CPU_IRQ_TIMER); | |
188 | ||
4dbcbdf8 GJ |
189 | else if (pending & STATUSF_IP2) |
190 | ath79_ip2_handler(); | |
d4a67d9d GJ |
191 | |
192 | else if (pending & STATUSF_IP4) | |
193 | do_IRQ(ATH79_CPU_IRQ_GE0); | |
194 | ||
195 | else if (pending & STATUSF_IP5) | |
196 | do_IRQ(ATH79_CPU_IRQ_GE1); | |
197 | ||
4dbcbdf8 GJ |
198 | else if (pending & STATUSF_IP3) |
199 | ath79_ip3_handler(); | |
d4a67d9d GJ |
200 | |
201 | else if (pending & STATUSF_IP6) | |
202 | do_IRQ(ATH79_CPU_IRQ_MISC); | |
203 | ||
204 | else | |
205 | spurious_interrupt(); | |
206 | } | |
207 | ||
4dbcbdf8 GJ |
208 | /* |
209 | * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for | |
210 | * these devices typically allocate coherent DMA memory, however the | |
211 | * DMA controller may still have some unsynchronized data in the FIFO. | |
212 | * Issue a flush in the handlers to ensure that the driver sees | |
213 | * the update. | |
214 | */ | |
215 | static void ar71xx_ip2_handler(void) | |
216 | { | |
217 | ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_PCI); | |
218 | do_IRQ(ATH79_CPU_IRQ_IP2); | |
219 | } | |
220 | ||
221 | static void ar724x_ip2_handler(void) | |
222 | { | |
223 | ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_PCIE); | |
224 | do_IRQ(ATH79_CPU_IRQ_IP2); | |
225 | } | |
226 | ||
227 | static void ar913x_ip2_handler(void) | |
228 | { | |
229 | ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_WMAC); | |
230 | do_IRQ(ATH79_CPU_IRQ_IP2); | |
231 | } | |
232 | ||
233 | static void ar933x_ip2_handler(void) | |
234 | { | |
235 | ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_WMAC); | |
236 | do_IRQ(ATH79_CPU_IRQ_IP2); | |
237 | } | |
238 | ||
fce5cc6e GJ |
239 | static void ar934x_ip2_handler(void) |
240 | { | |
241 | do_IRQ(ATH79_CPU_IRQ_IP2); | |
242 | } | |
243 | ||
4dbcbdf8 GJ |
244 | static void ar71xx_ip3_handler(void) |
245 | { | |
246 | ath79_ddr_wb_flush(AR71XX_DDR_REG_FLUSH_USB); | |
247 | do_IRQ(ATH79_CPU_IRQ_USB); | |
248 | } | |
249 | ||
250 | static void ar724x_ip3_handler(void) | |
251 | { | |
252 | ath79_ddr_wb_flush(AR724X_DDR_REG_FLUSH_USB); | |
253 | do_IRQ(ATH79_CPU_IRQ_USB); | |
254 | } | |
255 | ||
256 | static void ar913x_ip3_handler(void) | |
257 | { | |
258 | ath79_ddr_wb_flush(AR913X_DDR_REG_FLUSH_USB); | |
259 | do_IRQ(ATH79_CPU_IRQ_USB); | |
260 | } | |
261 | ||
262 | static void ar933x_ip3_handler(void) | |
263 | { | |
264 | ath79_ddr_wb_flush(AR933X_DDR_REG_FLUSH_USB); | |
265 | do_IRQ(ATH79_CPU_IRQ_USB); | |
266 | } | |
267 | ||
fce5cc6e GJ |
268 | static void ar934x_ip3_handler(void) |
269 | { | |
270 | ath79_ddr_wb_flush(AR934X_DDR_REG_FLUSH_USB); | |
271 | do_IRQ(ATH79_CPU_IRQ_USB); | |
272 | } | |
273 | ||
d4a67d9d GJ |
274 | void __init arch_init_irq(void) |
275 | { | |
276 | if (soc_is_ar71xx()) { | |
4dbcbdf8 GJ |
277 | ath79_ip2_handler = ar71xx_ip2_handler; |
278 | ath79_ip3_handler = ar71xx_ip3_handler; | |
d4a67d9d | 279 | } else if (soc_is_ar724x()) { |
4dbcbdf8 GJ |
280 | ath79_ip2_handler = ar724x_ip2_handler; |
281 | ath79_ip3_handler = ar724x_ip3_handler; | |
d4a67d9d | 282 | } else if (soc_is_ar913x()) { |
4dbcbdf8 GJ |
283 | ath79_ip2_handler = ar913x_ip2_handler; |
284 | ath79_ip3_handler = ar913x_ip3_handler; | |
54eed4c7 | 285 | } else if (soc_is_ar933x()) { |
4dbcbdf8 GJ |
286 | ath79_ip2_handler = ar933x_ip2_handler; |
287 | ath79_ip3_handler = ar933x_ip3_handler; | |
fce5cc6e GJ |
288 | } else if (soc_is_ar934x()) { |
289 | ath79_ip2_handler = ar934x_ip2_handler; | |
290 | ath79_ip3_handler = ar934x_ip3_handler; | |
4dbcbdf8 | 291 | } else { |
d4a67d9d | 292 | BUG(); |
4dbcbdf8 | 293 | } |
d4a67d9d GJ |
294 | |
295 | cp0_perfcount_irq = ATH79_MISC_IRQ_PERFC; | |
296 | mips_cpu_irq_init(); | |
297 | ath79_misc_irq_init(); | |
fce5cc6e GJ |
298 | |
299 | if (soc_is_ar934x()) | |
300 | ar934x_ip2_irq_init(); | |
d4a67d9d | 301 | } |