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