xref: /netbsd/sys/arch/dreamcast/dreamcast/sysasic.c (revision c4a72b64)
1 /*	$NetBSD: sysasic.c,v 1.4 2002/11/15 13:29:27 itohy 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  * 3. All advertising materials mentioning features or use of this software
16  *    must display the following acknowledgement:
17  *        This product includes software developed by the NetBSD
18  *        Foundation, Inc. and its contributors.
19  * 4. Neither the name of The NetBSD Foundation nor the names of its
20  *    contributors may be used to endorse or promote products derived
21  *    from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
24  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
25  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
26  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
27  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
28  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
29  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
30  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
31  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
32  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
33  * POSSIBILITY OF SUCH DAMAGE.
34  */
35 
36 #include <sys/param.h>
37 #include <sys/systm.h>
38 #include <sys/device.h>
39 #include <sys/syslog.h>
40 
41 #include <sh3/exception.h>
42 
43 #include <machine/intr.h>
44 #include <machine/sysasicvar.h>
45 
46 #define SYSASIC_INTR_ST		0xa05f6900
47 #define SYSASIC_INTR_EN(level)	(0xa05f6910 + ((level) << 4))
48 
49 #define SYSASIC_IRQ_LEVEL_13	0
50 #define SYSASIC_IRQ_LEVEL_11	1
51 #define SYSASIC_IRQ_LEVEL_9	2
52 #define SYSASIC_IRQ_LEVEL_MAX	2
53 #define SYSASIC_IRQ_INDEX_TO_IRQ(i)	(13 - 2 * (i))
54 
55 #define IPL_IRL9	IPL_BIO
56 #define IPL_IRL11	IPL_NET
57 #define IPL_IRL13	IPL_TTY
58 
59 /* per-irq */
60 struct sysasic_intrhand {
61 	/* for quick check on interrupt */
62 	unsigned	syh_events[(SYSASIC_EVENT_MAX + 1 + (32 - 1)) / 32];
63 #define SYSASIC_EVENT_NMAP	((SYSASIC_EVENT_MAX + 1 + (32 - 1)) / 32)
64 #define SYSASIC_EVENT_INTR_MAP(ev)	((ev) >> 5)
65 #define SYSASIC_EVENT_INTR_BIT(ev)	((unsigned) 1 << ((ev) & 31))
66 
67 	void	*syh_intc;
68 	int	syh_idx;
69 } sysasic_intrhand[SYSASIC_IRQ_LEVEL_MAX + 1];
70 
71 /* per-event */
72 struct	syh_eventhand {
73 	int	(*hnd_fn)(void *);	/* sub handler */
74 	void	*hnd_arg;
75 	struct sysasic_intrhand *hnd_syh;
76 } sysasic_eventhand[SYSASIC_EVENT_MAX + 1];
77 
78 int sysasic_intr(void *);
79 
80 const char * __pure
81 sysasic_intr_string(int ipl)
82 {
83 
84 	switch (ipl) {
85 	default:
86 #ifdef DEBUG
87 		panic("sysasic_intr_string: unknown ipl %d", ipl);
88 #endif
89 	case IPL_IRL9:
90 		return "SH4 IRL 9";
91 	case IPL_IRL11:
92 		return "SH4 IRL 11";
93 	case IPL_IRL13:
94 		return "SH4 IRL 13";
95 	}
96 	/* NOTREACHED */
97 }
98 
99 /*
100  * Set up an interrupt handler to start being called.
101  */
102 void *
103 sysasic_intr_establish(int event, int ipl, int (*ih_fun)(void *), void *ih_arg)
104 {
105 	struct sysasic_intrhand *syh;
106 	struct syh_eventhand *hnd;
107 	int idx;
108 	static const int idx2evt[3] = {
109 		SH_INTEVT_IRL13, SH_INTEVT_IRL11, SH_INTEVT_IRL9
110 	};
111 #ifdef DEBUG
112 	int i;
113 #endif
114 
115 	KDASSERT(event >= 0 && event <= SYSASIC_EVENT_MAX);
116 	KDASSERT(ih_fun);
117 
118 	/*
119 	 * Dreamcast use SH4 INTC as IRL mode.  If IRQ is specified,
120 	 * its priority level is fixed.
121 	 *
122 	 * We use IPL to specify the IRQ to trap programming errors. :D
123 	 */
124 	switch (ipl) {
125 	default:
126 #ifdef DEBUG
127 		panic("sysasic_intr_establish: unknown ipl %d", ipl);
128 #endif
129 	case IPL_IRL9:
130 		idx = SYSASIC_IRQ_LEVEL_9;
131 		break;
132 	case IPL_IRL11:
133 		idx = SYSASIC_IRQ_LEVEL_11;
134 		break;
135 	case IPL_IRL13:
136 		idx = SYSASIC_IRQ_LEVEL_13;
137 		break;
138 	}
139 
140 	syh = &sysasic_intrhand[idx];
141 
142 	if (syh->syh_intc == NULL) {
143 		syh->syh_idx	= idx;
144 		syh->syh_intc	= intc_intr_establish(idx2evt[idx], IST_LEVEL,
145 		    ipl, sysasic_intr, syh);
146 	}
147 
148 #ifdef DEBUG
149 	/* check if the event handler is already installed */
150 	for (i = 0; i <= SYSASIC_IRQ_LEVEL_MAX; i++)
151 		if ((sysasic_intrhand[i].syh_events[SYSASIC_EVENT_INTR_MAP(event)] &
152 		    SYSASIC_EVENT_INTR_BIT(event)) != 0)
153 			panic("sysasic_intr_establish: event %d already insatlled irq %d",
154 			    event, SYSASIC_IRQ_INDEX_TO_IRQ(i));
155 #endif
156 
157 	hnd = &sysasic_eventhand[event];
158 	hnd->hnd_fn = ih_fun;
159 	hnd->hnd_arg = ih_arg;
160 	hnd->hnd_syh = syh;
161 	sysasic_intr_enable(hnd, 1);
162 
163 	return (void *)hnd;
164 }
165 
166 /*
167  * Deregister an interrupt handler.
168  */
169 void
170 sysasic_intr_disestablish(void *arg)
171 {
172 	struct syh_eventhand *hnd = arg;
173 	struct sysasic_intrhand *syh;
174 	int event;
175 	int i;
176 
177 	event = hnd - sysasic_eventhand;
178 	KDASSERT(event >= 0 && event <= SYSASIC_EVENT_MAX);
179 	syh = hnd->hnd_syh;
180 
181 #ifdef DISAGNOSTIC
182 	if (syh->syh_events[SYSASIC_EVENT_INTR_MAP(event)] &
183 	    SYSASIC_EVENT_INTR_BIT(event)) == 0)
184 		panic("sysasic_intr_disestablish: event %d not installed for irq %d",
185 		    event, SYSASIC_IRQ_INDEX_TO_IRQ(syh->syh_idx));
186 #endif
187 
188 	sysasic_intr_enable(hnd, 0);
189 	hnd->hnd_fn = 0;
190 	hnd->hnd_arg = 0;
191 	hnd->hnd_syh = 0;
192 
193 	/* deinstall intrc if no event exists */
194 	for (i = 0; i < SYSASIC_EVENT_NMAP; i++)
195 		if (syh->syh_events[i])
196 			return;
197 	intc_intr_disestablish(syh->syh_intc);
198 	syh->syh_intc = 0;
199 }
200 
201 void
202 sysasic_intr_enable(void *arg, int on)
203 {
204 	struct syh_eventhand *hnd = arg;
205 	struct sysasic_intrhand *syh;
206 	int event;
207 	__volatile u_int32_t *masks, *stats;
208 	int evmap;
209 	unsigned evbit;
210 
211 	event = hnd - sysasic_eventhand;
212 	KDASSERT(event >= 0 && event <= SYSASIC_EVENT_MAX);
213 	syh = hnd->hnd_syh;
214 
215 #ifdef DISAGNOSTIC
216 	if (syh->syh_events[SYSASIC_EVENT_INTR_MAP(event)] &
217 	    SYSASIC_EVENT_INTR_BIT(event)) == 0)
218 		panic("sysasic_intr_enable: event %d not installed for irq %d",
219 		    event, SYSASIC_IRQ_INDEX_TO_IRQ(syh->syh_idx));
220 #endif
221 
222 	masks = (__volatile u_int32_t *) SYSASIC_INTR_EN(syh->syh_idx);
223 	stats = (__volatile u_int32_t *) SYSASIC_INTR_ST;
224 	evmap = SYSASIC_EVENT_INTR_MAP(event);
225 	evbit = SYSASIC_EVENT_INTR_BIT(event);
226 
227 	if (on) {
228 		/* clear pending event if any */
229 		stats[evmap] = evbit;
230 
231 		/* set event map */
232 		syh->syh_events[evmap] |= evbit;
233 
234 		/* enable interrupt */
235 		masks[evmap] = syh->syh_events[evmap];
236 	} else {
237 		/* disable interrupt */
238 		masks[evmap] = syh->syh_events[evmap] & ~evbit;
239 
240 		/* clear pending event if any */
241 		stats[evmap] = evbit;
242 
243 		/* clear event map */
244 		syh->syh_events[evmap] &= ~evbit;
245 	}
246 }
247 
248 int
249 sysasic_intr(void *arg)
250 {
251 	struct sysasic_intrhand *syh = arg;
252 	unsigned ev;
253 	int n, pos;
254 	u_int32_t *evwatched;
255 	__volatile u_int32_t *evmap;
256 	struct syh_eventhand *evh;
257 #ifdef DEBUG
258 	int handled = 0;
259 	static int reportcnt = 10;
260 #endif
261 
262 	/* bitmap of watched events */
263 	evwatched = syh->syh_events;
264 
265 	/* status / clear */
266 	evmap = (__volatile u_int32_t *) SYSASIC_INTR_ST;
267 
268 	for (n = 0; n < (SYSASIC_EVENT_NMAP << 5); n |= 31, n++, evmap++) {
269 		if ((ev = *evwatched++) && (ev &= *evmap)) {
270 
271 			/* clear interrupts */
272 			*evmap = ev;
273 
274 			n--;	/* to point at current bit after  n += pos */
275 
276 			/* call handlers */
277 			do {
278 				pos = ffs(ev);
279 				n += pos;
280 #ifdef __OPTIMIZE__
281 				/* optimized, assuming 1 <= pos <= 32 */
282 				asm("shld	%2,%0"
283 				    : "=r" (ev) : "0" (ev), "r" (-pos));
284 #else
285 				/* ``shift count >= bit width'' is undefined */
286 				if (pos >= 32)
287 					ev = 0;
288 				else
289 					ev >>= pos;
290 #endif
291 
292 				evh = &sysasic_eventhand[n];
293 #ifdef DEBUG
294 				KDASSERT(evh->hnd_fn);
295 				if ((*evh->hnd_fn)(evh->hnd_arg))
296 					handled = 1;
297 #else
298 				(*evh->hnd_fn)(evh->hnd_arg);
299 #endif
300 			} while (ev);
301 		}
302 	}
303 
304 #ifdef DEBUG
305 	if (!handled && reportcnt > 0) {
306 		reportcnt--;
307 		log(LOG_ERR, "sysasic_intr: stray irq%d interrupt%s\n",
308 		    SYSASIC_IRQ_INDEX_TO_IRQ(syh->syh_idx),
309 		    reportcnt == 0 ? "; stopped logging" : "");
310 	}
311 #endif
312 
313 	return 0;
314 }
315