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