dmac.c revision 1.2 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