1 /* $NetBSD: interrupt.c,v 1.20 2023/12/20 15:29:06 thorpej 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 * 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: interrupt.c,v 1.20 2023/12/20 15:29:06 thorpej Exp $"); 31 32 #include "debug_playstation2.h" 33 #if defined INTR_DEBUG && !defined GSFB_DEBUG_MONITOR 34 #error "add option GSFB_DEBUG_MONITOR" 35 #endif 36 37 #include <sys/param.h> 38 39 #include <uvm/uvm_extern.h> /* uvmexp.intrs */ 40 41 #include <machine/locore.h> /* mips3_cp0_*() */ 42 43 #include <playstation2/playstation2/interrupt.h> 44 45 #include <playstation2/ee/eevar.h> 46 #include <playstation2/ee/intcvar.h> 47 #include <playstation2/ee/intcreg.h> 48 #include <playstation2/ee/dmacreg.h> 49 #include <playstation2/ee/dmacvar.h> 50 #include <playstation2/ee/timervar.h> 51 52 #ifdef INTR_DEBUG 53 #include <playstation2/ee/gsvar.h> /* debug monitor */ 54 #endif 55 56 #ifdef DEBUG 57 #define STATIC 58 #else 59 #define STATIC static 60 #endif 61 62 struct _playstation2_evcnt _playstation2_evcnt = { 63 .clock = EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "hard", "clock"), 64 .sbus = EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "hard", "sbus"), 65 .dmac = EVCNT_INITIALIZER(EVCNT_TYPE_INTR, NULL, "hard", "dmac"), 66 }; 67 68 STATIC struct { 69 u_int32_t sr, imask; 70 } _sif_call_env; 71 72 struct clockframe playstation2_clockframe; 73 74 u_int32_t __icu_mask[_IPL_N]; /* interrupt mask of DMAC/INTC */ 75 volatile u_int32_t md_imask; 76 77 #ifdef INTR_DEBUG 78 void _debug_print_ipl(void); 79 void _debug_print_intr(const char *); 80 #endif /* INTR_DEBUG */ 81 82 void 83 interrupt_init_bootstrap(void) 84 { 85 int i; 86 87 /* initialize interrupt mask (masked all) */ 88 for (i = 0; i < _IPL_N; i++) 89 __icu_mask[i] = 0xffffffff; 90 91 /* initialize EE embedded device */ 92 timer_init(); 93 94 /* clear all pending interrupt and disable all */ 95 intc_init(); /* INT0 */ 96 dmac_init(); /* INT1 */ 97 } 98 99 void 100 interrupt_init(void) 101 { 102 evcnt_attach_static(&_playstation2_evcnt.clock); 103 evcnt_attach_static(&_playstation2_evcnt.sbus); 104 evcnt_attach_static(&_playstation2_evcnt.dmac); 105 106 /* install software interrupt handler */ 107 intc_intr_establish(I_CH10_TIMER1, IPL_SOFTCLOCK, timer1_intr, 0); 108 intc_intr_establish(I_CH11_TIMER2, IPL_SOFTBIO, timer2_intr, 0); 109 110 /* IPL_SOFTNET and IPL_SOFTSERIAL are shared interrupt. */ 111 intc_intr_establish(I_CH12_TIMER3, IPL_SOFTNET, timer3_intr, 0); 112 113 /* enable SIF BIOS access */ 114 md_imask = ~D_STAT_CIM_BIT(D_CH5_SIF0); 115 mips_cp0_status_write(0x00010801); 116 } 117 118 /* 119 * Hardware interrupt support 120 */ 121 void 122 cpu_intr(int ppl, vaddr_t pc, uint32_t status) 123 { 124 struct cpu_info *ci; 125 uint32_t ipending; 126 int ipl; 127 #if 0 128 _debug_print_intr(__func__); 129 #endif 130 131 ci = curcpu(); 132 ci->ci_idepth++; 133 ci->ci_data.cpu_nintr++; 134 135 playstation2_clockframe.intr = (curcpu()->ci_idepth > 1); 136 playstation2_clockframe.sr = status; 137 playstation2_clockframe.pc = pc; 138 139 while (ppl < (ipl = splintr(&ipending))) { 140 splx(ipl); 141 if (ipending & MIPS_INT_MASK_0) { 142 intc_intr(md_imask); 143 } 144 145 if (ipending & MIPS_INT_MASK_1) { 146 _playstation2_evcnt.dmac.ev_count++; 147 dmac_intr(md_imask); 148 } 149 (void)splhigh(); 150 } 151 } 152 void 153 setsoft(int ipl) 154 { 155 const static int timer_map[] = { 156 [IPL_SOFTCLOCK] = 1, 157 [IPL_SOFTBIO] = 2, 158 [IPL_SOFTNET] = 3, 159 [IPL_SOFTSERIAL]= 3, 160 }; 161 162 KDASSERT(ipl >= IPL_SOFTCLOCK && ipl <= IPL_SOFTSERIAL); 163 164 /* kick one shot timer */ 165 timer_one_shot(timer_map[ipl]); 166 } 167 168 /* 169 * SPL support 170 */ 171 void 172 md_ipl_register(enum ipl_type type, struct _ipl_holder *holder) 173 { 174 u_int32_t mask, new; 175 int i; 176 177 mask = (type == IPL_DMAC) ? 0x0000ffff : 0xffff0000; 178 179 for (i = 0; i < _IPL_N; i++) { 180 new = __icu_mask[i]; 181 new &= mask; 182 new |= (holder[i].mask & ~mask); 183 __icu_mask[i] = new; 184 } 185 } 186 187 int 188 splraise(int npl) 189 { 190 int s, opl; 191 192 s = _intr_suspend(); 193 opl = md_imask; 194 md_imask = opl | npl; 195 md_imask_update(); 196 _intr_resume(s); 197 198 return (opl); 199 } 200 201 void 202 splset(int npl) 203 { 204 int s; 205 206 s = _intr_suspend(); 207 md_imask = npl; 208 md_imask_update(); 209 _intr_resume(s); 210 } 211 212 void 213 spl0(void) 214 { 215 216 splset(0); 217 _spllower(0); 218 } 219 220 /* 221 * SIF BIOS call of interrupt utility. 222 */ 223 void 224 _sif_call_start(void) 225 { 226 int s; 227 228 s = _intr_suspend(); 229 230 _sif_call_env.sr = mips_cp0_status_read(); 231 _sif_call_env.imask = md_imask; 232 233 md_imask = ~D_STAT_CIM_BIT(D_CH5_SIF0); 234 md_imask_update(); 235 236 mips_cp0_status_write(0x00010801); 237 dmac_intr_enable(D_CH5_SIF0); 238 239 _intr_resume(s); 240 } 241 242 void 243 _sif_call_end(void) 244 { 245 int s; 246 247 s = _intr_suspend(); 248 249 md_imask = _sif_call_env.imask; 250 md_imask_update(); 251 mips_cp0_status_write(_sif_call_env.sr); 252 253 _intr_resume(s); 254 } 255 256 #ifdef INTR_DEBUG 257 void 258 _debug_print_ipl(void) 259 { 260 int i; 261 262 printf("interrupt mask\n"); 263 for (i = 0; i < _IPL_N; i++) 264 printf("%d: %08x\n", i, __icu_mask[i]); 265 } 266 267 void 268 _debug_print_intr(const char *ident) 269 { 270 271 __gsfb_print(0, 272 "CLOCK %-5lld SBUS %-5lld DMAC %-5lld " 273 "SR=%08x PC=%08x cpl=%08x intc=%08x dmac=%08x\n", 274 _playstation2_evcnt.clock.ev_count, 275 _playstation2_evcnt.sbus.ev_count, 276 _playstation2_evcnt.dmac.ev_count, 277 playstation2_clockframe.sr, playstation2_clockframe.pc, 278 md_imask, 279 (_reg_read_4(I_MASK_REG) << 16) | 280 (_reg_read_4(I_STAT_REG) & 0x0000ffff), 281 _reg_read_4(D_STAT_REG)); 282 } 283 #endif /* INTR_DEBUG */ 284 285