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