1 /* $NetBSD: sysasic.c,v 1.14 2008/04/28 20:23:16 martin Exp $ */
2
3 /*-
4 * Copyright (c) 2001, 2002 The NetBSD Foundation, Inc.
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: sysasic.c,v 1.14 2008/04/28 20:23:16 martin Exp $");
31
32 #include <sys/param.h>
33 #include <sys/systm.h>
34 #include <sys/device.h>
35 #include <sys/syslog.h>
36
37 #include <sh3/exception.h>
38
39 #include <machine/intr.h>
40 #include <machine/sysasicvar.h>
41
42 #define SYSASIC_INTR_ST 0xa05f6900
43 #define SYSASIC_INTR_EN(level) (0xa05f6910 + ((level) << 4))
44
45 #define SYSASIC_IRQ_LEVEL_13 0
46 #define SYSASIC_IRQ_LEVEL_11 1
47 #define SYSASIC_IRQ_LEVEL_9 2
48 #define SYSASIC_IRQ_LEVEL_MAX 2
49 #define SYSASIC_IRQ_INDEX_TO_IRQ(i) (13 - 2 * (i))
50
51 /* per-irq */
52 struct sysasic_intrhand {
53 /* for quick check on interrupt */
54 #define SYSASIC_EVENT_NMAP ((SYSASIC_EVENT_MAX + 1 + (32 - 1)) / 32)
55 #define SYSASIC_EVENT_INTR_MAP(ev) ((ev) >> 5)
56 #define SYSASIC_EVENT_INTR_BIT(ev) ((unsigned) 1 << ((ev) & 31))
57 unsigned syh_events[SYSASIC_EVENT_NMAP]; /* enabled */
58 unsigned syh_hndmap[SYSASIC_EVENT_NMAP]; /* handler installed */
59
60 void *syh_intc;
61 int syh_idx;
62 } sysasic_intrhand[SYSASIC_IRQ_LEVEL_MAX + 1];
63
64 /* per-event */
65 struct syh_eventhand {
66 int (*hnd_fn)(void *); /* sub handler */
67 void *hnd_arg;
68 struct sysasic_intrhand *hnd_syh;
69 } sysasic_eventhand[SYSASIC_EVENT_MAX + 1];
70
71 int sysasic_intr(void *);
72
73 const char * __pure
sysasic_intr_string(int irl)74 sysasic_intr_string(int irl)
75 {
76
77 switch (irl) {
78 default:
79 #ifdef DEBUG
80 panic("sysasic_intr_string: unknown IRL%d", irl);
81 #endif
82 case SYSASIC_IRL9:
83 return "SH4 IRL 9";
84 case SYSASIC_IRL11:
85 return "SH4 IRL 11";
86 case SYSASIC_IRL13:
87 return "SH4 IRL 13";
88 }
89 /* NOTREACHED */
90 }
91
92 /*
93 * Set up an interrupt handler to start being called.
94 */
95 void *
sysasic_intr_establish(int event,int ipl,int irl,int (* ih_fun)(void *),void * ih_arg)96 sysasic_intr_establish(int event, int ipl, int irl, int (*ih_fun)(void *),
97 void *ih_arg)
98 {
99 struct sysasic_intrhand *syh;
100 struct syh_eventhand *hnd;
101 int idx;
102 static const int idx2evt[3] = {
103 SH_INTEVT_IRL13, SH_INTEVT_IRL11, SH_INTEVT_IRL9
104 };
105 #ifdef DEBUG
106 int i;
107 #endif
108
109 KDASSERT(event >= 0 && event <= SYSASIC_EVENT_MAX);
110 KDASSERT(ih_fun);
111
112 /*
113 * Dreamcast use SH4 INTC as IRL mode. If IRQ is specified,
114 * its priority level is fixed.
115 *
116 * We use IPL to specify the IRQ for clearness, that is, we use
117 * a splxxx() and IPL_XXX pair in a device driver.
118 */
119 switch (irl) {
120 default:
121 #ifdef DEBUG
122 panic("sysasic_intr_establish: unknown IRL %d", irl);
123 #endif
124 case SYSASIC_IRL9:
125 idx = SYSASIC_IRQ_LEVEL_9;
126 break;
127 case SYSASIC_IRL11:
128 idx = SYSASIC_IRQ_LEVEL_11;
129 break;
130 case SYSASIC_IRL13:
131 idx = SYSASIC_IRQ_LEVEL_13;
132 break;
133 }
134
135 syh = &sysasic_intrhand[idx];
136
137 if (syh->syh_intc == NULL) {
138 syh->syh_idx = idx;
139 syh->syh_intc = intc_intr_establish(idx2evt[idx], IST_LEVEL,
140 irl, sysasic_intr, syh);
141 }
142
143 #ifdef DEBUG
144 /* check if the event handler is already installed */
145 for (i = 0; i <= SYSASIC_IRQ_LEVEL_MAX; i++)
146 if ((sysasic_intrhand[i].syh_hndmap[SYSASIC_EVENT_INTR_MAP(event)] &
147 SYSASIC_EVENT_INTR_BIT(event)) != 0)
148 panic("sysasic_intr_establish: event %d already installed irq %d",
149 event, SYSASIC_IRQ_INDEX_TO_IRQ(i));
150 #endif
151
152 /* mark this event is established */
153 syh->syh_hndmap[SYSASIC_EVENT_INTR_MAP(event)] |=
154 SYSASIC_EVENT_INTR_BIT(event);
155
156 hnd = &sysasic_eventhand[event];
157 hnd->hnd_fn = ih_fun;
158 hnd->hnd_arg = ih_arg;
159 hnd->hnd_syh = syh;
160 sysasic_intr_enable(hnd, 1);
161
162 return (void *)hnd;
163 }
164
165 /*
166 * Deregister an interrupt handler.
167 */
168 void
sysasic_intr_disestablish(void * arg)169 sysasic_intr_disestablish(void *arg)
170 {
171 struct syh_eventhand *hnd = arg;
172 struct sysasic_intrhand *syh;
173 int event;
174 int i;
175
176 event = hnd - sysasic_eventhand;
177 KDASSERT(event >= 0 && event <= SYSASIC_EVENT_MAX);
178 syh = hnd->hnd_syh;
179
180 #ifdef DIAGNOSTIC
181 if ((syh->syh_hndmap[SYSASIC_EVENT_INTR_MAP(event)] &
182 SYSASIC_EVENT_INTR_BIT(event)) == 0)
183 panic("sysasic_intr_disestablish: event %d not installed for irq %d",
184 event, SYSASIC_IRQ_INDEX_TO_IRQ(syh->syh_idx));
185 #endif
186
187 sysasic_intr_enable(hnd, 0);
188 hnd->hnd_fn = 0;
189 hnd->hnd_arg = 0;
190 hnd->hnd_syh = 0;
191
192 syh->syh_hndmap[SYSASIC_EVENT_INTR_MAP(event)] &=
193 ~SYSASIC_EVENT_INTR_BIT(event);
194
195 /* deinstall intrc if no event exists */
196 for (i = 0; i < SYSASIC_EVENT_NMAP; i++)
197 if (syh->syh_hndmap[i])
198 return;
199 intc_intr_disestablish(syh->syh_intc);
200 syh->syh_intc = 0;
201 }
202
203 void
sysasic_intr_enable(void * arg,int on)204 sysasic_intr_enable(void *arg, int on)
205 {
206 struct syh_eventhand *hnd = arg;
207 struct sysasic_intrhand *syh;
208 int event;
209 volatile uint32_t *masks, *stats;
210 int evmap;
211 unsigned evbit;
212
213 event = hnd - sysasic_eventhand;
214 KDASSERT(event >= 0 && event <= SYSASIC_EVENT_MAX);
215 syh = hnd->hnd_syh;
216
217 #ifdef DIAGNOSTIC
218 if ((syh->syh_hndmap[SYSASIC_EVENT_INTR_MAP(event)] &
219 SYSASIC_EVENT_INTR_BIT(event)) == 0)
220 panic("sysasic_intr_enable: event %d not installed for irq %d",
221 event, SYSASIC_IRQ_INDEX_TO_IRQ(syh->syh_idx));
222 #endif
223
224 masks = (volatile uint32_t *) SYSASIC_INTR_EN(syh->syh_idx);
225 stats = (volatile uint32_t *) SYSASIC_INTR_ST;
226 evmap = SYSASIC_EVENT_INTR_MAP(event);
227 evbit = SYSASIC_EVENT_INTR_BIT(event);
228
229 if (on) {
230 /* clear pending event if any */
231 stats[evmap] = evbit;
232
233 /* set event map */
234 syh->syh_events[evmap] |= evbit;
235
236 /* enable interrupt */
237 masks[evmap] = syh->syh_events[evmap];
238 } else {
239 /* disable interrupt */
240 masks[evmap] = syh->syh_events[evmap] & ~evbit;
241
242 /* clear pending event if any */
243 stats[evmap] = evbit;
244
245 /* clear event map */
246 syh->syh_events[evmap] &= ~evbit;
247 }
248 }
249
250 int
sysasic_intr(void * arg)251 sysasic_intr(void *arg)
252 {
253 struct sysasic_intrhand *syh = arg;
254 unsigned ev;
255 int n, pos;
256 uint32_t *evwatched;
257 volatile uint32_t *evmap;
258 struct syh_eventhand *evh;
259 #ifdef DEBUG
260 int handled = 0;
261 static int reportcnt = 10;
262 #endif
263
264 /* bitmap of watched events */
265 evwatched = syh->syh_events;
266
267 /* status / clear */
268 evmap = (volatile uint32_t *) SYSASIC_INTR_ST;
269
270 for (n = 0; n < (SYSASIC_EVENT_NMAP << 5); n |= 31, n++, evmap++) {
271 if ((ev = *evwatched++) && (ev &= *evmap)) {
272
273 /* clear interrupts */
274 *evmap = ev;
275
276 n--; /* to point at current bit after n += pos */
277
278 /* call handlers */
279 do {
280 pos = ffs(ev);
281 n += pos;
282 #ifdef __OPTIMIZE__
283 /* optimized, assuming 1 <= pos <= 32 */
284 __asm("shld %2,%0"
285 : "=r" (ev) : "0" (ev), "r" (-pos));
286 #else
287 /* ``shift count >= bit width'' is undefined */
288 if (pos >= 32)
289 ev = 0;
290 else
291 ev >>= pos;
292 #endif
293
294 evh = &sysasic_eventhand[n];
295 #ifdef DEBUG
296 KDASSERT(evh->hnd_fn);
297 if ((*evh->hnd_fn)(evh->hnd_arg))
298 handled = 1;
299 #else
300 (*evh->hnd_fn)(evh->hnd_arg);
301 #endif
302 } while (ev);
303 }
304 }
305
306 #ifdef DEBUG
307 if (!handled && reportcnt > 0) {
308 reportcnt--;
309 log(LOG_ERR, "sysasic_intr: stray irq%d interrupt%s\n",
310 SYSASIC_IRQ_INDEX_TO_IRQ(syh->syh_idx),
311 reportcnt == 0 ? "; stopped logging" : "");
312 }
313 #endif
314
315 return 0;
316 }
317