xref: /netbsd-src/sys/arch/playstation2/playstation2/interrupt.c (revision e5fbc36ada28f9b9a5836ecffaf4a06aa1ebb687)
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
interrupt_init_bootstrap(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
interrupt_init(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
cpu_intr(int ppl,vaddr_t pc,uint32_t status)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
setsoft(int ipl)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
md_ipl_register(enum ipl_type type,struct _ipl_holder * holder)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
splraise(int npl)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
splset(int npl)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
spl0(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
_sif_call_start(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
_sif_call_end(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
_debug_print_ipl(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
_debug_print_intr(const char * ident)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