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.5 thorpej #include <sys/kmem.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.2 skrll uint8_t dp_id; 82 1.2 skrll uint8_t dp_channel_order[DMAC_N_CHANNELS+1]; 83 1.1 nisimura #define PERPH_LAST DMAC_N_CHANNELS+1 84 1.2 skrll uint8_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.2 skrll static void dmac_start(uint8_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.4 chs s3c2440_dmac_allocate_xfer(void) { 277 1.1 nisimura struct dmac_xfer_state *dxs; 278 1.1 nisimura 279 1.5 thorpej dxs = kmem_alloc(sizeof(struct dmac_xfer_state), KM_SLEEP); 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.5 thorpej kmem_free(dx, sizeof(struct dmac_xfer_state)); 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.2 skrll uint8_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.2 skrll uint8_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.2 skrll dmac_start(uint8_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.3 riastrad transfer_size = uimin(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.3 riastrad uimin((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