Home | History | Annotate | Line # | Download | only in playstation2
      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