]> git.proxmox.com Git - mirror_ubuntu-artful-kernel.git/blame - arch/mips/lantiq/irq.c
MIPS: lantiq: enable oprofile support on lantiq targets
[mirror_ubuntu-artful-kernel.git] / arch / mips / lantiq / irq.c
CommitLineData
171bb2f1
JC
1/*
2 * This program is free software; you can redistribute it and/or modify it
3 * under the terms of the GNU General Public License version 2 as published
4 * by the Free Software Foundation.
5 *
6 * Copyright (C) 2010 John Crispin <blogic@openwrt.org>
7 * Copyright (C) 2010 Thomas Langer <thomas.langer@lantiq.com>
8 */
9
10#include <linux/interrupt.h>
11#include <linux/ioport.h>
12
13#include <asm/bootinfo.h>
14#include <asm/irq_cpu.h>
15
16#include <lantiq_soc.h>
17#include <irq.h>
18
19/* register definitions */
20#define LTQ_ICU_IM0_ISR 0x0000
21#define LTQ_ICU_IM0_IER 0x0008
22#define LTQ_ICU_IM0_IOSR 0x0010
23#define LTQ_ICU_IM0_IRSR 0x0018
24#define LTQ_ICU_IM0_IMR 0x0020
25#define LTQ_ICU_IM1_ISR 0x0028
26#define LTQ_ICU_OFFSET (LTQ_ICU_IM1_ISR - LTQ_ICU_IM0_ISR)
27
28#define LTQ_EIU_EXIN_C 0x0000
29#define LTQ_EIU_EXIN_INIC 0x0004
30#define LTQ_EIU_EXIN_INEN 0x000C
31
32/* irq numbers used by the external interrupt unit (EIU) */
33#define LTQ_EIU_IR0 (INT_NUM_IM4_IRL0 + 30)
34#define LTQ_EIU_IR1 (INT_NUM_IM3_IRL0 + 31)
35#define LTQ_EIU_IR2 (INT_NUM_IM1_IRL0 + 26)
36#define LTQ_EIU_IR3 INT_NUM_IM1_IRL0
37#define LTQ_EIU_IR4 (INT_NUM_IM1_IRL0 + 1)
38#define LTQ_EIU_IR5 (INT_NUM_IM1_IRL0 + 2)
39#define LTQ_EIU_IR6 (INT_NUM_IM2_IRL0 + 30)
40
41#define MAX_EIU 6
42
59c11579
JC
43/* the performance counter */
44#define LTQ_PERF_IRQ (INT_NUM_IM4_IRL0 + 31)
45
171bb2f1
JC
46/* irqs generated by device attached to the EBU need to be acked in
47 * a special manner
48 */
49#define LTQ_ICU_EBU_IRQ 22
50
51#define ltq_icu_w32(x, y) ltq_w32((x), ltq_icu_membase + (y))
52#define ltq_icu_r32(x) ltq_r32(ltq_icu_membase + (x))
53
54#define ltq_eiu_w32(x, y) ltq_w32((x), ltq_eiu_membase + (y))
55#define ltq_eiu_r32(x) ltq_r32(ltq_eiu_membase + (x))
56
57static unsigned short ltq_eiu_irq[MAX_EIU] = {
58 LTQ_EIU_IR0,
59 LTQ_EIU_IR1,
60 LTQ_EIU_IR2,
61 LTQ_EIU_IR3,
62 LTQ_EIU_IR4,
63 LTQ_EIU_IR5,
64};
65
66static struct resource ltq_icu_resource = {
67 .name = "icu",
68 .start = LTQ_ICU_BASE_ADDR,
69 .end = LTQ_ICU_BASE_ADDR + LTQ_ICU_SIZE - 1,
70 .flags = IORESOURCE_MEM,
71};
72
73static struct resource ltq_eiu_resource = {
74 .name = "eiu",
75 .start = LTQ_EIU_BASE_ADDR,
76 .end = LTQ_EIU_BASE_ADDR + LTQ_ICU_SIZE - 1,
77 .flags = IORESOURCE_MEM,
78};
79
80static void __iomem *ltq_icu_membase;
81static void __iomem *ltq_eiu_membase;
82
83void ltq_disable_irq(struct irq_data *d)
84{
85 u32 ier = LTQ_ICU_IM0_IER;
86 int irq_nr = d->irq - INT_NUM_IRQ0;
87
88 ier += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
89 irq_nr %= INT_NUM_IM_OFFSET;
90 ltq_icu_w32(ltq_icu_r32(ier) & ~(1 << irq_nr), ier);
91}
92
93void ltq_mask_and_ack_irq(struct irq_data *d)
94{
95 u32 ier = LTQ_ICU_IM0_IER;
96 u32 isr = LTQ_ICU_IM0_ISR;
97 int irq_nr = d->irq - INT_NUM_IRQ0;
98
99 ier += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
100 isr += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
101 irq_nr %= INT_NUM_IM_OFFSET;
102 ltq_icu_w32(ltq_icu_r32(ier) & ~(1 << irq_nr), ier);
103 ltq_icu_w32((1 << irq_nr), isr);
104}
105
106static void ltq_ack_irq(struct irq_data *d)
107{
108 u32 isr = LTQ_ICU_IM0_ISR;
109 int irq_nr = d->irq - INT_NUM_IRQ0;
110
111 isr += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
112 irq_nr %= INT_NUM_IM_OFFSET;
113 ltq_icu_w32((1 << irq_nr), isr);
114}
115
116void ltq_enable_irq(struct irq_data *d)
117{
118 u32 ier = LTQ_ICU_IM0_IER;
119 int irq_nr = d->irq - INT_NUM_IRQ0;
120
121 ier += LTQ_ICU_OFFSET * (irq_nr / INT_NUM_IM_OFFSET);
122 irq_nr %= INT_NUM_IM_OFFSET;
123 ltq_icu_w32(ltq_icu_r32(ier) | (1 << irq_nr), ier);
124}
125
126static unsigned int ltq_startup_eiu_irq(struct irq_data *d)
127{
128 int i;
171bb2f1
JC
129
130 ltq_enable_irq(d);
131 for (i = 0; i < MAX_EIU; i++) {
77fbdb30 132 if (d->irq == ltq_eiu_irq[i]) {
171bb2f1
JC
133 /* low level - we should really handle set_type */
134 ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_C) |
135 (0x6 << (i * 4)), LTQ_EIU_EXIN_C);
136 /* clear all pending */
137 ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INIC) & ~(1 << i),
138 LTQ_EIU_EXIN_INIC);
139 /* enable */
140 ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) | (1 << i),
141 LTQ_EIU_EXIN_INEN);
142 break;
143 }
144 }
145
146 return 0;
147}
148
149static void ltq_shutdown_eiu_irq(struct irq_data *d)
150{
151 int i;
171bb2f1
JC
152
153 ltq_disable_irq(d);
154 for (i = 0; i < MAX_EIU; i++) {
77fbdb30 155 if (d->irq == ltq_eiu_irq[i]) {
171bb2f1
JC
156 /* disable */
157 ltq_eiu_w32(ltq_eiu_r32(LTQ_EIU_EXIN_INEN) & ~(1 << i),
158 LTQ_EIU_EXIN_INEN);
159 break;
160 }
161 }
162}
163
164static struct irq_chip ltq_irq_type = {
165 "icu",
166 .irq_enable = ltq_enable_irq,
167 .irq_disable = ltq_disable_irq,
168 .irq_unmask = ltq_enable_irq,
169 .irq_ack = ltq_ack_irq,
170 .irq_mask = ltq_disable_irq,
171 .irq_mask_ack = ltq_mask_and_ack_irq,
172};
173
174static struct irq_chip ltq_eiu_type = {
175 "eiu",
176 .irq_startup = ltq_startup_eiu_irq,
177 .irq_shutdown = ltq_shutdown_eiu_irq,
178 .irq_enable = ltq_enable_irq,
179 .irq_disable = ltq_disable_irq,
180 .irq_unmask = ltq_enable_irq,
181 .irq_ack = ltq_ack_irq,
182 .irq_mask = ltq_disable_irq,
183 .irq_mask_ack = ltq_mask_and_ack_irq,
184};
185
186static void ltq_hw_irqdispatch(int module)
187{
188 u32 irq;
189
190 irq = ltq_icu_r32(LTQ_ICU_IM0_IOSR + (module * LTQ_ICU_OFFSET));
191 if (irq == 0)
192 return;
193
194 /* silicon bug causes only the msb set to 1 to be valid. all
195 * other bits might be bogus
196 */
197 irq = __fls(irq);
198 do_IRQ((int)irq + INT_NUM_IM0_IRL0 + (INT_NUM_IM_OFFSET * module));
199
200 /* if this is a EBU irq, we need to ack it or get a deadlock */
201 if ((irq == LTQ_ICU_EBU_IRQ) && (module == 0))
202 ltq_ebu_w32(ltq_ebu_r32(LTQ_EBU_PCC_ISTAT) | 0x10,
203 LTQ_EBU_PCC_ISTAT);
204}
205
206#define DEFINE_HWx_IRQDISPATCH(x) \
207 static void ltq_hw ## x ## _irqdispatch(void) \
208 { \
209 ltq_hw_irqdispatch(x); \
210 }
211DEFINE_HWx_IRQDISPATCH(0)
212DEFINE_HWx_IRQDISPATCH(1)
213DEFINE_HWx_IRQDISPATCH(2)
214DEFINE_HWx_IRQDISPATCH(3)
215DEFINE_HWx_IRQDISPATCH(4)
216
217static void ltq_hw5_irqdispatch(void)
218{
219 do_IRQ(MIPS_CPU_TIMER_IRQ);
220}
221
222asmlinkage void plat_irq_dispatch(void)
223{
224 unsigned int pending = read_c0_status() & read_c0_cause() & ST0_IM;
225 unsigned int i;
226
227 if (pending & CAUSEF_IP7) {
228 do_IRQ(MIPS_CPU_TIMER_IRQ);
229 goto out;
230 } else {
231 for (i = 0; i < 5; i++) {
232 if (pending & (CAUSEF_IP2 << i)) {
233 ltq_hw_irqdispatch(i);
234 goto out;
235 }
236 }
237 }
238 pr_alert("Spurious IRQ: CAUSE=0x%08x\n", read_c0_status());
239
240out:
241 return;
242}
243
244static struct irqaction cascade = {
245 .handler = no_action,
171bb2f1
JC
246 .name = "cascade",
247};
248
249void __init arch_init_irq(void)
250{
251 int i;
252
253 if (insert_resource(&iomem_resource, &ltq_icu_resource) < 0)
ab75dc02 254 panic("Failed to insert icu memory");
171bb2f1
JC
255
256 if (request_mem_region(ltq_icu_resource.start,
257 resource_size(&ltq_icu_resource), "icu") < 0)
ab75dc02 258 panic("Failed to request icu memory");
171bb2f1
JC
259
260 ltq_icu_membase = ioremap_nocache(ltq_icu_resource.start,
261 resource_size(&ltq_icu_resource));
262 if (!ltq_icu_membase)
ab75dc02 263 panic("Failed to remap icu memory");
171bb2f1
JC
264
265 if (insert_resource(&iomem_resource, &ltq_eiu_resource) < 0)
ab75dc02 266 panic("Failed to insert eiu memory");
171bb2f1
JC
267
268 if (request_mem_region(ltq_eiu_resource.start,
269 resource_size(&ltq_eiu_resource), "eiu") < 0)
ab75dc02 270 panic("Failed to request eiu memory");
171bb2f1
JC
271
272 ltq_eiu_membase = ioremap_nocache(ltq_eiu_resource.start,
273 resource_size(&ltq_eiu_resource));
274 if (!ltq_eiu_membase)
ab75dc02 275 panic("Failed to remap eiu memory");
171bb2f1 276
16f70b56
JC
277 /* turn off all irqs by default */
278 for (i = 0; i < 5; i++) {
279 /* make sure all irqs are turned off by default */
171bb2f1 280 ltq_icu_w32(0, LTQ_ICU_IM0_IER + (i * LTQ_ICU_OFFSET));
16f70b56
JC
281 /* clear all possibly pending interrupts */
282 ltq_icu_w32(~0, LTQ_ICU_IM0_ISR + (i * LTQ_ICU_OFFSET));
283 }
171bb2f1
JC
284
285 mips_cpu_irq_init();
286
287 for (i = 2; i <= 6; i++)
288 setup_irq(i, &cascade);
289
290 if (cpu_has_vint) {
291 pr_info("Setting up vectored interrupts\n");
292 set_vi_handler(2, ltq_hw0_irqdispatch);
293 set_vi_handler(3, ltq_hw1_irqdispatch);
294 set_vi_handler(4, ltq_hw2_irqdispatch);
295 set_vi_handler(5, ltq_hw3_irqdispatch);
296 set_vi_handler(6, ltq_hw4_irqdispatch);
297 set_vi_handler(7, ltq_hw5_irqdispatch);
298 }
299
300 for (i = INT_NUM_IRQ0;
301 i <= (INT_NUM_IRQ0 + (5 * INT_NUM_IM_OFFSET)); i++)
302 if ((i == LTQ_EIU_IR0) || (i == LTQ_EIU_IR1) ||
303 (i == LTQ_EIU_IR2))
304 irq_set_chip_and_handler(i, &ltq_eiu_type,
305 handle_level_irq);
306 /* EIU3-5 only exist on ar9 and vr9 */
307 else if (((i == LTQ_EIU_IR3) || (i == LTQ_EIU_IR4) ||
308 (i == LTQ_EIU_IR5)) && (ltq_is_ar9() || ltq_is_vr9()))
309 irq_set_chip_and_handler(i, &ltq_eiu_type,
310 handle_level_irq);
311 else
312 irq_set_chip_and_handler(i, &ltq_irq_type,
313 handle_level_irq);
314
315#if !defined(CONFIG_MIPS_MT_SMP) && !defined(CONFIG_MIPS_MT_SMTC)
316 set_c0_status(IE_IRQ0 | IE_IRQ1 | IE_IRQ2 |
317 IE_IRQ3 | IE_IRQ4 | IE_IRQ5);
318#else
319 set_c0_status(IE_SW0 | IE_SW1 | IE_IRQ0 | IE_IRQ1 |
320 IE_IRQ2 | IE_IRQ3 | IE_IRQ4 | IE_IRQ5);
321#endif
59c11579
JC
322
323 /* tell oprofile which irq to use */
324 cp0_perfcount_irq = LTQ_PERF_IRQ;
171bb2f1
JC
325}
326
327unsigned int __cpuinit get_c0_compare_int(void)
328{
329 return CP0_LEGACY_COMPARE_IRQ;
330}