ap_ms104_sh4_intr.c revision 1.1.18.1 1 /* $NetBSD: ap_ms104_sh4_intr.c,v 1.1.18.1 2012/02/18 07:32:00 mrg Exp $ */
2
3 /*-
4 * Copyright (C) 2009 NONAKA Kimihiro <nonaka (at) netbsd.org>
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 AUTHOR ``AS IS'' AND ANY EXPRESS OR
17 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
18 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
19 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
20 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
21 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
22 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
23 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
24 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
25 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
26 */
27
28 #include <sys/cdefs.h>
29 __KERNEL_RCSID(0, "$NetBSD: ap_ms104_sh4_intr.c,v 1.1.18.1 2012/02/18 07:32:00 mrg Exp $");
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/malloc.h>
35 #include <sys/device.h>
36
37 #include <sh3/devreg.h>
38 #include <sh3/exception.h>
39
40 #include <machine/intr.h>
41
42 #include <evbsh3/ap_ms104_sh4/ap_ms104_sh4reg.h>
43 #include <evbsh3/ap_ms104_sh4/ap_ms104_sh4var.h>
44
45 #define _N_EXTINTR 16
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 void *eih_func;
59 struct intrhand *eih_ih;
60 int eih_nih;
61 };
62 static struct extintr_handler extintr_handler[_N_EXTINTR];
63
64 static const char *extintr_names[_N_EXTINTR] = {
65 "irq0", "irq1", "irq2", "irq3",
66 "irq4", "irq5", "irq6", "irq7",
67 "irq8", "irq9", "irq10", "irq11",
68 "irq12", "irq13", "irq14", "irq15"
69 };
70
71 static int fakeintr(void *arg);
72 static int extintr_intr_handler(void *arg);
73
74 void
75 extintr_init(void)
76 {
77
78 _reg_write_1(EXTINTR_MASK1, 0);
79 _reg_write_1(EXTINTR_MASK2, 0);
80 _reg_write_1(EXTINTR_MASK3, 0);
81 _reg_write_1(EXTINTR_MASK4, 0);
82 }
83
84 /*ARGSUSED*/
85 static int
86 fakeintr(void *arg)
87 {
88
89 return 0;
90 }
91
92 void *
93 extintr_establish(int irq, int trigger, int level,
94 int (*ih_fun)(void *), void *ih_arg)
95 {
96 static struct intrhand fakehand = {fakeintr};
97 struct extintr_handler *eih;
98 struct intrhand **p, *q, *ih;
99 const char *name;
100 int evtcode;
101 int s;
102
103 KDASSERT(irq >= 1 && irq <= 14);
104
105 ih = malloc(sizeof(*ih), M_DEVBUF, cold ? M_NOWAIT : M_WAITOK);
106 if (ih == NULL)
107 panic("intr_establish: can't malloc handler info");
108
109 s = _cpu_intr_suspend();
110
111 switch (level) {
112 default:
113 #if defined(DEBUG)
114 panic("extintr_establish: unknown level %d", level);
115 /*NOTREACHED*/
116 #endif
117 case IPL_VM:
118 break;
119 }
120
121 eih = &extintr_handler[irq];
122 if (eih->eih_func == NULL) {
123 evtcode = 0x200 + (irq << 5);
124 eih->eih_func = intc_intr_establish(evtcode, trigger, level,
125 extintr_intr_handler, eih);
126 }
127
128 /*
129 * Figure out where to put the handler.
130 * This is O(N^2), but we want to preserve the order, and N is
131 * generally small.
132 */
133 for (p = &eih->eih_ih; (q = *p) != NULL; p = &q->ih_next)
134 continue;
135
136 /*
137 * Actually install a fake handler momentarily, since we might be doing
138 * this with interrupts enabled and don't want the real routine called
139 * until masking is set up.
140 */
141 fakehand.ih_level = level;
142 *p = &fakehand;
143
144 /*
145 * Poke the real handler in now.
146 */
147 memset(ih, 0, sizeof(*ih));
148 ih->ih_fun = ih_fun;
149 ih->ih_arg = ih_arg;
150 ih->ih_next = NULL;
151 ih->ih_enable = 1;
152 ih->ih_level = level;
153 ih->ih_irq = irq;
154 name = extintr_names[irq];
155 evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR, NULL, "ext", name);
156 *p = ih;
157
158 if (++eih->eih_nih == 1) {
159 uint8_t reg;
160
161 /* Unmask interrupt */
162 switch (irq) {
163 case 1: case 2:
164 reg = _reg_read_1(EXTINTR_MASK4);
165 reg |= 1 << (2 - irq);
166 _reg_write_1(EXTINTR_MASK4, reg);
167 break;
168
169 case 3: case 4: case 5: case 6:
170 reg = _reg_read_1(EXTINTR_MASK3);
171 reg |= 1 << (6 - irq);
172 _reg_write_1(EXTINTR_MASK3, reg);
173 break;
174
175 case 7: case 8: case 9: case 10:
176 reg = _reg_read_1(EXTINTR_MASK2);
177 reg |= 1 << (10 - irq);
178 _reg_write_1(EXTINTR_MASK2, reg);
179 break;
180
181 case 11: case 12: case 13: case 14:
182 reg = _reg_read_1(EXTINTR_MASK1);
183 reg |= 1 << (14 - irq);
184 _reg_write_1(EXTINTR_MASK1, reg);
185 break;
186
187 default:
188 panic("unknown irq%d\n", irq);
189 /*NOTREACHED*/
190 break;
191 }
192 }
193
194 splx(s);
195
196 return (ih);
197 }
198
199 void
200 extintr_disestablish(void *cookie)
201 {
202 struct intrhand *ih = (struct intrhand *)cookie;
203 struct intrhand **p, *q;
204 struct extintr_handler *eih;
205 int irq;
206 int s;
207
208 KDASSERT(ih != NULL);
209
210 s = _cpu_intr_suspend();
211
212 irq = ih->ih_irq;
213 eih = &extintr_handler[irq];
214
215 /*
216 * Remove the handler from the chain.
217 * This is O(n^2), too.
218 */
219 for (p = &eih->eih_ih; (q = *p) != NULL && q != ih; p = &q->ih_next)
220 continue;
221 if (q == NULL)
222 panic("extintr_disestablish: handler not registered");
223
224 *p = q->ih_next;
225
226 evcnt_detach(&ih->ih_evcnt);
227
228 free((void *)ih, M_DEVBUF);
229
230 if (--eih->eih_nih == 0) {
231 uint8_t reg;
232
233 intc_intr_disestablish(eih->eih_func);
234 eih->eih_func = NULL;
235
236 /* Mask interrupt */
237 switch (irq) {
238 case 1: case 2:
239 reg = _reg_read_1(EXTINTR_MASK4);
240 reg &= ~(1 << (2 - irq));
241 _reg_write_1(EXTINTR_MASK4, reg);
242 break;
243
244 case 3: case 4: case 5: case 6:
245 reg = _reg_read_1(EXTINTR_MASK3);
246 reg &= ~(1 << (6 - irq));
247 _reg_write_1(EXTINTR_MASK3, reg);
248 break;
249
250 case 7: case 8: case 9: case 10:
251 reg = _reg_read_1(EXTINTR_MASK2);
252 reg &= ~(1 << (10 - irq));
253 _reg_write_1(EXTINTR_MASK2, reg);
254 break;
255
256 case 11: case 12: case 13: case 14:
257 reg = _reg_read_1(EXTINTR_MASK1);
258 reg &= ~(1 << (14 - irq));
259 _reg_write_1(EXTINTR_MASK1, reg);
260 break;
261
262 default:
263 panic("unknown irq%d\n", irq);
264 /*NOTREACHED*/
265 break;
266 }
267 }
268
269 splx(s);
270 }
271
272 static int
273 extintr_intr_handler(void *arg)
274 {
275 struct extintr_handler *eih = arg;
276 struct intrhand *ih;
277 int r;
278
279 if (__predict_true(eih != NULL)) {
280 for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
281 if (__predict_true(ih->ih_enable)) {
282 r = (*ih->ih_fun)(ih->ih_arg);
283 if (__predict_true(r != 0)) {
284 ih->ih_evcnt.ev_count++;
285 }
286 }
287 }
288 return 1;
289 }
290 return 0;
291 }
292