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