intr.c revision 1.4 1 /* $NetBSD: intr.c,v 1.4 2010/12/20 00:25:36 matt Exp $ */
2
3 /*-
4 * Copyright (c) 2005 NONAKA Kimihiro
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 REGENTS AND CONTRIBUTORS ``AS IS'' AND
17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
26 * SUCH DAMAGE.
27 */
28
29 #include <sys/cdefs.h>
30 __KERNEL_RCSID(0, "$NetBSD: intr.c,v 1.4 2010/12/20 00:25:36 matt Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/kernel.h>
35 #include <sys/malloc.h>
36 #include <sys/device.h>
37
38 #include <sh3/exception.h>
39
40 #include <machine/intr.h>
41
42 #define _N_EXTINTR 8
43
44 #define LANDISK_INTEN 0xb0000005
45 #define INTEN_ALL_MASK 0x00
46
47 struct intrhand {
48 int (*ih_fun)(void *);
49 void *ih_arg;
50 struct intrhand *ih_next;
51 int ih_enable;
52 int ih_level;
53 int ih_irq;
54 struct evcnt ih_evcnt;
55 };
56
57 struct extintr_handler {
58 int (*eih_func)(void *eih_arg);
59 void *eih_arg;
60 struct intrhand *eih_ih;
61 int eih_nih;
62 };
63
64 static struct extintr_handler extintr_handler[_N_EXTINTR];
65
66 static const char *extintr_names[_N_EXTINTR] = {
67 "irq5", "irq6", "irq7", "irq8",
68 "irq9", "irq10", "irq11", "irq12"
69 };
70
71 static int fakeintr(void *arg);
72 static int extintr_intr_handler(void *arg);
73
74 void
75 intc_intr(int ssr, int spc, int ssp)
76 {
77 struct intc_intrhand *ih;
78 struct clockframe cf;
79 int evtcode;
80
81 curcpu()->ci_data.cpu_nintr++;
82
83 evtcode = _reg_read_4(SH4_INTEVT);
84 ih = EVTCODE_IH(evtcode);
85 KDASSERT(ih->ih_func);
86
87 switch (evtcode) {
88 #if 0
89 #define IRL(irq) (0x200 + ((irq) << 5))
90 case IRL(5): case IRL(6): case IRL(7): case IRL(8):
91 case IRL(9): case IRL(10): case IRL(11): case IRL(12):
92 {
93 int level;
94 uint8_t inten, bit;
95
96 bit = 1 << (EVTCODE_TO_MAP_INDEX(evtcode) - 5);
97 inten = _reg_read_1(LANDISK_INTEN);
98 _reg_write_1(LANDISK_INTEN, inten & ~bit);
99 level = (_IPL_NSOFT + 1) << 4; /* disable softintr */
100 ssr &= 0xf0;
101 if (level < ssr)
102 level = ssr;
103 (void)_cpu_intr_resume(level);
104 (*ih->ih_func)(ih->ih_arg);
105 _reg_write_1(LANDISK_INTEN, inten);
106 break;
107 }
108 #endif
109 default:
110 (void)_cpu_intr_resume(ih->ih_level);
111 (*ih->ih_func)(ih->ih_arg);
112 break;
113
114 case SH_INTEVT_TMU0_TUNI0:
115 (void)_cpu_intr_resume(ih->ih_level);
116 cf.spc = spc;
117 cf.ssr = ssr;
118 cf.ssp = ssp;
119 (*ih->ih_func)(&cf);
120 break;
121
122 case SH_INTEVT_NMI:
123 printf("NMI ignored.\n");
124 break;
125 }
126 }
127
128 void
129 intr_init(void)
130 {
131
132 _reg_write_1(LANDISK_INTEN, INTEN_ALL_MASK);
133 }
134
135 void *
136 extintr_establish(int irq, int level, int (*ih_fun)(void *), void *ih_arg)
137 {
138 static struct intrhand fakehand = {fakeintr};
139 struct extintr_handler *eih;
140 struct intrhand **p, *q, *ih;
141 const char *name;
142 int evtcode;
143 int s;
144
145 KDASSERT(irq >= 5 && irq <= 12);
146
147 ih = malloc(sizeof(*ih), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
148 if (ih == NULL)
149 panic("intr_establish: can't malloc handler info");
150
151 s = _cpu_intr_suspend();
152
153 switch (level) {
154 default:
155 #if defined(DEBUG)
156 panic("extintr_establish: unknown level %d", level);
157 /*NOTREACHED*/
158 #endif
159 case IPL_VM:
160 break;
161 }
162
163 eih = &extintr_handler[irq - 5];
164 if (eih->eih_func == NULL) {
165 evtcode = 0x200 + (irq << 5);
166 eih->eih_func = intc_intr_establish(evtcode, IST_LEVEL, level,
167 extintr_intr_handler, eih);
168 }
169
170 /*
171 * Figure out where to put the handler.
172 * This is O(N^2), but we want to preserve the order, and N is
173 * generally small.
174 */
175 for (p = &eih->eih_ih; (q = *p) != NULL; p = &q->ih_next)
176 continue;
177
178 /*
179 * Actually install a fake handler momentarily, since we might be doing
180 * this with interrupts enabled and don't want the real routine called
181 * until masking is set up.
182 */
183 fakehand.ih_level = level;
184 *p = &fakehand;
185
186 /*
187 * Poke the real handler in now.
188 */
189 memset(ih, 0, sizeof(*ih));
190 ih->ih_fun = ih_fun;
191 ih->ih_arg = ih_arg;
192 ih->ih_next = NULL;
193 ih->ih_enable = 1;
194 ih->ih_level = level;
195 ih->ih_irq = irq - 5;
196 name = extintr_names[irq - 5];
197 evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR,
198 NULL, "ext", name);
199 *p = ih;
200
201 if (++eih->eih_nih == 1) {
202 /* Unmask interrupt */
203 _reg_bset_1(LANDISK_INTEN, (1 << (irq - 5)));
204 }
205
206 splx(s);
207
208 return (ih);
209 }
210
211 void
212 extintr_disestablish(void *aux)
213 {
214 struct intrhand *ih = aux;
215 struct intrhand **p, *q;
216 struct extintr_handler *eih;
217 int irq;
218 int s;
219
220 KDASSERT(ih != NULL);
221
222 s = _cpu_intr_suspend();
223
224 irq = ih->ih_irq;
225 eih = &extintr_handler[irq];
226
227 /*
228 * Remove the handler from the chain.
229 * This is O(n^2), too.
230 */
231 for (p = &eih->eih_ih; (q = *p) != NULL && q != ih; p = &q->ih_next)
232 continue;
233 if (q == NULL)
234 panic("extintr_disestablish: handler not registered");
235
236 *p = q->ih_next;
237
238 evcnt_detach(&ih->ih_evcnt);
239
240 free((void *)ih, M_DEVBUF);
241
242 if (--eih->eih_nih == 0) {
243 intc_intr_disestablish(eih->eih_func);
244
245 /* Mask interrupt */
246 _reg_bclr_1(LANDISK_INTEN, (1 << irq));
247 }
248
249 splx(s);
250 }
251
252 void
253 extintr_enable(void *aux)
254 {
255 struct intrhand *ih = aux;
256 struct intrhand *p, *q;
257 struct extintr_handler *eih;
258 int irq;
259 int cnt;
260 int s;
261
262 KDASSERT(ih != NULL);
263
264 s = _cpu_intr_suspend();
265
266 irq = ih->ih_irq;
267 KDASSERT(irq >= 0 && irq < 8);
268 eih = &extintr_handler[irq];
269 for (cnt = 0, p = eih->eih_ih, q = NULL; p != NULL; p = p->ih_next) {
270 if (p->ih_enable) {
271 cnt++;
272 }
273 if (p == ih) {
274 q = p;
275 p->ih_enable = 1;
276 }
277 }
278 KDASSERT(q != NULL);
279
280 if (cnt == 0) {
281 /* Unmask interrupt */
282 _reg_bset_1(LANDISK_INTEN, (1 << irq));
283 }
284
285 splx(s);
286 }
287
288 void
289 extintr_disable(void *aux)
290 {
291 struct intrhand *ih = aux;
292 struct intrhand *p, *q;
293 struct extintr_handler *eih;
294 int irq;
295 int cnt;
296 int s;
297
298 KDASSERT(ih != NULL);
299
300 s = _cpu_intr_suspend();
301
302 irq = ih->ih_irq;
303 KDASSERT(irq >= 0 && irq < 8);
304 eih = &extintr_handler[irq];
305 for (cnt = 0, p = eih->eih_ih, q = NULL; p != NULL; p = p->ih_next) {
306 if (p == ih) {
307 q = p;
308 p->ih_enable = 0;
309 }
310 if (!ih->ih_enable) {
311 cnt++;
312 }
313 }
314 KDASSERT(q != NULL);
315
316 if (cnt == 0) {
317 /* Mask interrupt */
318 _reg_bclr_1(LANDISK_INTEN, (1 << irq));
319 }
320
321 splx(s);
322 }
323
324 void
325 extintr_disable_by_num(int irq)
326 {
327 struct extintr_handler *eih;
328 struct intrhand *ih;
329 int s;
330
331 KDASSERT(irq >= 5 && irq <= 12);
332
333 s = _cpu_intr_suspend();
334 eih = &extintr_handler[irq - 5];
335 for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
336 ih->ih_enable = 0;
337 }
338 /* Mask interrupt */
339 _reg_bclr_1(LANDISK_INTEN, (1 << irq));
340 splx(s);
341 }
342
343 static int
344 fakeintr(void *arg)
345 {
346
347 return 0;
348 }
349
350 static int
351 extintr_intr_handler(void *arg)
352 {
353 struct extintr_handler *eih = arg;
354 struct intrhand *ih;
355 int r;
356
357 if (__predict_true(eih != NULL)) {
358 for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
359 if (__predict_true(ih->ih_enable)) {
360 r = (*ih->ih_fun)(ih->ih_arg);
361 if (__predict_true(r != 0)) {
362 ih->ih_evcnt.ev_count++;
363 }
364 }
365 }
366 return 1;
367 }
368 return 0;
369 }
370