intr.c revision 1.12 1 /* $NetBSD: intr.c,v 1.12 2016/08/27 05:52:43 skrll Exp $ */
2
3 /*-
4 * Copyright (c) 2014 Michael Lorenz
5 * All rights reserved.
6 *
7 * Redistribution and use in source and binary forms, with or without
8 * modification, are permitted provided that the following conditions
9 * are met:
10 * 1. Redistributions of source code must retain the above copyright
11 * notice, this list of conditions and the following disclaimer.
12 * 2. Redistributions in binary form must reproduce the above copyright
13 * notice, this list of conditions and the following disclaimer in the
14 * documentation and/or other materials provided with the distribution.
15 *
16 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
17 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
18 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
19 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
20 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 * POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.12 2016/08/27 05:52:43 skrll Exp $");
31
32 #define __INTR_PRIVATE
33
34 #include "opt_multiprocessor.h"
35
36 #include <sys/param.h>
37 #include <sys/cpu.h>
38 #include <sys/device.h>
39 #include <sys/kernel.h>
40 #include <sys/systm.h>
41 #include <sys/timetc.h>
42 #include <sys/bitops.h>
43
44 #include <mips/locore.h>
45 #include <machine/intr.h>
46
47 #include <mips/ingenic/ingenic_regs.h>
48
49 #include "opt_ingenic.h"
50
51 #ifdef INGENIC_INTR_DEBUG
52 #define DPRINTF printf
53 #else
54 #define DPRINTF while (0) printf
55 #endif
56
57 extern void ingenic_clockintr(struct clockframe *);
58 extern void ingenic_puts(const char *);
59 /*
60 * This is a mask of bits to clear in the SR when we go to a
61 * given hardware interrupt priority level.
62 */
63 static const struct ipl_sr_map ingenic_ipl_sr_map = {
64 .sr_bits = {
65 [IPL_NONE] = 0,
66 [IPL_SOFTCLOCK] = MIPS_SOFT_INT_MASK_0,
67 [IPL_SOFTNET] = MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1,
68 [IPL_VM] =
69 MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
70 MIPS_INT_MASK_0 |
71 MIPS_INT_MASK_3 |
72 MIPS_INT_MASK_4 |
73 MIPS_INT_MASK_5,
74 [IPL_SCHED] =
75 MIPS_SOFT_INT_MASK_0 | MIPS_SOFT_INT_MASK_1 |
76 MIPS_INT_MASK_0 |
77 MIPS_INT_MASK_1 |
78 MIPS_INT_MASK_2 |
79 MIPS_INT_MASK_3 |
80 MIPS_INT_MASK_4 |
81 MIPS_INT_MASK_5,
82 [IPL_DDB] = MIPS_INT_MASK,
83 [IPL_HIGH] = MIPS_INT_MASK,
84 },
85 };
86
87 #define NINTR 64
88
89 /* some timer channels share interrupts, couldn't find any others */
90 struct intrhand {
91 struct evcnt ih_count;
92 char ih_name[16];
93 int (*ih_func)(void *);
94 void *ih_arg;
95 int ih_ipl;
96 };
97
98 struct intrhand intrs[NINTR];
99 struct evcnt clockintrs;
100
101 void ingenic_irq(int);
102
103 void
104 evbmips_intr_init(void)
105 {
106 uint32_t reg;
107 int i;
108
109 ipl_sr_map = ingenic_ipl_sr_map;
110
111 evcnt_attach_dynamic(&clockintrs,
112 EVCNT_TYPE_INTR, NULL, "timer", "intr");
113
114 /* zero all handlers */
115 for (i = 0; i < NINTR; i++) {
116 intrs[i].ih_func = NULL;
117 intrs[i].ih_arg = NULL;
118 snprintf(intrs[i].ih_name, sizeof(intrs[i].ih_name),
119 "irq %d", i);
120 evcnt_attach_dynamic(&intrs[i].ih_count, EVCNT_TYPE_INTR,
121 NULL, "INTC", intrs[i].ih_name);
122 }
123
124 /* mask all peripheral IRQs */
125 writereg(JZ_ICMR0, 0xffffffff);
126 writereg(JZ_ICMR1, 0xffffffff);
127
128 /* allow peripheral interrupts to core 0 only */
129 reg = MFC0(12, 4); /* reset entry and interrupts */
130 reg &= 0xffff0000;
131 reg |= REIM_IRQ0_M | REIM_MIRQ0_M;
132 #ifdef MULTIPROCESSOR
133 reg |= REIM_MIRQ1_M;
134 #endif
135 MTC0(reg, 12, 4);
136 MTC0(0, 20, 1); /* ping the 2nd core */
137 DPRINTF("%s %08x\n", __func__, reg);
138 }
139
140 void
141 evbmips_iointr(int ipl, uint32_t ipending, struct clockframe *cf)
142 {
143 uint32_t id;
144 #ifdef INGENIC_INTR_DEBUG
145 char buffer[256];
146
147 #if 0
148 snprintf(buffer, 256, "pending: %08x CR %08x\n", ipending,
149 MFC0(MIPS_COP_0_CAUSE, 0));
150 ingenic_puts(buffer);
151 #endif
152 #endif
153 /* see which core we're on */
154 id = MFC0(15, 1) & 7;
155
156 /*
157 * XXX
158 * the manual counts the softint bits as INT0 and INT1, our headers
159 * don't so everything here looks off by two
160 */
161 if (ipending & MIPS_INT_MASK_1) {
162 /*
163 * this is a mailbox interrupt / IPI
164 */
165 uint32_t reg;
166 int s = splsched();
167
168 /* read pending IPIs */
169 reg = MFC0(12, 3);
170 if (id == 0) {
171 if (reg & CS_MIRQ0_P) {
172 #ifdef MULTIPROCESSOR
173 uint32_t tag;
174 tag = MFC0(CP0_CORE_MBOX, 0);
175
176 ipi_process(curcpu(), tag);
177 #ifdef INGENIC_INTR_DEBUG
178 snprintf(buffer, 256,
179 "IPI for core 0, msg %08x\n", tag);
180 ingenic_puts(buffer);
181 #endif
182 #endif
183 reg &= (~CS_MIRQ0_P);
184 /* clear it */
185 MTC0(reg, 12, 3);
186 }
187 } else if (id == 1) {
188 if (reg & CS_MIRQ1_P) {
189 #ifdef MULTIPROCESSOR
190 uint32_t tag;
191 tag = MFC0(CP0_CORE_MBOX, 1);
192 ingenic_puts("1");
193 if (tag & 0x400)
194 hardclock(cf);
195 //ipi_process(curcpu(), tag);
196 #ifdef INGENIC_INTR_DEBUG
197 snprintf(buffer, 256,
198 "IPI for core 1, msg %08x\n", tag);
199 ingenic_puts(buffer);
200 #endif
201 #endif
202 reg &= (~CS_MIRQ1_P);
203 /* clear it */
204 MTC0(reg, 12, 3);
205 }
206 }
207 splx(s);
208 }
209 if (ipending & MIPS_INT_MASK_2) {
210 /* this is a timer interrupt */
211 ingenic_clockintr(cf);
212 clockintrs.ev_count++;
213 ingenic_puts("INT2\n");
214 }
215 if (ipending & MIPS_INT_MASK_0) {
216 uint32_t mask;
217 /* peripheral interrupt */
218
219 /*
220 * XXX
221 * OS timer interrupts are supposed to show up as INT2 as well
222 * but I haven't seen them there so for now we just weed them
223 * out right here.
224 * The idea is to allow peripheral interrupts on both cores but
225 * block INT0 on core1 so it would see only timer interrupts
226 * and IPIs. If that doesn't work we'll have to send an IPI to
227 * core1 for each timer tick.
228 */
229 mask = readreg(JZ_ICPR0);
230 if (mask & 0x0c000000) {
231 writereg(JZ_ICMSR0, 0x0c000000);
232 ingenic_clockintr(cf);
233 writereg(JZ_ICMCR0, 0x0c000000);
234 clockintrs.ev_count++;
235 }
236 ingenic_irq(ipl);
237 KASSERT(id == 0);
238 }
239 }
240
241 void
242 ingenic_irq(int ipl)
243 {
244 uint32_t irql, irqh, mask, ll, hh;
245 int bit, idx, bail;
246 #ifdef INGENIC_INTR_DEBUG
247 char buffer[16];
248 #endif
249
250 irql = readreg(JZ_ICPR0);
251 irqh = readreg(JZ_ICPR1);
252 #ifdef INGENIC_INTR_DEBUG
253 if (irql != 0) {
254 snprintf(buffer, 16, " il%08x", irql);
255 ingenic_puts(buffer);
256 }
257 #endif
258 bail = 32;
259 ll = irql;
260 hh = irqh;
261 writereg(JZ_ICMSR0, ll);
262 writereg(JZ_ICMSR1, hh);
263 bit = ffs32(irql);
264 while (bit != 0) {
265 idx = bit - 1;
266 mask = 1 << idx;
267 intrs[idx].ih_count.ev_count++;
268 if (intrs[idx].ih_func != NULL) {
269 if (intrs[idx].ih_ipl == IPL_VM)
270 KERNEL_LOCK(1, NULL);
271 intrs[idx].ih_func(intrs[idx].ih_arg);
272 if (intrs[idx].ih_ipl == IPL_VM)
273 KERNEL_UNLOCK_ONE(NULL);
274 } else {
275 /* spurious interrupt, mask it */
276 writereg(JZ_ICMSR0, mask);
277 }
278 irql &= ~mask;
279 bit = ffs32(irql);
280 bail--;
281 KASSERT(bail > 0);
282 }
283
284 #ifdef INGENIC_INTR_DEBUG
285 if (irqh != 0) {
286 snprintf(buffer, 16, " ih%08x", irqh);
287 ingenic_puts(buffer);
288 }
289 #endif
290 bit = ffs32(irqh);
291 while (bit != 0) {
292 idx = bit - 1;
293 mask = 1 << idx;
294 idx += 32;
295 intrs[idx].ih_count.ev_count++;
296 if (intrs[idx].ih_func != NULL) {
297 if (intrs[idx].ih_ipl == IPL_VM)
298 KERNEL_LOCK(1, NULL);
299 intrs[idx].ih_func(intrs[idx].ih_arg);
300 if (intrs[idx].ih_ipl == IPL_VM)
301 KERNEL_UNLOCK_ONE(NULL);
302 } else {
303 /* spurious interrupt, mask it */
304 writereg(JZ_ICMSR1, mask);
305 }
306 irqh &= ~mask;
307 bit = ffs32(irqh);
308 }
309 writereg(JZ_ICMCR0, ll);
310 writereg(JZ_ICMCR1, hh);
311 }
312
313 void *
314 evbmips_intr_establish(int irq, int (*func)(void *), void *arg)
315 {
316 int s;
317
318 if ((irq < 0) || (irq >= NINTR)) {
319 aprint_error("%s: invalid irq %d\n", __func__, irq);
320 return NULL;
321 }
322
323 s = splhigh(); /* XXX probably needs a mutex */
324 intrs[irq].ih_func = func;
325 intrs[irq].ih_arg = arg;
326 intrs[irq].ih_ipl = IPL_VM;
327
328 /* now enable the IRQ */
329 if (irq >= 32) {
330 writereg(JZ_ICMCR1, 1 << (irq - 32));
331 } else
332 writereg(JZ_ICMCR0, 1 << irq);
333
334 splx(s);
335
336 return ((void *)(irq + 1));
337 }
338
339 void
340 evbmips_intr_disestablish(void *cookie)
341 {
342 int irq = ((int)cookie) - 1;
343 int s;
344
345 if ((irq < 0) || (irq >= NINTR)) {
346 aprint_error("%s: invalid irq %d\n", __func__, irq);
347 return;
348 }
349
350 s = splhigh();
351
352 /* disable the IRQ */
353 if (irq >= 32) {
354 writereg(JZ_ICMSR1, 1 << (irq - 32));
355 } else
356 writereg(JZ_ICMSR0, 1 << irq);
357
358 intrs[irq].ih_func = NULL;
359 intrs[irq].ih_arg = NULL;
360 intrs[irq].ih_ipl = 0;
361
362 splx(s);
363 }
364