xref: /netbsd/sys/arch/playstation2/ee/dmac.c (revision bf9ec67e)
1 /*	$NetBSD: dmac.c,v 1.2 2001/11/14 18:15:31 thorpej Exp $	*/
2 
3 /*-
4  * Copyright (c) 2001 The NetBSD Foundation, Inc.
5  * All rights reserved.
6  *
7  * This code is derived from software contributed to The NetBSD Foundation
8  * by UCHIYAMA Yasushi.
9  *
10  * Redistribution and use in source and binary forms, with or without
11  * modification, are permitted provided that the following conditions
12  * are met:
13  * 1. Redistributions of source code must retain the above copyright
14  *    notice, this list of conditions and the following disclaimer.
15  * 2. Redistributions in binary form must reproduce the above copyright
16  *    notice, this list of conditions and the following disclaimer in the
17  *    documentation and/or other materials provided with the distribution.
18  * 3. All advertising materials mentioning features or use of this software
19  *    must display the following acknowledgement:
20  *        This product includes software developed by the NetBSD
21  *        Foundation, Inc. and its contributors.
22  * 4. Neither the name of The NetBSD Foundation nor the names of its
23  *    contributors may be used to endorse or promote products derived
24  *    from this software without specific prior written permission.
25  *
26  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
27  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
28  * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
29  * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
30  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
31  * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
32  * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
33  * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
34  * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
35  * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
36  * POSSIBILITY OF SUCH DAMAGE.
37  */
38 
39 #include "debug_playstation2.h"
40 
41 #include <sys/param.h>
42 #include <sys/systm.h>
43 
44 #include <mips/cache.h>
45 
46 #include <playstation2/ee/eevar.h>
47 #include <playstation2/ee/dmacvar.h>
48 #include <playstation2/ee/dmacreg.h>
49 #include <playstation2/ee/gsvar.h>	/* debug monitor */
50 
51 #include <playstation2/playstation2/interrupt.h>
52 
53 #ifdef DEBUG
54 #define LEGAL_CHANNEL(x)	((x) >= 0 && (x) <= 15)
55 #define STATIC
56 #else
57 #define STATIC	static
58 #endif
59 
60 #define _DMAC_NINTR	10
61 
62 STATIC vaddr_t __dmac_channel_base[_DMAC_NINTR] = {
63 	D0_REGBASE,
64 	D1_REGBASE,
65 	D2_REGBASE,
66 	D3_REGBASE,
67 	D4_REGBASE,
68 	D5_REGBASE,
69 	D6_REGBASE,
70 	D7_REGBASE,
71 	D8_REGBASE,
72 	D9_REGBASE
73 };
74 
75 u_int32_t __dmac_enabled_channel;
76 
77 STATIC int __dmac_intialized;
78 STATIC struct _ipl_dispatcher __dmac_dispatcher[_DMAC_NINTR];
79 STATIC struct _ipl_holder __dmac_ipl_holder[_IPL_N];
80 STATIC SLIST_HEAD(, _ipl_dispatcher) __dmac_dispatcher_head =
81  SLIST_HEAD_INITIALIZER(__dmac_dispatcher_head);
82 
83 void
84 dmac_init()
85 {
86 	int i;
87 
88 	if (__dmac_intialized++)
89 		return;
90 
91 	/* disable DMAC */
92 	_reg_write_4(D_ENABLEW_REG, D_ENABLE_SUSPEND);
93 
94 	/* disable all interrupt */
95 	for (i = 0; i < _DMAC_NINTR; i++)
96 		dmac_intr_disable(i);
97 
98 	for (i = 0; i < _IPL_N; i++)
99  		__dmac_ipl_holder[i].mask = 0xffffffff;
100 
101 	if (_reg_read_4(D_STAT_REG) & D_STAT_SIM)
102 		_reg_write_4(D_STAT_REG, D_STAT_SIM);
103 	if (_reg_read_4(D_STAT_REG) & D_STAT_MEIM)
104 		_reg_write_4(D_STAT_REG, D_STAT_MEIM);
105 
106 	/* clear all status */
107 	_reg_write_4(D_STAT_REG, _reg_read_4(D_STAT_REG) & D_STAT_CIS_MASK);
108 
109 	/* enable DMAC */
110 	_reg_write_4(D_ENABLEW_REG, 0);
111 	_reg_write_4(D_CTRL_REG, D_CTRL_DMAE);
112 }
113 
114 /*
115  * Interrupt
116  */
117 int
118 dmac_intr(u_int32_t mask)
119 {
120 	struct _ipl_dispatcher *dispatcher;
121 	u_int32_t r, dispatch, pending;
122 
123 	r = _reg_read_4(D_STAT_REG);
124 	mask = D_STAT_CIM(mask);
125 	dispatch = r & ~mask & __dmac_enabled_channel;
126 	pending = r & mask & __dmac_enabled_channel;
127 #if 0
128 	__gsfb_print(2,
129 	    "DMAC stat=%08x, mask=%08x, pend=%08x, disp=%08x enable=%08x\n",
130 	    r, mask, pending, dispatch, __dmac_enabled_channel);
131 #endif
132 	if (dispatch == 0)
133 		return (pending == 0 ? 1 : 0);
134 
135 	/* clear interrupt */
136 	_reg_write_4(D_STAT_REG, dispatch);
137 
138 	/* dispatch interrupt handler */
139 	SLIST_FOREACH(dispatcher, &__dmac_dispatcher_head, link) {
140 		if (dispatcher->bit & dispatch) {
141 			KDASSERT(dispatcher->func);
142 			(*dispatcher->func)(dispatcher->arg);
143 			dispatch &= ~dispatcher->bit;
144 		}
145 	}
146 
147 	/* disable spurious interrupt source */
148 	if (dispatch) {
149 		int i, bit;
150 		for (i = 0, bit = 1; i < _DMAC_NINTR; i++, bit <<= 1) {
151 			if (bit & dispatch) {
152 				dmac_intr_disable(i);
153 				printf("%s: spurious interrupt %d disabled.\n",
154 				    __FUNCTION__, i);
155 			}
156 		}
157 	}
158 
159 
160 	return (pending == 0 ? 1 : 0);
161 }
162 
163 void
164 dmac_intr_enable(enum dmac_channel ch)
165 {
166 	u_int32_t mask;
167 
168 	KDASSERT(LEGAL_CHANNEL(ch));
169 
170 	mask = D_STAT_CIM_BIT(ch);
171 	_reg_write_4(D_STAT_REG, (_reg_read_4(D_STAT_REG) & mask) ^ mask);
172 }
173 
174 void
175 dmac_intr_disable(enum dmac_channel ch)
176 {
177 	KDASSERT(LEGAL_CHANNEL(ch));
178 
179 	_reg_write_4(D_STAT_REG, _reg_read_4(D_STAT_REG) & D_STAT_CIM_BIT(ch));
180 }
181 
182 void
183 dmac_update_mask(u_int32_t mask)
184 {
185 	u_int32_t cur_mask;
186 
187 	mask = D_STAT_CIM(mask);
188 	cur_mask = _reg_read_4(D_STAT_REG);
189 
190 	_reg_write_4(D_STAT_REG, ((cur_mask ^ ~mask) | (cur_mask & mask)) &
191 	    D_STAT_CIM(__dmac_enabled_channel));
192 }
193 
194 void *
195 dmac_intr_establish(enum dmac_channel ch, int ipl, int (*func)(void *),
196     void *arg)
197 {
198 	struct _ipl_dispatcher *dispatcher = &__dmac_dispatcher[ch];
199 	struct _ipl_dispatcher *d;
200 	int i, s;
201 
202 	KDASSERT(dispatcher->func == NULL);
203 
204 	s = _intr_suspend();
205 	dispatcher->func = func;
206 	dispatcher->arg = arg;
207 	dispatcher->ipl = ipl;
208 	dispatcher->channel = ch;
209 	dispatcher->bit = D_STAT_CIS_BIT(ch);
210 
211 	for (i = 0; i < _IPL_N; i++) {
212 		if (i < ipl)
213 			__dmac_ipl_holder[i].mask &= ~D_STAT_CIM_BIT(ch);
214 		else
215 			__dmac_ipl_holder[i].mask |= D_STAT_CIM_BIT(ch);
216 	}
217 
218 	/* insert queue IPL order */
219 	if (SLIST_EMPTY(&__dmac_dispatcher_head)) {
220 		SLIST_INSERT_HEAD(&__dmac_dispatcher_head, dispatcher, link);
221 	} else {
222 		SLIST_FOREACH(d, &__dmac_dispatcher_head, link) {
223 			if (SLIST_NEXT(d, link) == 0 ||
224 			    SLIST_NEXT(d, link)->ipl < ipl) {
225 				SLIST_INSERT_AFTER(d, dispatcher, link);
226 				break;
227 			}
228 		}
229 	}
230 
231 	md_ipl_register(IPL_DMAC, __dmac_ipl_holder);
232 
233 	dmac_intr_enable(ch);
234 	__dmac_enabled_channel |= D_STAT_CIS_BIT(ch);
235 
236 	_intr_resume(s);
237 
238 	return ((void *)ch);
239 }
240 
241 void
242 dmac_intr_disestablish(void *handle)
243 {
244 	int ch = (int)(handle);
245 	struct _ipl_dispatcher *dispatcher = &__dmac_dispatcher[ch];
246 	int i, s;
247 
248 	s = _intr_suspend();
249 
250 	dmac_intr_disable(ch);
251 	dispatcher->func = NULL;
252 
253 	SLIST_REMOVE(&__dmac_dispatcher_head, dispatcher,
254 	    _ipl_dispatcher, link);
255 
256 	for (i = 0; i < _IPL_N; i++)
257 		__dmac_ipl_holder[i].mask |= D_STAT_CIM_BIT(ch);
258 
259 	md_ipl_register(IPL_DMAC, __dmac_ipl_holder);
260 	__dmac_enabled_channel &= ~D_STAT_CIS_BIT(ch);
261 
262 	_intr_resume(s);
263 }
264 
265 /*
266  * Start/Stop
267  */
268 void
269 dmac_start_channel(enum dmac_channel ch)
270 {
271 	bus_addr_t chcr = D_CHCR_REG(__dmac_channel_base[ch]);
272 	u_int32_t r;
273 	int s;
274 
275 	/* suspend all channels */
276 	s = _intr_suspend();
277 	r = _reg_read_4(D_ENABLER_REG);
278 	_reg_write_4(D_ENABLEW_REG, r | D_ENABLE_SUSPEND);
279 
280 	/* access CHCR */
281 	_reg_write_4(chcr, (_reg_read_4(chcr) | D_CHCR_STR));
282 
283 	/* start all channels */
284 	_reg_write_4(D_ENABLEW_REG, r & ~D_ENABLE_SUSPEND);
285 	_intr_resume(s);
286 }
287 
288 void
289 dmac_stop_channel(enum dmac_channel ch)
290 {
291 	bus_addr_t chcr = D_CHCR_REG(__dmac_channel_base[ch]);
292 	u_int32_t r;
293 	int s;
294 
295 	/* suspend all channels */
296 	s = _intr_suspend();
297 	r = _reg_read_4(D_ENABLER_REG);
298 	_reg_write_4(D_ENABLEW_REG, r | D_ENABLE_SUSPEND);
299 
300 	/* access CHCR */
301 	_reg_write_4(chcr, (_reg_read_4(chcr) & ~D_CHCR_STR));
302 
303 	/* resume all chanells */
304 	_reg_write_4(D_ENABLEW_REG, r);
305 	_intr_resume(s);
306 }
307 
308 void
309 dmac_sync_buffer()
310 {
311 
312 	mips_dcache_wbinv_all();
313 	__asm__ __volatile("sync.l");
314 }
315 
316 /*
317  * Polling
318  *   DMAC status connected to CPCOND[0].
319  */
320 void
321 dmac_cpc_set(enum dmac_channel ch)
322 {
323 	u_int32_t r;
324 
325 	r = _reg_read_4(D_PCR_REG);
326 	KDASSERT((D_PCR_CPC(r) & ~D_PCR_CPC_BIT(ch)) == 0);
327 
328 	/* clear interrupt status */
329 	_reg_write_4(D_STAT_REG, D_STAT_CIS_BIT(ch));
330 
331 	_reg_write_4(D_PCR_REG, r | D_PCR_CPC_BIT(ch));
332 }
333 
334 void
335 dmac_cpc_clear(enum dmac_channel ch)
336 {
337 
338 	_reg_write_4(D_PCR_REG,	_reg_read_4(D_PCR_REG) & ~D_PCR_CPC_BIT(ch))
339 }
340 
341 void
342 dmac_cpc_poll()
343 {
344 	__asm__ __volatile__(
345 		".set noreorder;"
346 	"1:	 nop;"
347 		"nop;"
348 		"nop;"
349 		"nop;"
350 		"nop;"
351 		"bc0f 1b;"
352 		" nop;"
353 		".set reorder");
354 }
355 
356 /* not recommended. use dmac_cpc_poll as possible */
357 void
358 dmac_bus_poll(enum dmac_channel ch)
359 {
360 	bus_addr_t chcr = D_CHCR_REG(__dmac_channel_base[ch]);
361 
362 	while (_reg_read_4(chcr) & D_CHCR_STR)
363 		;
364 }
365 
366 /*
367  * Misc
368  */
369 void
370 dmac_chcr_write(enum dmac_channel ch, u_int32_t v)
371 {
372 	u_int32_t r;
373 	int s;
374 
375 	/* suspend all channels */
376 	s = _intr_suspend();
377 	r = _reg_read_4(D_ENABLER_REG);
378 	_reg_write_4(D_ENABLEW_REG, r | D_ENABLE_SUSPEND);
379 
380 	/* write CHCR reg */
381 	_reg_write_4(D_CHCR_REG(__dmac_channel_base[ch]), v);
382 
383 	/* resume all chanells */
384 	_reg_write_4(D_ENABLEW_REG, r);
385 	_intr_resume(s);
386 }
387 
388