1 /* $NetBSD: ap_ms104_sh4_intr.c,v 1.4 2020/11/21 16:21:24 thorpej Exp $ */
2
3 /*-
4 * Copyright (C) 2009 NONAKA Kimihiro <nonaka@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.4 2020/11/21 16:21:24 thorpej Exp $");
30
31 #include <sys/param.h>
32 #include <sys/systm.h>
33 #include <sys/kernel.h>
34 #include <sys/kmem.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
extintr_init(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
fakeintr(void * arg)86 fakeintr(void *arg)
87 {
88
89 return 0;
90 }
91
92 void *
extintr_establish(int irq,int trigger,int level,int (* ih_fun)(void *),void * ih_arg)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 = kmem_alloc(sizeof(*ih), KM_SLEEP);
106
107 s = _cpu_intr_suspend();
108
109 switch (level) {
110 default:
111 #if defined(DEBUG)
112 panic("extintr_establish: unknown level %d", level);
113 /*NOTREACHED*/
114 #endif
115 case IPL_VM:
116 break;
117 }
118
119 eih = &extintr_handler[irq];
120 if (eih->eih_func == NULL) {
121 evtcode = 0x200 + (irq << 5);
122 eih->eih_func = intc_intr_establish(evtcode, trigger, level,
123 extintr_intr_handler, eih);
124 }
125
126 /*
127 * Figure out where to put the handler.
128 * This is O(N^2), but we want to preserve the order, and N is
129 * generally small.
130 */
131 for (p = &eih->eih_ih; (q = *p) != NULL; p = &q->ih_next)
132 continue;
133
134 /*
135 * Actually install a fake handler momentarily, since we might be doing
136 * this with interrupts enabled and don't want the real routine called
137 * until masking is set up.
138 */
139 fakehand.ih_level = level;
140 *p = &fakehand;
141
142 /*
143 * Poke the real handler in now.
144 */
145 memset(ih, 0, sizeof(*ih));
146 ih->ih_fun = ih_fun;
147 ih->ih_arg = ih_arg;
148 ih->ih_next = NULL;
149 ih->ih_enable = 1;
150 ih->ih_level = level;
151 ih->ih_irq = irq;
152 name = extintr_names[irq];
153 evcnt_attach_dynamic(&ih->ih_evcnt, EVCNT_TYPE_INTR, NULL, "ext", name);
154 *p = ih;
155
156 if (++eih->eih_nih == 1) {
157 uint8_t reg;
158
159 /* Unmask interrupt */
160 switch (irq) {
161 case 1: case 2:
162 reg = _reg_read_1(EXTINTR_MASK4);
163 reg |= 1 << (2 - irq);
164 _reg_write_1(EXTINTR_MASK4, reg);
165 break;
166
167 case 3: case 4: case 5: case 6:
168 reg = _reg_read_1(EXTINTR_MASK3);
169 reg |= 1 << (6 - irq);
170 _reg_write_1(EXTINTR_MASK3, reg);
171 break;
172
173 case 7: case 8: case 9: case 10:
174 reg = _reg_read_1(EXTINTR_MASK2);
175 reg |= 1 << (10 - irq);
176 _reg_write_1(EXTINTR_MASK2, reg);
177 break;
178
179 case 11: case 12: case 13: case 14:
180 reg = _reg_read_1(EXTINTR_MASK1);
181 reg |= 1 << (14 - irq);
182 _reg_write_1(EXTINTR_MASK1, reg);
183 break;
184
185 default:
186 panic("unknown irq%d\n", irq);
187 /*NOTREACHED*/
188 break;
189 }
190 }
191
192 splx(s);
193
194 return (ih);
195 }
196
197 void
extintr_disestablish(void * cookie)198 extintr_disestablish(void *cookie)
199 {
200 struct intrhand *ih = (struct intrhand *)cookie;
201 struct intrhand **p, *q;
202 struct extintr_handler *eih;
203 int irq;
204 int s;
205
206 KDASSERT(ih != NULL);
207
208 s = _cpu_intr_suspend();
209
210 irq = ih->ih_irq;
211 eih = &extintr_handler[irq];
212
213 /*
214 * Remove the handler from the chain.
215 * This is O(n^2), too.
216 */
217 for (p = &eih->eih_ih; (q = *p) != NULL && q != ih; p = &q->ih_next)
218 continue;
219 if (q == NULL)
220 panic("extintr_disestablish: handler not registered");
221
222 *p = q->ih_next;
223
224 evcnt_detach(&ih->ih_evcnt);
225
226 kmem_free((void *)ih, sizeof(*ih));
227
228 if (--eih->eih_nih == 0) {
229 uint8_t reg;
230
231 intc_intr_disestablish(eih->eih_func);
232 eih->eih_func = NULL;
233
234 /* Mask interrupt */
235 switch (irq) {
236 case 1: case 2:
237 reg = _reg_read_1(EXTINTR_MASK4);
238 reg &= ~(1 << (2 - irq));
239 _reg_write_1(EXTINTR_MASK4, reg);
240 break;
241
242 case 3: case 4: case 5: case 6:
243 reg = _reg_read_1(EXTINTR_MASK3);
244 reg &= ~(1 << (6 - irq));
245 _reg_write_1(EXTINTR_MASK3, reg);
246 break;
247
248 case 7: case 8: case 9: case 10:
249 reg = _reg_read_1(EXTINTR_MASK2);
250 reg &= ~(1 << (10 - irq));
251 _reg_write_1(EXTINTR_MASK2, reg);
252 break;
253
254 case 11: case 12: case 13: case 14:
255 reg = _reg_read_1(EXTINTR_MASK1);
256 reg &= ~(1 << (14 - irq));
257 _reg_write_1(EXTINTR_MASK1, reg);
258 break;
259
260 default:
261 panic("unknown irq%d\n", irq);
262 /*NOTREACHED*/
263 break;
264 }
265 }
266
267 splx(s);
268 }
269
270 static int
extintr_intr_handler(void * arg)271 extintr_intr_handler(void *arg)
272 {
273 struct extintr_handler *eih = arg;
274 struct intrhand *ih;
275 int r;
276
277 if (__predict_true(eih != NULL)) {
278 for (ih = eih->eih_ih; ih != NULL; ih = ih->ih_next) {
279 if (__predict_true(ih->ih_enable)) {
280 r = (*ih->ih_fun)(ih->ih_arg);
281 if (__predict_true(r != 0)) {
282 ih->ih_evcnt.ev_count++;
283 }
284 }
285 }
286 return 1;
287 }
288 return 0;
289 }
290