s3c2440_dma.c revision 1.1 1 1.1 nisimura /*-
2 1.1 nisimura * Copyright (c) 2012 The NetBSD Foundation, Inc.
3 1.1 nisimura * All rights reserved.
4 1.1 nisimura *
5 1.1 nisimura * This code is derived from software contributed to The NetBSD Foundation
6 1.1 nisimura * by Paul Fleischer <paul (at) xpg.dk>
7 1.1 nisimura *
8 1.1 nisimura * Redistribution and use in source and binary forms, with or without
9 1.1 nisimura * modification, are permitted provided that the following conditions
10 1.1 nisimura * are met:
11 1.1 nisimura * 1. Redistributions of source code must retain the above copyright
12 1.1 nisimura * notice, this list of conditions and the following disclaimer.
13 1.1 nisimura * 2. Redistributions in binary form must reproduce the above copyright
14 1.1 nisimura * notice, this list of conditions and the following disclaimer in the
15 1.1 nisimura * documentation and/or other materials provided with the distribution.
16 1.1 nisimura *
17 1.1 nisimura * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
18 1.1 nisimura * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
19 1.1 nisimura * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 1.1 nisimura * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
21 1.1 nisimura * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 1.1 nisimura * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 1.1 nisimura * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
24 1.1 nisimura * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
25 1.1 nisimura * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
26 1.1 nisimura * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
27 1.1 nisimura * POSSIBILITY OF SUCH DAMAGE.
28 1.1 nisimura */
29 1.1 nisimura
30 1.1 nisimura #include <sys/cdefs.h>
31 1.1 nisimura
32 1.1 nisimura #include <sys/param.h>
33 1.1 nisimura #include <sys/systm.h>
34 1.1 nisimura #include <sys/device.h>
35 1.1 nisimura #include <sys/kernel.h>
36 1.1 nisimura #include <sys/malloc.h>
37 1.1 nisimura #include <sys/queue.h>
38 1.1 nisimura
39 1.1 nisimura #include <sys/mutex.h>
40 1.1 nisimura #include <sys/condvar.h>
41 1.1 nisimura
42 1.1 nisimura #include <machine/cpu.h>
43 1.1 nisimura #include <sys/bus.h>
44 1.1 nisimura
45 1.1 nisimura #include <arch/arm/s3c2xx0/s3c2440_dma.h>
46 1.1 nisimura
47 1.1 nisimura #include <arm/s3c2xx0/s3c2440var.h>
48 1.1 nisimura #include <arch/arm/s3c2xx0/s3c2440reg.h>
49 1.1 nisimura
50 1.1 nisimura #include <uvm/uvm_extern.h>
51 1.1 nisimura #include <machine/pmap.h>
52 1.1 nisimura
53 1.1 nisimura //#define S3C2440_DMA_DEBUG
54 1.1 nisimura #ifdef S3C2440_DMA_DEBUG
55 1.1 nisimura #define DPRINTF(s) do {printf s; } while (/*CONSTCOND*/0)
56 1.1 nisimura #else
57 1.1 nisimura #define DPRINTF(s) do {} while (/*CONSTCOND*/0)
58 1.1 nisimura #endif
59 1.1 nisimura
60 1.1 nisimura #define DMAC_N_CHANNELS 4
61 1.1 nisimura
62 1.1 nisimura struct dmac_desc_segs {
63 1.1 nisimura bus_dma_segment_t *ds_curseg;
64 1.1 nisimura uint8_t ds_nsegs;
65 1.1 nisimura };
66 1.1 nisimura
67 1.1 nisimura SIMPLEQ_HEAD(dmac_xfer_state_head, dmac_xfer_state);
68 1.1 nisimura
69 1.1 nisimura struct dmac_xfer_state {
70 1.1 nisimura struct dmac_xfer dxs_xfer;
71 1.1 nisimura SIMPLEQ_ENTRY(dmac_xfer_state) dxs_link;
72 1.1 nisimura uint8_t dxs_channel;
73 1.1 nisimura #define DMAC_NO_CHANNEL (~0)
74 1.1 nisimura uint8_t dxs_width;
75 1.1 nisimura bool dxs_complete;
76 1.1 nisimura struct dmac_desc_segs dxs_segs[2];
77 1.1 nisimura uint32_t dxs_options;
78 1.1 nisimura };
79 1.1 nisimura
80 1.1 nisimura struct s3c2440_dmac_peripheral {
81 1.1 nisimura u_int8_t dp_id;
82 1.1 nisimura u_int8_t dp_channel_order[DMAC_N_CHANNELS+1];
83 1.1 nisimura #define PERPH_LAST DMAC_N_CHANNELS+1
84 1.1 nisimura u_int8_t dp_channel_source[DMAC_N_CHANNELS];
85 1.1 nisimura #define PERPH_NA 7
86 1.1 nisimura };
87 1.1 nisimura
88 1.1 nisimura struct s3c2440_dmac_channel {
89 1.1 nisimura struct dmac_xfer_state *dc_active; /* Active transfer, NULL if none */
90 1.1 nisimura
91 1.1 nisimura /* Request queue. Can easily be extended to support multiple
92 1.1 nisimura priorities */
93 1.1 nisimura struct dmac_xfer_state_head dc_queue;
94 1.1 nisimura };
95 1.1 nisimura
96 1.1 nisimura struct s3c2440_dmac_softc {
97 1.1 nisimura bus_space_tag_t sc_iot;
98 1.1 nisimura bus_space_handle_t sc_dmach;
99 1.1 nisimura bus_dma_tag_t sc_dmat;
100 1.1 nisimura struct kmutex sc_mutex;
101 1.1 nisimura struct s3c2440_dmac_channel sc_channels[DMAC_N_CHANNELS];
102 1.1 nisimura struct kmutex sc_intr_mutex;
103 1.1 nisimura struct kcondvar sc_intr_cv;
104 1.1 nisimura };
105 1.1 nisimura
106 1.1 nisimura static struct s3c2440_dmac_softc _s3c2440_dmac_sc;
107 1.1 nisimura static struct s3c2440_dmac_softc *s3c2440_dmac_sc = &_s3c2440_dmac_sc;
108 1.1 nisimura
109 1.1 nisimura /* TODO: Consider making the order configurable. */
110 1.1 nisimura static struct s3c2440_dmac_peripheral s3c2440_peripherals[] = {
111 1.1 nisimura {DMAC_PERIPH_NONE, {0,1,2,3}, {0, 0, 0, 0}},
112 1.1 nisimura {DMAC_PERIPH_XDREQ0, {0,PERPH_LAST}, {0, PERPH_NA, PERPH_NA, PERPH_NA}},
113 1.1 nisimura {DMAC_PERIPH_XDREQ1, {1,PERPH_LAST}, {PERPH_NA, 0, PERPH_NA, PERPH_NA}},
114 1.1 nisimura {DMAC_PERIPH_UART0, {0,PERPH_LAST}, {1, PERPH_NA, PERPH_NA, PERPH_NA}},
115 1.1 nisimura {DMAC_PERIPH_UART1, {1,PERPH_LAST}, {PERPH_NA, 1, PERPH_NA, PERPH_NA}},
116 1.1 nisimura {DMAC_PERIPH_UART2, {3,PERPH_LAST}, {PERPH_NA, PERPH_NA, PERPH_NA, 0}},
117 1.1 nisimura {DMAC_PERIPH_I2SSDO, {0, 2, PERPH_LAST}, {5, PERPH_NA, 0, PERPH_NA}},
118 1.1 nisimura {DMAC_PERIPH_I2SSDI, {1, 2, PERPH_LAST}, {PERPH_NA, 2, 1, PERPH_NA}},
119 1.1 nisimura {DMAC_PERIPH_SDI, {3, 2, 1, PERPH_LAST}, {2, 6, 2, 1}},
120 1.1 nisimura {DMAC_PERIPH_SPI0, {1, PERPH_LAST}, {PERPH_NA, 3, PERPH_NA, PERPH_NA}},
121 1.1 nisimura {DMAC_PERIPH_SPI1, {3, PERPH_LAST}, {PERPH_NA, PERPH_NA, PERPH_NA, 2}},
122 1.1 nisimura {DMAC_PERIPH_PCMIN, {0, 2, PERPH_LAST}, {6, PERPH_NA, 5, PERPH_NA}},
123 1.1 nisimura {DMAC_PERIPH_PCMOUT, {1, 3, PERPH_LAST}, {PERPH_NA, 5, PERPH_NA, 6}},
124 1.1 nisimura {DMAC_PERIPH_MICIN, {2, 3, PERPH_LAST}, {PERPH_NA, PERPH_NA, 6, 5}},
125 1.1 nisimura {DMAC_PERIPH_MICOUT, {2, 3, PERPH_LAST}, {PERPH_NA, PERPH_NA, 6, 5}},
126 1.1 nisimura {DMAC_PERIPH_TIMER, {0, 2, 3, PERPH_LAST}, {3, PERPH_NA, 3, 3}},
127 1.1 nisimura {DMAC_PERIPH_USBEP1, {0, PERPH_LAST}, {4, PERPH_NA, PERPH_NA, PERPH_NA}},
128 1.1 nisimura {DMAC_PERIPH_USBEP2, {1, PERPH_LAST}, {PERPH_NA, 4, PERPH_NA, PERPH_NA}},
129 1.1 nisimura {DMAC_PERIPH_USBEP3, {2, PERPH_LAST}, {PERPH_NA, PERPH_NA, 4, PERPH_NA}},
130 1.1 nisimura {DMAC_PERIPH_USBEP4, {3, PERPH_LAST}, {PERPH_NA, PERPH_NA, PERPH_NA, 4}}
131 1.1 nisimura };
132 1.1 nisimura
133 1.1 nisimura static void dmac_start(u_int8_t channel_no, struct dmac_xfer_state*);
134 1.1 nisimura static void dmac_transfer_segment(uint8_t channel_no, struct dmac_xfer_state*);
135 1.1 nisimura static void dmac_channel_done(uint8_t channel_no);
136 1.1 nisimura
137 1.1 nisimura void
138 1.1 nisimura s3c2440_dma_init(void)
139 1.1 nisimura {
140 1.1 nisimura struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
141 1.1 nisimura int i;
142 1.1 nisimura
143 1.1 nisimura sc->sc_iot = s3c2xx0_softc->sc_iot;
144 1.1 nisimura sc->sc_dmach = s3c2xx0_softc->sc_dmach;
145 1.1 nisimura sc->sc_dmat = s3c2xx0_softc->sc_dmat;
146 1.1 nisimura for(i = 0; i<DMAC_N_CHANNELS; i++) {
147 1.1 nisimura sc->sc_channels[i].dc_active = NULL;
148 1.1 nisimura SIMPLEQ_INIT(&sc->sc_channels[i].dc_queue);
149 1.1 nisimura }
150 1.1 nisimura
151 1.1 nisimura mutex_init(&sc->sc_mutex, MUTEX_DEFAULT, IPL_BIO);
152 1.1 nisimura
153 1.1 nisimura mutex_init(&sc->sc_intr_mutex, MUTEX_DEFAULT, IPL_BIO);
154 1.1 nisimura cv_init(&sc->sc_intr_cv, "s3c2440_dmaintr");
155 1.1 nisimura
156 1.1 nisimura /* Setup interrupt handler for DMA controller */
157 1.1 nisimura s3c24x0_intr_establish(S3C24X0_INT_DMA0, IPL_BIO,
158 1.1 nisimura IST_EDGE_RISING, s3c2440_dma_intr, (void*)1);
159 1.1 nisimura s3c24x0_intr_establish(S3C24X0_INT_DMA1, IPL_BIO,
160 1.1 nisimura IST_EDGE_RISING, s3c2440_dma_intr, (void*)2);
161 1.1 nisimura s3c24x0_intr_establish(S3C24X0_INT_DMA2, IPL_BIO,
162 1.1 nisimura IST_EDGE_RISING, s3c2440_dma_intr, (void*)3);
163 1.1 nisimura s3c24x0_intr_establish(S3C24X0_INT_DMA3, IPL_BIO,
164 1.1 nisimura IST_EDGE_RISING, s3c2440_dma_intr, (void*)4);
165 1.1 nisimura }
166 1.1 nisimura
167 1.1 nisimura int
168 1.1 nisimura s3c2440_dma_intr(void *arg)
169 1.1 nisimura {
170 1.1 nisimura /*struct s3c2xx0_softc *sc = s3c2xx0_softc;*/
171 1.1 nisimura struct s3c2440_dmac_softc *sc;
172 1.1 nisimura uint32_t status;
173 1.1 nisimura int channel;
174 1.1 nisimura struct s3c2440_dmac_channel *dc;
175 1.1 nisimura
176 1.1 nisimura sc = s3c2440_dmac_sc;
177 1.1 nisimura
178 1.1 nisimura channel = (int)arg - 1;
179 1.1 nisimura dc = &sc->sc_channels[channel];
180 1.1 nisimura
181 1.1 nisimura DPRINTF(("s3c2440_dma_intr\n"));
182 1.1 nisimura DPRINTF(("Channel %d\n", channel));
183 1.1 nisimura
184 1.1 nisimura status = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_STAT(channel));
185 1.1 nisimura DPRINTF(("Channel %d status: %d\n", channel, status));
186 1.1 nisimura
187 1.1 nisimura if ( !(status & DMASTAT_BUSY) ) {
188 1.1 nisimura struct dmac_xfer_state *dxs;
189 1.1 nisimura struct dmac_xfer *dx;
190 1.1 nisimura
191 1.1 nisimura dxs = dc->dc_active;
192 1.1 nisimura KASSERT(dxs != NULL);
193 1.1 nisimura
194 1.1 nisimura dx = &dxs->dxs_xfer;
195 1.1 nisimura
196 1.1 nisimura if (dx->dx_desc[DMAC_DESC_SRC].xd_increment) {
197 1.1 nisimura dxs->dxs_segs[DMAC_DESC_SRC].ds_nsegs--;
198 1.1 nisimura if (dxs->dxs_segs[DMAC_DESC_SRC].ds_nsegs == 0) {
199 1.1 nisimura dxs->dxs_complete = TRUE;
200 1.1 nisimura } else {
201 1.1 nisimura dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg++;
202 1.1 nisimura }
203 1.1 nisimura }
204 1.1 nisimura if (dx->dx_desc[DMAC_DESC_DST].xd_increment) {
205 1.1 nisimura dxs->dxs_segs[DMAC_DESC_DST].ds_nsegs--;
206 1.1 nisimura if (dxs->dxs_segs[DMAC_DESC_DST].ds_nsegs == 0) {
207 1.1 nisimura dxs->dxs_complete = TRUE;
208 1.1 nisimura } else {
209 1.1 nisimura dxs->dxs_segs[DMAC_DESC_DST].ds_curseg++;
210 1.1 nisimura }
211 1.1 nisimura }
212 1.1 nisimura
213 1.1 nisimura if (dxs->dxs_complete) {
214 1.1 nisimura dxs->dxs_channel = DMAC_NO_CHANNEL;
215 1.1 nisimura
216 1.1 nisimura /* Lock the DMA mutex before tampering with
217 1.1 nisimura the channel.
218 1.1 nisimura */
219 1.1 nisimura mutex_enter(&sc->sc_mutex);
220 1.1 nisimura dmac_channel_done(channel);
221 1.1 nisimura mutex_exit(&sc->sc_mutex);
222 1.1 nisimura
223 1.1 nisimura DPRINTF(("dx_done: %p\n", (void*)dx->dx_done));
224 1.1 nisimura if (dx->dx_done != NULL) {
225 1.1 nisimura (dx->dx_done)(dx, dx->dx_cookie);
226 1.1 nisimura }
227 1.1 nisimura } else {
228 1.1 nisimura dmac_transfer_segment(channel, dxs);
229 1.1 nisimura }
230 1.1 nisimura }
231 1.1 nisimura #if 0
232 1.1 nisimura if ( !(status & DMASTAT_BUSY) ) {
233 1.1 nisimura s3c2440_dma_xfer_t xfer;
234 1.1 nisimura
235 1.1 nisimura xfer = dma_channel_xfer[channel];
236 1.1 nisimura dma_channel_xfer[channel] = NULL;
237 1.1 nisimura
238 1.1 nisimura DPRINTF((" Channel %d completed transfer\n", channel));
239 1.1 nisimura
240 1.1 nisimura if (xfer->dx_remaining > 0 &&
241 1.1 nisimura xfer->dx_aborted == FALSE) {
242 1.1 nisimura
243 1.1 nisimura DPRINTF(("Preparing next transfer\n"));
244 1.1 nisimura
245 1.1 nisimura s3c2440_dma_xfer_start(xfer);
246 1.1 nisimura } else {
247 1.1 nisimura if (!xfer->dx_aborted && xfer->dx_callback != NULL)
248 1.1 nisimura (xfer->dx_callback)(xfer->dx_callback_arg);
249 1.1 nisimura
250 1.1 nisimura xfer->dx_complete = TRUE;
251 1.1 nisimura }
252 1.1 nisimura
253 1.1 nisimura }
254 1.1 nisimura #endif
255 1.1 nisimura
256 1.1 nisimura #ifdef S3C2440_DMA_DEBUG
257 1.1 nisimura status = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_CSRC(channel));
258 1.1 nisimura printf("Current source for channel %d: 0x%x\n", channel, status);
259 1.1 nisimura
260 1.1 nisimura status = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_CDST(channel));
261 1.1 nisimura printf("Current dest for channel %d: 0x%x\n", channel, status);
262 1.1 nisimura #endif
263 1.1 nisimura
264 1.1 nisimura /* TODO: Remove this as it activates any thread waiting for a transfer
265 1.1 nisimura to complete */
266 1.1 nisimura mutex_enter(&sc->sc_intr_mutex);
267 1.1 nisimura DPRINTF(("cv_broadcast\n"));
268 1.1 nisimura cv_broadcast(&sc->sc_intr_cv);
269 1.1 nisimura DPRINTF(("cv_broadcast done\n"));
270 1.1 nisimura mutex_exit(&sc->sc_intr_mutex);
271 1.1 nisimura
272 1.1 nisimura return 1;
273 1.1 nisimura }
274 1.1 nisimura
275 1.1 nisimura dmac_xfer_t
276 1.1 nisimura s3c2440_dmac_allocate_xfer(int flags) {
277 1.1 nisimura struct dmac_xfer_state *dxs;
278 1.1 nisimura
279 1.1 nisimura dxs = malloc(sizeof(struct dmac_xfer_state), M_DEVBUF, flags);
280 1.1 nisimura
281 1.1 nisimura dxs->dxs_xfer.dx_done = NULL;
282 1.1 nisimura dxs->dxs_xfer.dx_sync_bus = DMAC_SYNC_BUS_AUTO;
283 1.1 nisimura dxs->dxs_xfer.dx_xfer_mode = DMAC_XFER_MODE_DEMAND;
284 1.1 nisimura dxs->dxs_channel = DMAC_NO_CHANNEL;
285 1.1 nisimura
286 1.1 nisimura return ((dmac_xfer_t)dxs);
287 1.1 nisimura }
288 1.1 nisimura
289 1.1 nisimura void
290 1.1 nisimura s3c2440_dmac_free_xfer(dmac_xfer_t dx) {
291 1.1 nisimura free(dx, M_DEVBUF);
292 1.1 nisimura }
293 1.1 nisimura
294 1.1 nisimura int
295 1.1 nisimura s3c2440_dmac_start_xfer(dmac_xfer_t dx) {
296 1.1 nisimura struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
297 1.1 nisimura struct dmac_xfer_state *dxs = (struct dmac_xfer_state*)dx;
298 1.1 nisimura struct s3c2440_dmac_peripheral *perph;
299 1.1 nisimura int i;
300 1.1 nisimura bool transfer_started = FALSE;
301 1.1 nisimura
302 1.1 nisimura if (dxs->dxs_xfer.dx_peripheral != DMAC_PERIPH_NONE &&
303 1.1 nisimura dxs->dxs_xfer.dx_peripheral >= DMAC_N_PERIPH)
304 1.1 nisimura return EINVAL;
305 1.1 nisimura
306 1.1 nisimura dxs->dxs_complete = FALSE;
307 1.1 nisimura
308 1.1 nisimura perph = &s3c2440_peripherals[dxs->dxs_xfer.dx_peripheral];
309 1.1 nisimura #ifdef DIAGNOSTIC
310 1.1 nisimura DPRINTF(("dp_id: %d, dx_peripheral: %d\n", perph->dp_id, dxs->dxs_xfer.dx_peripheral));
311 1.1 nisimura KASSERT(perph->dp_id == dxs->dxs_xfer.dx_peripheral);
312 1.1 nisimura #endif
313 1.1 nisimura
314 1.1 nisimura mutex_enter(&sc->sc_mutex);
315 1.1 nisimura /* Get list of possible channels for this peripheral.
316 1.1 nisimura If none of the channels are ready to transmit, queue
317 1.1 nisimura the transfer in the one with the highest priority
318 1.1 nisimura (first in the order list).
319 1.1 nisimura */
320 1.1 nisimura for(i=0;
321 1.1 nisimura perph->dp_channel_order[i] != PERPH_LAST;
322 1.1 nisimura i++) {
323 1.1 nisimura u_int8_t channel_no = perph->dp_channel_order[i];
324 1.1 nisimura
325 1.1 nisimura #ifdef DIAGNOSTIC
326 1.1 nisimura /* Check that there is a mapping for the given channel.
327 1.1 nisimura If this fails, there is something wrong in
328 1.1 nisimura s3c2440_peripherals.
329 1.1 nisimura */
330 1.1 nisimura KASSERT(perph->dp_channel_source[channel_no] != PERPH_NA);
331 1.1 nisimura #endif
332 1.1 nisimura
333 1.1 nisimura if (sc->sc_channels[channel_no].dc_active == NULL) {
334 1.1 nisimura /* Transfer can start right away */
335 1.1 nisimura dmac_start(channel_no, dxs);
336 1.1 nisimura transfer_started = TRUE;
337 1.1 nisimura break;
338 1.1 nisimura }
339 1.1 nisimura }
340 1.1 nisimura
341 1.1 nisimura if (transfer_started == FALSE) {
342 1.1 nisimura u_int8_t channel_no = perph->dp_channel_order[0];
343 1.1 nisimura /* Enqueue the transfer, as none of the DMA channels were
344 1.1 nisimura available.
345 1.1 nisimura The highest priority channel is used.
346 1.1 nisimura */
347 1.1 nisimura dxs->dxs_channel = channel_no;
348 1.1 nisimura SIMPLEQ_INSERT_TAIL(&sc->sc_channels[channel_no].dc_queue, dxs, dxs_link);
349 1.1 nisimura DPRINTF(("Enqueued transfer on channel %d\n", channel_no));
350 1.1 nisimura }
351 1.1 nisimura
352 1.1 nisimura mutex_exit(&sc->sc_mutex);
353 1.1 nisimura
354 1.1 nisimura return 0;
355 1.1 nisimura }
356 1.1 nisimura
357 1.1 nisimura static void
358 1.1 nisimura dmac_start(u_int8_t channel_no, struct dmac_xfer_state *dxs) {
359 1.1 nisimura struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
360 1.1 nisimura struct s3c2440_dmac_channel *dc = &sc->sc_channels[channel_no];
361 1.1 nisimura uint32_t options;
362 1.1 nisimura #ifdef DIAGNOSTIC
363 1.1 nisimura uint32_t reg;
364 1.1 nisimura #endif
365 1.1 nisimura dmac_sync_bus_t sync_bus;
366 1.1 nisimura struct dmac_xfer *dx = &dxs->dxs_xfer;
367 1.1 nisimura
368 1.1 nisimura /* Must be called with sc_mutex locked */
369 1.1 nisimura
370 1.1 nisimura DPRINTF(("Starting DMA transfer (%p) on channel %d\n", dxs, channel_no));
371 1.1 nisimura
372 1.1 nisimura KASSERT(dc->dc_active == NULL);
373 1.1 nisimura
374 1.1 nisimura #ifdef DIAGNOSTIC
375 1.1 nisimura reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_STAT(channel_no));
376 1.1 nisimura if (reg & DMASTAT_BUSY)
377 1.1 nisimura panic("DMA channel is busy, cannot start new transfer!");
378 1.1 nisimura
379 1.1 nisimura #endif
380 1.1 nisimura
381 1.1 nisimura dc->dc_active = dxs;
382 1.1 nisimura dxs->dxs_channel = channel_no;
383 1.1 nisimura dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg = dx->dx_desc[DMAC_DESC_SRC].xd_dma_segs;
384 1.1 nisimura dxs->dxs_segs[DMAC_DESC_SRC].ds_nsegs = dx->dx_desc[DMAC_DESC_SRC].xd_nsegs;
385 1.1 nisimura dxs->dxs_segs[DMAC_DESC_DST].ds_curseg = dx->dx_desc[DMAC_DESC_DST].xd_dma_segs;
386 1.1 nisimura dxs->dxs_segs[DMAC_DESC_DST].ds_nsegs = dx->dx_desc[DMAC_DESC_DST].xd_nsegs;
387 1.1 nisimura
388 1.1 nisimura options = DMACON_INT_INT |
389 1.1 nisimura DMACON_RELOAD_NO_AUTO;
390 1.1 nisimura
391 1.1 nisimura if (dxs->dxs_xfer.dx_peripheral == DMAC_PERIPH_NONE) {
392 1.1 nisimura options |= DMACON_SERVMODE_WHOLE;
393 1.1 nisimura } else {
394 1.1 nisimura options |= DMACON_SERVMODE_SINGLE;
395 1.1 nisimura }
396 1.1 nisimura
397 1.1 nisimura switch (dxs->dxs_xfer.dx_xfer_mode) {
398 1.1 nisimura case DMAC_XFER_MODE_DEMAND:
399 1.1 nisimura options |= DMACON_DEMAND;
400 1.1 nisimura break;
401 1.1 nisimura case DMAC_XFER_MODE_HANDSHAKE:
402 1.1 nisimura options |= DMACON_HANDSHAKE;
403 1.1 nisimura break;
404 1.1 nisimura default:
405 1.1 nisimura panic("Unknown dx_xfer_mode");
406 1.1 nisimura }
407 1.1 nisimura
408 1.1 nisimura sync_bus = dxs->dxs_xfer.dx_sync_bus;
409 1.1 nisimura
410 1.1 nisimura switch (dxs->dxs_xfer.dx_xfer_width) {
411 1.1 nisimura case DMAC_XFER_WIDTH_8BIT:
412 1.1 nisimura DPRINTF(("8-Bit (BYTE) transfer width\n"));
413 1.1 nisimura options |= DMACON_DSZ_B;
414 1.1 nisimura dxs->dxs_width = 1;
415 1.1 nisimura break;
416 1.1 nisimura case DMAC_XFER_WIDTH_16BIT:
417 1.1 nisimura DPRINTF(("16-Bit (HALF-WORD) transfer width\n"));
418 1.1 nisimura options |= DMACON_DSZ_HW;
419 1.1 nisimura dxs->dxs_width = 2;
420 1.1 nisimura break;
421 1.1 nisimura case DMAC_XFER_WIDTH_32BIT:
422 1.1 nisimura DPRINTF(("32-Bit (WORD) transfer width\n"));
423 1.1 nisimura options |= DMACON_DSZ_W;
424 1.1 nisimura dxs->dxs_width = 4;
425 1.1 nisimura break;
426 1.1 nisimura default:
427 1.1 nisimura panic("Unknown transfer width");
428 1.1 nisimura }
429 1.1 nisimura
430 1.1 nisimura if (dxs->dxs_xfer.dx_peripheral == DMAC_PERIPH_NONE) {
431 1.1 nisimura options |= DMACON_SW_REQ;
432 1.1 nisimura if (sync_bus == DMAC_SYNC_BUS_AUTO)
433 1.1 nisimura sync_bus = DMAC_SYNC_BUS_SYSTEM;
434 1.1 nisimura } else {
435 1.1 nisimura uint8_t source = s3c2440_peripherals[dxs->dxs_xfer.dx_peripheral].dp_channel_source[channel_no];
436 1.1 nisimura DPRINTF(("Hw request source: %d, channel: %d\n", source, channel_no));
437 1.1 nisimura options |= DMACON_HW_REQ | DMACON_HW_SRCSEL(source);
438 1.1 nisimura if (sync_bus == DMAC_SYNC_BUS_AUTO)
439 1.1 nisimura sync_bus = DMAC_SYNC_BUS_PERIPHERAL;
440 1.1 nisimura }
441 1.1 nisimura
442 1.1 nisimura if (sync_bus == DMAC_SYNC_BUS_SYSTEM) {
443 1.1 nisimura DPRINTF(("Syncing with system bus\n"));
444 1.1 nisimura options |= DMACON_SYNC_AHB;
445 1.1 nisimura } else if (sync_bus == DMAC_SYNC_BUS_PERIPHERAL) {
446 1.1 nisimura DPRINTF(("Syncing with peripheral bus\n"));
447 1.1 nisimura options |= DMACON_SYNC_APB;
448 1.1 nisimura } else {
449 1.1 nisimura panic("No sync bus given");
450 1.1 nisimura }
451 1.1 nisimura
452 1.1 nisimura dxs->dxs_options = options;
453 1.1 nisimura
454 1.1 nisimura /* We have now configured the options that will hold for all segment transfers.
455 1.1 nisimura Next, we prepare and start the transfer for the first segment */
456 1.1 nisimura dmac_transfer_segment(channel_no, dxs);
457 1.1 nisimura
458 1.1 nisimura }
459 1.1 nisimura
460 1.1 nisimura static void
461 1.1 nisimura dmac_transfer_segment(uint8_t channel_no, struct dmac_xfer_state *dxs)
462 1.1 nisimura {
463 1.1 nisimura struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
464 1.1 nisimura /* struct s3c2440_dmac_channel *dc = &sc->sc_channels[channel_no];*/
465 1.1 nisimura uint32_t reg, transfer_size;
466 1.1 nisimura struct dmac_xfer *dx = &dxs->dxs_xfer;
467 1.1 nisimura
468 1.1 nisimura DPRINTF(("dmac_transfer_segment\n"));
469 1.1 nisimura
470 1.1 nisimura /* Prepare the source */
471 1.1 nisimura bus_space_write_4(sc->sc_iot, sc->sc_dmach,
472 1.1 nisimura DMA_DISRC(channel_no),
473 1.1 nisimura dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg->ds_addr);
474 1.1 nisimura
475 1.1 nisimura DPRINTF(("Source address: 0x%x\n", (unsigned)dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg->ds_addr));
476 1.1 nisimura DPRINTF(("Dest. address: 0x%x\n", (unsigned)dxs->dxs_segs[DMAC_DESC_DST].ds_curseg->ds_addr));
477 1.1 nisimura reg = 0;
478 1.1 nisimura if (dx->dx_desc[DMAC_DESC_SRC].xd_bus_type == DMAC_BUS_TYPE_PERIPHERAL) {
479 1.1 nisimura reg |= DISRCC_LOC_APB;
480 1.1 nisimura } else {
481 1.1 nisimura reg |= DISRCC_LOC_AHB;
482 1.1 nisimura }
483 1.1 nisimura if (dx->dx_desc[DMAC_DESC_SRC].xd_increment) {
484 1.1 nisimura reg |= DISRCC_INC_INC;
485 1.1 nisimura } else {
486 1.1 nisimura reg |= DISRCC_INC_FIXED;
487 1.1 nisimura }
488 1.1 nisimura bus_space_write_4(sc->sc_iot, sc->sc_dmach, DMA_DISRCC(channel_no), reg);
489 1.1 nisimura
490 1.1 nisimura /* Prepare the destination */
491 1.1 nisimura bus_space_write_4(sc->sc_iot, sc->sc_dmach,
492 1.1 nisimura DMA_DIDST(channel_no),
493 1.1 nisimura dxs->dxs_segs[DMAC_DESC_DST].ds_curseg->ds_addr);
494 1.1 nisimura reg = 0;
495 1.1 nisimura if (dx->dx_desc[DMAC_DESC_DST].xd_bus_type == DMAC_BUS_TYPE_PERIPHERAL) {
496 1.1 nisimura reg |= DIDSTC_LOC_APB;
497 1.1 nisimura } else {
498 1.1 nisimura reg |= DIDSTC_LOC_AHB;
499 1.1 nisimura }
500 1.1 nisimura if (dx->dx_desc[DMAC_DESC_DST].xd_increment) {
501 1.1 nisimura reg |= DIDSTC_INC_INC;
502 1.1 nisimura } else {
503 1.1 nisimura reg |= DIDSTC_INC_FIXED;
504 1.1 nisimura }
505 1.1 nisimura bus_space_write_4(sc->sc_iot, sc->sc_dmach, DMA_DIDSTC(channel_no), reg);
506 1.1 nisimura
507 1.1 nisimura /* Let the incrementing party decide how much data to transfer.
508 1.1 nisimura If both are incrementing, set the transfer size to the smallest one.
509 1.1 nisimura */
510 1.1 nisimura if (dx->dx_desc[DMAC_DESC_SRC].xd_increment) {
511 1.1 nisimura if (!dx->dx_desc[DMAC_DESC_DST].xd_increment) {
512 1.1 nisimura transfer_size = dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg->ds_len;
513 1.1 nisimura } else {
514 1.1 nisimura transfer_size = min(dxs->dxs_segs[DMAC_DESC_DST].ds_curseg->ds_len,
515 1.1 nisimura dxs->dxs_segs[DMAC_DESC_SRC].ds_curseg->ds_len);
516 1.1 nisimura }
517 1.1 nisimura } else {
518 1.1 nisimura if (dx->dx_desc[DMAC_DESC_DST].xd_increment) {
519 1.1 nisimura transfer_size = dxs->dxs_segs[DMAC_DESC_DST].ds_curseg->ds_len;
520 1.1 nisimura } else {
521 1.1 nisimura panic("S3C2440 DMA code does not support both source and destination being non-incrementing");
522 1.1 nisimura }
523 1.1 nisimura }
524 1.1 nisimura
525 1.1 nisimura /* Set options as prepared by dmac_start and add the transfer size.
526 1.1 nisimura If the transfer_size is not an even number of dxs_width,
527 1.1 nisimura ensure that all bytes are transferred by adding an extra transfer
528 1.1 nisimura of dxs_width.
529 1.1 nisimura */
530 1.1 nisimura bus_space_write_4(sc->sc_iot, sc->sc_dmach, DMA_CON(channel_no),
531 1.1 nisimura dxs->dxs_options |
532 1.1 nisimura DMACON_TC(((transfer_size/dxs->dxs_width)+
533 1.1 nisimura min((transfer_size % dxs->dxs_width), 1))));
534 1.1 nisimura
535 1.1 nisimura DPRINTF(("Transfer size: %d (%d)\n", transfer_size, transfer_size/dxs->dxs_width));
536 1.1 nisimura
537 1.1 nisimura /* Start the transfer */
538 1.1 nisimura reg = DMAMASKTRIG_ON;
539 1.1 nisimura if (dxs->dxs_xfer.dx_peripheral == DMAC_PERIPH_NONE) {
540 1.1 nisimura reg |= DMAMASKTRIG_SW_TRIG;
541 1.1 nisimura }
542 1.1 nisimura bus_space_write_4(sc->sc_iot, sc->sc_dmach, DMA_MASKTRIG(channel_no),
543 1.1 nisimura reg);
544 1.1 nisimura
545 1.1 nisimura #if defined(S3C2440_DMA_DEBUG)
546 1.1 nisimura reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_DISRC(channel_no));
547 1.1 nisimura printf("DMA_DISRC: 0x%X\n", reg);
548 1.1 nisimura
549 1.1 nisimura reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_DISRCC(channel_no));
550 1.1 nisimura printf("DMA_DISRCC: 0x%X\n", reg);
551 1.1 nisimura
552 1.1 nisimura reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_DIDST(channel_no));
553 1.1 nisimura printf("DMA_DIDST: 0x%X\n", reg);
554 1.1 nisimura
555 1.1 nisimura reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_DIDSTC(channel_no));
556 1.1 nisimura printf("DMA_DIDSTC: 0x%X\n", reg);
557 1.1 nisimura
558 1.1 nisimura reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_CON(channel_no));
559 1.1 nisimura printf("DMA_CON: 0x%X\n", reg);
560 1.1 nisimura
561 1.1 nisimura reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_MASKTRIG(channel_no));
562 1.1 nisimura printf("DMA_MASKTRIG: 0x%X\n", reg);
563 1.1 nisimura
564 1.1 nisimura reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach, DMA_STAT(channel_no));
565 1.1 nisimura printf("DMA_STAT: 0x%X\n", reg);
566 1.1 nisimura #endif
567 1.1 nisimura }
568 1.1 nisimura
569 1.1 nisimura static void
570 1.1 nisimura dmac_channel_done(uint8_t channel_no)
571 1.1 nisimura {
572 1.1 nisimura struct s3c2440_dmac_softc *sc;
573 1.1 nisimura struct s3c2440_dmac_channel *dc;
574 1.1 nisimura
575 1.1 nisimura sc = s3c2440_dmac_sc;
576 1.1 nisimura
577 1.1 nisimura /* sc->sc_mutex must be held when calling this function */
578 1.1 nisimura
579 1.1 nisimura dc = &sc->sc_channels[channel_no];
580 1.1 nisimura
581 1.1 nisimura dc->dc_active = NULL;
582 1.1 nisimura /* We deal with the queue before calling the
583 1.1 nisimura done callback, as it might start a new DMA
584 1.1 nisimura transfer.
585 1.1 nisimura */
586 1.1 nisimura if ( SIMPLEQ_EMPTY(&dc->dc_queue) ) {
587 1.1 nisimura DPRINTF(("DMA Queue empty for channel %d\n", channel_no));
588 1.1 nisimura } else {
589 1.1 nisimura /* There is a transfer in the queue. Start it*/
590 1.1 nisimura struct dmac_xfer_state *dxs;
591 1.1 nisimura DPRINTF(("Took a transfer from the queue\n"));
592 1.1 nisimura dxs = SIMPLEQ_FIRST(&dc->dc_queue);
593 1.1 nisimura SIMPLEQ_REMOVE_HEAD(&dc->dc_queue, dxs_link);
594 1.1 nisimura
595 1.1 nisimura dmac_start(channel_no, dxs);
596 1.1 nisimura }
597 1.1 nisimura }
598 1.1 nisimura
599 1.1 nisimura int
600 1.1 nisimura s3c2440_dmac_wait_xfer(dmac_xfer_t dx, int timeout) {
601 1.1 nisimura uint32_t complete;
602 1.1 nisimura int err = 0;
603 1.1 nisimura struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
604 1.1 nisimura struct dmac_xfer_state *dxs = (struct dmac_xfer_state*)dx;
605 1.1 nisimura
606 1.1 nisimura mutex_enter(&sc->sc_intr_mutex);
607 1.1 nisimura complete = dxs->dxs_complete;
608 1.1 nisimura while(complete == 0) {
609 1.1 nisimura int status;
610 1.1 nisimura DPRINTF(("s3c2440_dma_xfer_wait: Complete: %x\n", complete));
611 1.1 nisimura
612 1.1 nisimura if ( (status = cv_timedwait(&sc->sc_intr_cv,
613 1.1 nisimura &sc->sc_intr_mutex, timeout)) ==
614 1.1 nisimura EWOULDBLOCK ) {
615 1.1 nisimura DPRINTF(("s3c2440_dma_xfer_wait: Timed out\n"));
616 1.1 nisimura complete = 1;
617 1.1 nisimura err = ETIMEDOUT;
618 1.1 nisimura break;
619 1.1 nisimura }
620 1.1 nisimura
621 1.1 nisimura complete = dxs->dxs_complete;
622 1.1 nisimura }
623 1.1 nisimura
624 1.1 nisimura mutex_exit(&sc->sc_intr_mutex);
625 1.1 nisimura
626 1.1 nisimura #if 0
627 1.1 nisimura if (err == 0 && dxs->dxs_aborted == 1) {
628 1.1 nisimura /* Transfer was aborted */
629 1.1 nisimura err = EIO;
630 1.1 nisimura }
631 1.1 nisimura #endif
632 1.1 nisimura
633 1.1 nisimura return err;
634 1.1 nisimura }
635 1.1 nisimura
636 1.1 nisimura void
637 1.1 nisimura s3c2440_dmac_abort_xfer(dmac_xfer_t dx) {
638 1.1 nisimura struct s3c2440_dmac_softc *sc = s3c2440_dmac_sc;
639 1.1 nisimura struct dmac_xfer_state *dxs = (struct dmac_xfer_state*)dx;
640 1.1 nisimura struct s3c2440_dmac_channel *dc;
641 1.1 nisimura bool wait = FALSE;
642 1.1 nisimura
643 1.1 nisimura KASSERT(dxs->dxs_channel != (uint8_t)DMAC_NO_CHANNEL);
644 1.1 nisimura
645 1.1 nisimura dc = &sc->sc_channels[dxs->dxs_channel];
646 1.1 nisimura
647 1.1 nisimura mutex_enter(&sc->sc_mutex);
648 1.1 nisimura
649 1.1 nisimura if (dc->dc_active == dxs) {
650 1.1 nisimura uint32_t reg;
651 1.1 nisimura
652 1.1 nisimura bus_space_write_4(sc->sc_iot, sc->sc_dmach,
653 1.1 nisimura DMA_MASKTRIG(dxs->dxs_channel),
654 1.1 nisimura DMAMASKTRIG_STOP);
655 1.1 nisimura reg = bus_space_read_4(sc->sc_iot, sc->sc_dmach,
656 1.1 nisimura DMA_MASKTRIG(dxs->dxs_channel));
657 1.1 nisimura DPRINTF(("s3c2440_dma: channel %d mask trigger %x\n", dxs->dxs_channel, reg));
658 1.1 nisimura
659 1.1 nisimura if ( !(reg & DMAMASKTRIG_ON) ) {
660 1.1 nisimura DPRINTF(("No wait for abort"));
661 1.1 nisimura
662 1.1 nisimura /* The transfer was aborted and the interrupt
663 1.1 nisimura was thus not triggered. We need to cleanup the
664 1.1 nisimura channel here. */
665 1.1 nisimura dmac_channel_done(dxs->dxs_channel);
666 1.1 nisimura } else {
667 1.1 nisimura wait = TRUE;
668 1.1 nisimura }
669 1.1 nisimura } else {
670 1.1 nisimura /* Transfer is not active, simply remove it from the queue */
671 1.1 nisimura DPRINTF(("Removed transfer from queue\n"));
672 1.1 nisimura SIMPLEQ_REMOVE(&dc->dc_queue, dxs, dmac_xfer_state, dxs_link);
673 1.1 nisimura }
674 1.1 nisimura
675 1.1 nisimura mutex_exit(&sc->sc_mutex);
676 1.1 nisimura
677 1.1 nisimura if (wait == TRUE) {
678 1.1 nisimura DPRINTF(("Abort: Wait for transfer to complete\n"));
679 1.1 nisimura s3c2440_dmac_wait_xfer(dx, 0);
680 1.1 nisimura }
681 1.1 nisimura }
682