MIPS: ath79: Improve the DDR controller interface
[linux-2.6-microblaze.git] / arch / mips / ath79 / irq.c
1 /*
2  *  Atheros AR71xx/AR724x/AR913x specific interrupt handling
3  *
4  *  Copyright (C) 2010-2011 Jaiganesh Narayanan <jnarayanan@atheros.com>
5  *  Copyright (C) 2008-2011 Gabor Juhos <juhosg@openwrt.org>
6  *  Copyright (C) 2008 Imre Kaloz <kaloz@openwrt.org>
7  *
8  *  Parts of this file are based on Atheros' 2.6.15/2.6.31 BSP
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
27 static void ath79_misc_irq_handler(unsigned int irq, struct irq_desc *desc)
28 {
29         void __iomem *base = ath79_reset_base;
30         u32 pending;
31
32         pending = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS) &
33                   __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
34
35         if (!pending) {
36                 spurious_interrupt();
37                 return;
38         }
39
40         while (pending) {
41                 int bit = __ffs(pending);
42
43                 generic_handle_irq(ATH79_MISC_IRQ(bit));
44                 pending &= ~BIT(bit);
45         }
46 }
47
48 static void ar71xx_misc_irq_unmask(struct irq_data *d)
49 {
50         unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE;
51         void __iomem *base = ath79_reset_base;
52         u32 t;
53
54         t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
55         __raw_writel(t | (1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
56
57         /* flush write */
58         __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
59 }
60
61 static void ar71xx_misc_irq_mask(struct irq_data *d)
62 {
63         unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE;
64         void __iomem *base = ath79_reset_base;
65         u32 t;
66
67         t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
68         __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_ENABLE);
69
70         /* flush write */
71         __raw_readl(base + AR71XX_RESET_REG_MISC_INT_ENABLE);
72 }
73
74 static void ar724x_misc_irq_ack(struct irq_data *d)
75 {
76         unsigned int irq = d->irq - ATH79_MISC_IRQ_BASE;
77         void __iomem *base = ath79_reset_base;
78         u32 t;
79
80         t = __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
81         __raw_writel(t & ~(1 << irq), base + AR71XX_RESET_REG_MISC_INT_STATUS);
82
83         /* flush write */
84         __raw_readl(base + AR71XX_RESET_REG_MISC_INT_STATUS);
85 }
86
87 static struct irq_chip ath79_misc_irq_chip = {
88         .name           = "MISC",
89         .irq_unmask     = ar71xx_misc_irq_unmask,
90         .irq_mask       = ar71xx_misc_irq_mask,
91 };
92
93 static void __init ath79_misc_irq_init(void)
94 {
95         void __iomem *base = ath79_reset_base;
96         int i;
97
98         __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_ENABLE);
99         __raw_writel(0, base + AR71XX_RESET_REG_MISC_INT_STATUS);
100
101         if (soc_is_ar71xx() || soc_is_ar913x())
102                 ath79_misc_irq_chip.irq_mask_ack = ar71xx_misc_irq_mask;
103         else if (soc_is_ar724x() ||
104                  soc_is_ar933x() ||
105                  soc_is_ar934x() ||
106                  soc_is_qca955x())
107                 ath79_misc_irq_chip.irq_ack = ar724x_misc_irq_ack;
108         else
109                 BUG();
110
111         for (i = ATH79_MISC_IRQ_BASE;
112              i < ATH79_MISC_IRQ_BASE + ATH79_MISC_IRQ_COUNT; i++) {
113                 irq_set_chip_and_handler(i, &ath79_misc_irq_chip,
114                                          handle_level_irq);
115         }
116
117         irq_set_chained_handler(ATH79_CPU_IRQ(6), ath79_misc_irq_handler);
118 }
119
120 static void ar934x_ip2_irq_dispatch(unsigned int irq, struct irq_desc *desc)
121 {
122         u32 status;
123
124         disable_irq_nosync(irq);
125
126         status = ath79_reset_rr(AR934X_RESET_REG_PCIE_WMAC_INT_STATUS);
127
128         if (status & AR934X_PCIE_WMAC_INT_PCIE_ALL) {
129                 ath79_ddr_wb_flush(3);
130                 generic_handle_irq(ATH79_IP2_IRQ(0));
131         } else if (status & AR934X_PCIE_WMAC_INT_WMAC_ALL) {
132                 ath79_ddr_wb_flush(4);
133                 generic_handle_irq(ATH79_IP2_IRQ(1));
134         } else {
135                 spurious_interrupt();
136         }
137
138         enable_irq(irq);
139 }
140
141 static void ar934x_ip2_irq_init(void)
142 {
143         int i;
144
145         for (i = ATH79_IP2_IRQ_BASE;
146              i < ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT; i++)
147                 irq_set_chip_and_handler(i, &dummy_irq_chip,
148                                          handle_level_irq);
149
150         irq_set_chained_handler(ATH79_CPU_IRQ(2), ar934x_ip2_irq_dispatch);
151 }
152
153 static void qca955x_ip2_irq_dispatch(unsigned int irq, struct irq_desc *desc)
154 {
155         u32 status;
156
157         disable_irq_nosync(irq);
158
159         status = ath79_reset_rr(QCA955X_RESET_REG_EXT_INT_STATUS);
160         status &= QCA955X_EXT_INT_PCIE_RC1_ALL | QCA955X_EXT_INT_WMAC_ALL;
161
162         if (status == 0) {
163                 spurious_interrupt();
164                 goto enable;
165         }
166
167         if (status & QCA955X_EXT_INT_PCIE_RC1_ALL) {
168                 /* TODO: flush DDR? */
169                 generic_handle_irq(ATH79_IP2_IRQ(0));
170         }
171
172         if (status & QCA955X_EXT_INT_WMAC_ALL) {
173                 /* TODO: flush DDR? */
174                 generic_handle_irq(ATH79_IP2_IRQ(1));
175         }
176
177 enable:
178         enable_irq(irq);
179 }
180
181 static void qca955x_ip3_irq_dispatch(unsigned int irq, struct irq_desc *desc)
182 {
183         u32 status;
184
185         disable_irq_nosync(irq);
186
187         status = ath79_reset_rr(QCA955X_RESET_REG_EXT_INT_STATUS);
188         status &= QCA955X_EXT_INT_PCIE_RC2_ALL |
189                   QCA955X_EXT_INT_USB1 |
190                   QCA955X_EXT_INT_USB2;
191
192         if (status == 0) {
193                 spurious_interrupt();
194                 goto enable;
195         }
196
197         if (status & QCA955X_EXT_INT_USB1) {
198                 /* TODO: flush DDR? */
199                 generic_handle_irq(ATH79_IP3_IRQ(0));
200         }
201
202         if (status & QCA955X_EXT_INT_USB2) {
203                 /* TODO: flush DDR? */
204                 generic_handle_irq(ATH79_IP3_IRQ(1));
205         }
206
207         if (status & QCA955X_EXT_INT_PCIE_RC2_ALL) {
208                 /* TODO: flush DDR? */
209                 generic_handle_irq(ATH79_IP3_IRQ(2));
210         }
211
212 enable:
213         enable_irq(irq);
214 }
215
216 static void qca955x_irq_init(void)
217 {
218         int i;
219
220         for (i = ATH79_IP2_IRQ_BASE;
221              i < ATH79_IP2_IRQ_BASE + ATH79_IP2_IRQ_COUNT; i++)
222                 irq_set_chip_and_handler(i, &dummy_irq_chip,
223                                          handle_level_irq);
224
225         irq_set_chained_handler(ATH79_CPU_IRQ(2), qca955x_ip2_irq_dispatch);
226
227         for (i = ATH79_IP3_IRQ_BASE;
228              i < ATH79_IP3_IRQ_BASE + ATH79_IP3_IRQ_COUNT; i++)
229                 irq_set_chip_and_handler(i, &dummy_irq_chip,
230                                          handle_level_irq);
231
232         irq_set_chained_handler(ATH79_CPU_IRQ(3), qca955x_ip3_irq_dispatch);
233 }
234
235 /*
236  * The IP2/IP3 lines are tied to a PCI/WMAC/USB device. Drivers for
237  * these devices typically allocate coherent DMA memory, however the
238  * DMA controller may still have some unsynchronized data in the FIFO.
239  * Issue a flush in the handlers to ensure that the driver sees
240  * the update.
241  *
242  * This array map the interrupt lines to the DDR write buffer channels.
243  */
244
245 static unsigned irq_wb_chan[8] = {
246         -1, -1, -1, -1, -1, -1, -1, -1,
247 };
248
249 asmlinkage void plat_irq_dispatch(void)
250 {
251         unsigned long pending;
252         int irq;
253
254         pending = read_c0_status() & read_c0_cause() & ST0_IM;
255
256         if (!pending) {
257                 spurious_interrupt();
258                 return;
259         }
260
261         pending >>= CAUSEB_IP;
262         while (pending) {
263                 irq = fls(pending) - 1;
264                 if (irq < ARRAY_SIZE(irq_wb_chan) && irq_wb_chan[irq] != -1)
265                         ath79_ddr_wb_flush(irq_wb_chan[irq]);
266                 do_IRQ(MIPS_CPU_IRQ_BASE + irq);
267                 pending &= ~BIT(irq);
268         }
269 }
270
271 void __init arch_init_irq(void)
272 {
273         if (soc_is_ar71xx() || soc_is_ar724x() ||
274             soc_is_ar913x() || soc_is_ar933x()) {
275                 irq_wb_chan[2] = 3;
276                 irq_wb_chan[3] = 2;
277         } else if (soc_is_ar934x()) {
278                 irq_wb_chan[3] = 2;
279         }
280
281         mips_cpu_irq_init();
282         ath79_misc_irq_init();
283
284         if (soc_is_ar934x())
285                 ar934x_ip2_irq_init();
286         else if (soc_is_qca955x())
287                 qca955x_irq_init();
288 }