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 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 * 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 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 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 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