1 /* $NetBSD: interrupt.c,v 1.1 2001/10/16 15:38:54 uch Exp $ */ 2 3 /*- 4 * Copyright (c) 2001 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 "debug_playstation2.h" 37 #if defined INTR_DEBUG && !defined GSFB_DEBUG_MONITOR 38 #error "add option GSFB_DEBUG_MONITOR" 39 #endif 40 41 #include <sys/param.h> 42 #include <sys/malloc.h> 43 44 #include <uvm/uvm_extern.h> /* uvmexp.intrs */ 45 46 #include <machine/locore.h> /* mips3_cp0_*() */ 47 48 #include <playstation2/playstation2/interrupt.h> 49 50 #include <playstation2/ee/eevar.h> 51 #include <playstation2/ee/intcvar.h> 52 #include <playstation2/ee/intcreg.h> 53 #include <playstation2/ee/dmacreg.h> 54 #include <playstation2/ee/dmacvar.h> 55 #include <playstation2/ee/timervar.h> 56 57 #ifdef INTR_DEBUG 58 #include <playstation2/ee/gsvar.h> /* debug monitor */ 59 #endif 60 61 #ifdef DEBUG 62 #define STATIC 63 #else 64 #define STATIC static 65 #endif 66 67 struct _playstation2_evcnt _playstation2_evcnt = { 68 .clock = EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "hard", "clock"), 69 .sbus = EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "hard", "sbus"), 70 .dmac = EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "hard", "dmac"), 71 }; 72 73 STATIC struct { 74 u_int32_t sr, imask; 75 } _sif_call_env; 76 77 struct clockframe playstation2_clockframe; 78 struct playstation2_soft_intr playstation2_soft_intrs[_IPL_NSOFT]; 79 struct playstation2_soft_intrhand *softnet_intrhand; 80 81 u_int32_t __icu_mask[_IPL_N]; /* interrupt mask of DMAC/INTC */ 82 volatile u_int32_t md_imask; 83 84 #ifdef INTR_DEBUG 85 void _debug_print_ipl(void); 86 void _debug_print_intr(const char *); 87 #endif /* INTR_DEBUG */ 88 89 void 90 interrupt_init_bootstrap() 91 { 92 int i; 93 94 /* initialize interrupt mask (masked all) */ 95 for (i = 0; i < _IPL_N; i++) 96 __icu_mask[i] = 0xffffffff; 97 98 /* intialize EE embeded device */ 99 timer_init(); 100 101 /* clear all pending interrupt and disable all */ 102 intc_init(); /* INT0 */ 103 dmac_init(); /* INT1 */ 104 } 105 106 void 107 interrupt_init(void) 108 { 109 static const char *softintr_names[] = IPL_SOFTNAMES; 110 struct playstation2_soft_intr *asi; 111 int i; 112 113 evcnt_attach_static(&_playstation2_evcnt.clock); 114 evcnt_attach_static(&_playstation2_evcnt.sbus); 115 evcnt_attach_static(&_playstation2_evcnt.dmac); 116 117 for (i = 0; i < _IPL_NSOFT; i++) { 118 asi = &playstation2_soft_intrs[i]; 119 TAILQ_INIT(&asi->softintr_q); 120 121 asi->softintr_ipl = IPL_SOFT + i; 122 simple_lock_init(&asi->softintr_slock); 123 evcnt_attach_dynamic(&asi->softintr_evcnt, EVCNT_TYPE_INTR, 124 NULL, "soft", softintr_names[i]); 125 } 126 127 /* XXX Establish legacy soft interrupt handlers. */ 128 softnet_intrhand = softintr_establish(IPL_SOFTNET, 129 (void (*)(void *))netintr, NULL); 130 KDASSERT(softnet_intrhand != NULL); 131 132 /* install software interrupt handler */ 133 intc_intr_establish(I_CH10_TIMER1, IPL_SOFT, timer1_intr, 0); 134 intc_intr_establish(I_CH11_TIMER2, IPL_SOFTCLOCK, timer2_intr, 0); 135 136 /* IPL_SOFTNET and IPL_SOFTSERIAL are shared interrupt. */ 137 intc_intr_establish(I_CH12_TIMER3, IPL_SOFTNET, timer3_intr, 0); 138 139 /* enable SIF BIOS access */ 140 md_imask = ~D_STAT_CIM_BIT(D_CH5_SIF0); 141 mips_cp0_status_write(0x00010801); 142 } 143 144 /* 145 * Hardware interrupt support 146 */ 147 void 148 cpu_intr(u_int32_t status, u_int32_t cause, u_int32_t pc, u_int32_t ipending) 149 { 150 #if 0 151 _debug_print_intr(__FUNCTION__); 152 #endif 153 uvmexp.intrs++; 154 155 playstation2_clockframe.ppl = md_imask; 156 playstation2_clockframe.sr = status; 157 playstation2_clockframe.pc = pc; 158 159 if (ipending & MIPS_INT_MASK_0) { 160 intc_intr(md_imask); 161 } 162 163 if (ipending & MIPS_INT_MASK_1) { 164 _playstation2_evcnt.dmac.ev_count++; 165 dmac_intr(md_imask); 166 } 167 } 168 169 /* 170 * Software interrupt support 171 */ 172 void 173 softintr_dispatch(int soft) 174 { 175 struct playstation2_soft_intr *asi; 176 struct playstation2_soft_intrhand *sih; 177 int s; 178 179 s = _intr_suspend(); 180 181 asi = &playstation2_soft_intrs[soft]; 182 183 if (TAILQ_FIRST(&asi->softintr_q) != NULL) 184 asi->softintr_evcnt.ev_count++; 185 186 while ((sih = TAILQ_FIRST(&asi->softintr_q)) != NULL) { 187 TAILQ_REMOVE(&asi->softintr_q, sih, sih_q); 188 sih->sih_pending = 0; 189 190 uvmexp.softs++; 191 192 _intr_resume(s); 193 (*sih->sih_fn)(sih->sih_arg); 194 s = _intr_suspend(); 195 } 196 197 _intr_resume(s); 198 } 199 200 void 201 setsoft(int ipl) 202 { 203 const static int timer_map[] = { 204 [IPL_SOFT] = 1, 205 [IPL_SOFTCLOCK] = 2, 206 [IPL_SOFTNET] = 3, 207 [IPL_SOFTSERIAL]= 3, 208 }; 209 210 KDASSERT(ipl >= IPL_SOFT && ipl <= IPL_SOFTSERIAL); 211 212 /* kick one shot timer */ 213 timer_one_shot(timer_map[ipl]); 214 } 215 216 /* Register a software interrupt handler. */ 217 void * 218 softintr_establish(int ipl, void (*func)(void *), void *arg) 219 { 220 struct playstation2_soft_intr *asi; 221 struct playstation2_soft_intrhand *sih; 222 int s; 223 224 if (__predict_false(ipl >= (IPL_SOFT + _IPL_NSOFT) || 225 ipl < IPL_SOFT)) 226 panic("softintr_establish"); 227 228 sih = malloc(sizeof(*sih), M_DEVBUF, M_NOWAIT); 229 230 s = _intr_suspend(); 231 asi = &playstation2_soft_intrs[ipl - IPL_SOFT]; 232 if (__predict_true(sih != NULL)) { 233 sih->sih_intrhead = asi; 234 sih->sih_fn = func; 235 sih->sih_arg = arg; 236 sih->sih_pending = 0; 237 } 238 _intr_resume(s); 239 240 return (sih); 241 } 242 243 /* Unregister a software interrupt handler. */ 244 void 245 softintr_disestablish(void *arg) 246 { 247 struct playstation2_soft_intrhand *sih = arg; 248 struct playstation2_soft_intr *asi = sih->sih_intrhead; 249 int s; 250 251 s = _intr_suspend(); 252 if (sih->sih_pending) { 253 TAILQ_REMOVE(&asi->softintr_q, sih, sih_q); 254 sih->sih_pending = 0; 255 } 256 _intr_resume(s); 257 258 free(sih, M_DEVBUF); 259 } 260 261 /* 262 * SPL support 263 */ 264 void 265 md_ipl_register(enum ipl_type type, struct _ipl_holder *holder) 266 { 267 u_int32_t mask, new; 268 int i; 269 270 mask = (type == IPL_DMAC) ? 0x0000ffff : 0xffff0000; 271 272 for (i = 0; i < _IPL_N; i++) { 273 new = __icu_mask[i]; 274 new &= mask; 275 new |= (holder[i].mask & ~mask); 276 __icu_mask[i] = new; 277 } 278 } 279 280 int 281 splraise(int npl) 282 { 283 int s, opl; 284 285 s = _intr_suspend(); 286 opl = md_imask; 287 md_imask = opl | npl; 288 md_imask_update(); 289 _intr_resume(s); 290 291 return (opl); 292 } 293 294 void 295 splset(int npl) 296 { 297 int s; 298 299 s = _intr_suspend(); 300 md_imask = npl; 301 md_imask_update(); 302 _intr_resume(s); 303 } 304 305 void 306 spl0() 307 { 308 extern void _spllower(int); 309 310 splset(0); 311 _spllower(0); 312 } 313 314 /* 315 * SIF BIOS call of interrupt utility. 316 */ 317 void 318 _sif_call_start() 319 { 320 int s; 321 322 s = _intr_suspend(); 323 324 _sif_call_env.sr = mips_cp0_status_read(); 325 _sif_call_env.imask = md_imask; 326 327 md_imask = ~D_STAT_CIM_BIT(D_CH5_SIF0); 328 md_imask_update(); 329 330 mips_cp0_status_write(0x00010801); 331 dmac_intr_enable(D_CH5_SIF0); 332 333 _intr_resume(s); 334 } 335 336 void 337 _sif_call_end() 338 { 339 int s; 340 341 s = _intr_suspend(); 342 343 md_imask = _sif_call_env.imask; 344 md_imask_update(); 345 mips_cp0_status_write(_sif_call_env.sr); 346 347 _intr_resume(s); 348 } 349 350 #ifdef INTR_DEBUG 351 void 352 _debug_print_ipl() 353 { 354 int i; 355 356 printf("interrupt mask\n"); 357 for (i = 0; i < _IPL_N; i++) 358 printf("%d: %08x\n", i, __icu_mask[i]); 359 } 360 361 void 362 _debug_print_intr(const char *ident) 363 { 364 365 __gsfb_print(0, 366 "CLOCK %-5lld SBUS %-5lld DMAC %-5lld " 367 "misc %-5lld clock %-5lld net %-5lld serial %-5lld\n" 368 "SR=%08x PC=%08x cpl=%08x intc=%08x dmac=%08x sched=%08x\n", 369 _playstation2_evcnt.clock.ev_count, 370 _playstation2_evcnt.sbus.ev_count, 371 _playstation2_evcnt.dmac.ev_count, 372 playstation2_soft_intrs[0].softintr_evcnt.ev_count, 373 playstation2_soft_intrs[1].softintr_evcnt.ev_count, 374 playstation2_soft_intrs[2].softintr_evcnt.ev_count, 375 playstation2_soft_intrs[3].softintr_evcnt.ev_count, 376 playstation2_clockframe.sr, playstation2_clockframe.pc, 377 md_imask, 378 (_reg_read_4(I_MASK_REG) << 16) | 379 (_reg_read_4(I_STAT_REG) & 0x0000ffff), 380 _reg_read_4(D_STAT_REG), sched_whichqs); 381 } 382 #endif /* INTR_DEBUG */ 383 384