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