1 /* $NetBSD: imx23_apbdma.c,v 1.10 2026/02/08 22:19:05 yurix Exp $ */ 2 3 /* 4 * Copyright (c) 2012 The NetBSD Foundation, Inc. 5 * All rights reserved. 6 * 7 * This code is derived from software contributed to The NetBSD Foundation 8 * by Petri Laakso. 9 * 10 * Redistribution and use in source and binary forms, with or without 11 * modification, are permitted provided that the following conditions 12 * are met: 13 * 1. Redistributions of source code must retain the above copyright 14 * notice, this list of conditions and the following disclaimer. 15 * 2. Redistributions in binary form must reproduce the above copyright 16 * notice, this list of conditions and the following disclaimer in the 17 * documentation and/or other materials provided with the distribution. 18 * 19 * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS 20 * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED 21 * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 22 * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS 23 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR 24 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF 25 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS 26 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN 27 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) 28 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE 29 * POSSIBILITY OF SUCH DAMAGE. 30 */ 31 32 #include <sys/param.h> 33 #include <sys/types.h> 34 #include <sys/bus.h> 35 #include <sys/device.h> 36 #include <sys/errno.h> 37 #include <sys/kmem.h> 38 #include <sys/systm.h> 39 40 #include <dev/fdt/fdtvar.h> 41 42 #include <arm/imx/imx23_apbdma.h> 43 #include <arm/imx/imx23_apbdmareg.h> 44 #include <arm/imx/imx23_apbhdmareg.h> 45 #include <arm/imx/imx23_apbxdmareg.h> 46 #include <arm/imx/imx23var.h> 47 48 /* DMA command control register bits. */ 49 #define APBDMA_CMD_XFER_COUNT __BITS(31, 16) 50 #define APBDMA_CMD_CMDPIOWORDS __BITS(15, 12) 51 #define APBDMA_CMD_RESERVED __BITS(11, 9) 52 #define APBDMA_CMD_HALTONTERMINATE __BIT(8) 53 #define APBDMA_CMD_WAIT4ENDCMD __BIT(7) 54 #define APBDMA_CMD_SEMAPHORE __BIT(6) 55 #define APBDMA_CMD_NANDWAIT4READY __BIT(5) 56 #define APBDMA_CMD_NANDLOCK __BIT(4) 57 #define APBDMA_CMD_IRQONCMPLT __BIT(3) 58 #define APBDMA_CMD_CHAIN __BIT(2) 59 #define APBDMA_CMD_COMMAND __BITS(1, 0) 60 61 #define APBDMA_CMD_XFER_MAX_BYTES 65535 62 63 /* DMA command types. */ 64 #define APBDMA_CMD_NO_DMA_XFER 0 65 #define APBDMA_CMD_DMA_WRITE 1 66 #define APBDMA_CMD_DMA_READ 2 67 #define APBDMA_CMD_DMA_SENSE 3 68 69 /* Flags. */ 70 #define F_APBH_DMA __BIT(0) 71 #define F_APBX_DMA __BIT(1) 72 73 /* Number of channels. */ 74 #define AHB_MAX_DMA_CHANNELS 16 75 76 /* Return codes for apbdma_intr_status() */ 77 #define DMA_IRQ_CMDCMPLT 0 78 #define DMA_IRQ_TERM 1 79 #define DMA_IRQ_BUS_ERROR 2 80 81 /* Supported number of PIO words */ 82 #define MAX_PIO_WORDS 3 83 84 /* Memory allocated for per-segment command buffer */ 85 #define IMX23_DMA_MAX_SEGMENTS 16 86 87 #define HW_APB_CHN_NXTCMDAR(base, channel) (base + (0x70 * channel)) 88 #define HW_APB_CHN_SEMA(base, channel) (base + (0x70 * channel)) 89 90 #define DMA_RD(sc, reg) \ 91 bus_space_read_4(sc->sc_iot, sc->sc_ioh, (reg)) 92 #define DMA_WR(sc, reg, val) \ 93 bus_space_write_4(sc->sc_iot, sc->sc_ioh, (reg), (val)) 94 95 #define APBDMA_SOFT_RST_LOOP 455 /* At least 1 us ... */ 96 97 struct imx23_apbdma_fdt_config { 98 u_int flags; 99 u_int num_channels; 100 }; 101 102 struct imx23_apbdma_command { 103 void *next; /* Physical address. */ 104 uint32_t control; 105 void *buffer; /* Physical address. */ 106 uint32_t pio_words[MAX_PIO_WORDS]; 107 }; 108 109 struct imx23_apbdma_softc; 110 struct imx23_apbdma_channel { 111 u_int chan_index; 112 void (*chan_cb)(void *); 113 void *chan_cbarg; 114 struct imx23_apbdma_softc *chan_parent; 115 bus_dmamap_t chan_dmamp; 116 bus_dma_segment_t chan_ds[1]; 117 bus_size_t chan_cmd_sz; 118 struct imx23_apbdma_command *chan_cmds; 119 }; 120 121 struct imx23_apbdma_softc { 122 device_t sc_dev; 123 bus_dma_tag_t sc_dmat; 124 bus_space_handle_t sc_ioh; 125 bus_space_tag_t sc_iot; 126 u_int flags; 127 struct imx23_apbdma_channel sc_chan[AHB_MAX_DMA_CHANNELS]; 128 }; 129 130 static int imx23_apbdma_match(device_t, cfdata_t, void *); 131 static void imx23_apbdma_attach(device_t, device_t, void *); 132 static void imx23_apbdma_reset(struct imx23_apbdma_softc *); 133 static void imx23_apbdma_init(struct imx23_apbdma_softc *); 134 static void * imx23_apbdma_fdt_acquire(device_t, const void *, size_t, 135 void (*)(void *), void *); 136 static void imx23_apbdma_fdt_release(device_t, void *); 137 static int imx23_apbdma_fdt_transfer(device_t, void *, 138 struct fdtbus_dma_req *); 139 static void imx23_apbdma_fdt_halt(device_t, void *); 140 static int imx23_apbdma_intr(void *); 141 static int imx23_apbdma_pio_transfer(struct imx23_apbdma_softc *sc, 142 struct imx23_apbdma_channel *chan, 143 struct fdtbus_dma_req *req); 144 static int imx23_apbdma_data_transfer(struct imx23_apbdma_softc *sc, 145 struct imx23_apbdma_channel *chan, 146 struct fdtbus_dma_req *req); 147 148 CFATTACH_DECL_NEW(imx23apbdma, sizeof(struct imx23_apbdma_softc), 149 imx23_apbdma_match, imx23_apbdma_attach, NULL, NULL); 150 151 static const struct fdtbus_dma_controller_func imx23_apbdma_fdt_dma_funcs = { 152 .acquire = imx23_apbdma_fdt_acquire, 153 .release = imx23_apbdma_fdt_release, 154 .transfer = imx23_apbdma_fdt_transfer, 155 .halt = imx23_apbdma_fdt_halt, 156 }; 157 158 static const struct imx23_apbdma_fdt_config imx23_apbh_config = { 159 .flags = F_APBH_DMA, 160 .num_channels = 8, 161 }; 162 static const struct imx23_apbdma_fdt_config imx23_apbx_config = { 163 .flags = F_APBX_DMA, 164 .num_channels = 16, 165 }; 166 167 static const struct device_compatible_entry compat_data[] = { 168 { .compat = "fsl,imx23-dma-apbh", .data = &imx23_apbh_config }, 169 { .compat = "fsl,imx23-dma-apbx", .data = &imx23_apbx_config }, 170 DEVICE_COMPAT_EOL 171 }; 172 173 static void * 174 imx23_apbdma_fdt_acquire(device_t dev, const void *data, size_t len, 175 void (*cb)(void *), void *cbarg) 176 { 177 struct imx23_apbdma_softc *sc = device_private(dev); 178 struct imx23_apbdma_channel *chan; 179 const uint32_t *specifier = data; 180 int rsegs; 181 182 /* get channel index */ 183 if (len != 4) { 184 return NULL; 185 } 186 const u_int chan_index = be32toh(specifier[0]); 187 188 chan = &sc->sc_chan[chan_index]; 189 190 chan->chan_cb = cb; 191 chan->chan_cbarg = cbarg; 192 193 /* Enable CMDCMPLT_IRQ. */ 194 DMA_WR(sc, HW_APB_CTRL1_SET, (1<<chan->chan_index)<<16); 195 196 /* Prepare DMA command buffers for channel */ 197 chan->chan_cmd_sz = IMX23_DMA_MAX_SEGMENTS * 198 sizeof(struct imx23_apbdma_command); 199 if (bus_dmamem_alloc(sc->sc_dmat, chan->chan_cmd_sz, PAGE_SIZE, 0, 200 chan->chan_ds, 1, &rsegs, BUS_DMA_WAITOK)) { 201 return NULL; 202 } 203 if (bus_dmamem_map(sc->sc_dmat, chan->chan_ds, rsegs, chan->chan_cmd_sz, 204 (void **)&chan->chan_cmds, BUS_DMA_WAITOK)) { 205 return NULL; 206 } 207 if (bus_dmamap_create(sc->sc_dmat, MAXPHYS, 1, MAXPHYS, 0, 208 BUS_DMA_WAITOK, &chan->chan_dmamp)) { 209 return NULL; 210 } 211 if (bus_dmamap_load(sc->sc_dmat, chan->chan_dmamp, chan->chan_cmds, 212 chan->chan_cmd_sz, NULL, BUS_DMA_WAITOK)) { 213 return NULL; 214 } 215 216 /* link next address field of commands */ 217 struct imx23_apbdma_command *pa_cmd = (void *) chan->chan_ds[0].ds_addr; 218 for (int i = 0; i < IMX23_DMA_MAX_SEGMENTS - 1; i++) { 219 chan->chan_cmds[i].next = &pa_cmd[i + 1].next; 220 } 221 222 return chan; 223 } 224 225 static void 226 imx23_apbdma_fdt_release(device_t dev, void *priv) 227 { 228 struct imx23_apbdma_softc *sc = device_private(dev); 229 struct imx23_apbdma_channel *chan = priv; 230 231 bus_dmamap_unload(sc->sc_dmat, chan->chan_dmamp); 232 bus_dmamap_destroy(sc->sc_dmat, chan->chan_dmamp); 233 bus_dmamem_unmap(sc->sc_dmat, chan->chan_cmds, chan->chan_cmd_sz); 234 bus_dmamem_free(sc->sc_dmat, chan->chan_ds, 1); 235 } 236 237 static int imx23_apbdma_pio_transfer(struct imx23_apbdma_softc *sc, 238 struct imx23_apbdma_channel *chan, 239 struct fdtbus_dma_req *req) 240 { 241 /* some sanity checks */ 242 if (req->dreq_nsegs != 0) { 243 return EINVAL; 244 } 245 if (req->dreq_datalen > MAX_PIO_WORDS) { 246 return EINVAL; 247 } 248 249 /* prepare command */ 250 chan->chan_cmds[0].control = __SHIFTIN(0, APBDMA_CMD_XFER_COUNT) | 251 __SHIFTIN(req->dreq_datalen, APBDMA_CMD_CMDPIOWORDS) | 252 __SHIFTIN(APBDMA_CMD_NO_DMA_XFER, APBDMA_CMD_COMMAND) | 253 APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_SEMAPHORE | 254 APBDMA_CMD_IRQONCMPLT; 255 if (req->dreq_block_irq) { 256 chan->chan_cmds[0].control |= APBDMA_CMD_WAIT4ENDCMD; 257 } 258 /* copy PIO words */ 259 uint32_t *pio_words = req->dreq_data; 260 for (int i = 0; i < req->dreq_datalen; i++) { 261 chan->chan_cmds[0].pio_words[i] = pio_words[i]; 262 } 263 264 return 0; 265 } 266 267 static int imx23_apbdma_data_transfer(struct imx23_apbdma_softc *sc, 268 struct imx23_apbdma_channel *chan, 269 struct fdtbus_dma_req *req) 270 { 271 /* some sanity checks */ 272 if (req->dreq_nsegs < 0 || req->dreq_nsegs > IMX23_DMA_MAX_SEGMENTS) { 273 return EINVAL; 274 } 275 for (int i = 0; i < req->dreq_nsegs; i++) { 276 if (req->dreq_segs[i].ds_len > APBDMA_CMD_XFER_MAX_BYTES) { 277 return EINVAL; 278 } 279 } 280 if (req->dreq_datalen > MAX_PIO_WORDS) { 281 return EINVAL; 282 } 283 284 /* The fdt subsystem and the imx23 documentation use opposite naming */ 285 uint32_t dma_dir; 286 if (req->dreq_dir == FDT_DMA_WRITE) { 287 dma_dir = __SHIFTIN(APBDMA_CMD_DMA_READ, APBDMA_CMD_COMMAND); 288 } else { 289 dma_dir = __SHIFTIN(APBDMA_CMD_DMA_WRITE, APBDMA_CMD_COMMAND); 290 } 291 292 /* prepare commands */ 293 for (int i = 0; i < req->dreq_nsegs; i++) { 294 chan->chan_cmds[i].buffer = (void *)req->dreq_segs[i].ds_addr; 295 chan->chan_cmds[i].control = 296 __SHIFTIN(req->dreq_segs[i].ds_len, APBDMA_CMD_XFER_COUNT) | 297 __SHIFTIN(0, APBDMA_CMD_CMDPIOWORDS) | dma_dir | 298 APBDMA_CMD_HALTONTERMINATE | APBDMA_CMD_CHAIN; 299 } 300 301 /* special case: first command does PIO */ 302 uint32_t *pio_words = req->dreq_data; 303 for (int i = 0; i < req->dreq_datalen; i++) { 304 chan->chan_cmds[0].pio_words[i] = pio_words[i]; 305 } 306 chan->chan_cmds[0].control &= ~APBDMA_CMD_CMDPIOWORDS; 307 chan->chan_cmds[0].control |= 308 __SHIFTIN(req->dreq_datalen, APBDMA_CMD_CMDPIOWORDS); 309 310 /* special case: last command needs to raise interrupt */ 311 int tail_index = req->dreq_nsegs - 1; 312 chan->chan_cmds[tail_index].control &= ~APBDMA_CMD_CHAIN; 313 chan->chan_cmds[tail_index].control |= 314 APBDMA_CMD_SEMAPHORE | APBDMA_CMD_IRQONCMPLT; 315 if (req->dreq_block_irq) { 316 chan->chan_cmds[tail_index].control |= APBDMA_CMD_WAIT4ENDCMD; 317 } 318 319 return 0; 320 } 321 322 static int 323 imx23_apbdma_fdt_transfer(device_t dev, void *priv, struct fdtbus_dma_req *req) 324 { 325 struct imx23_apbdma_softc *sc = device_private(dev); 326 struct imx23_apbdma_channel *chan = priv; 327 int error = 0; 328 uint32_t reg; 329 uint8_t val; 330 331 if (req->dreq_dir == FDT_DMA_NO_XFER) { 332 error = imx23_apbdma_pio_transfer(sc, chan, req); 333 if (error) 334 return error; 335 } else { 336 error = imx23_apbdma_data_transfer(sc, chan, req); 337 if (error) 338 return error; 339 } 340 341 bus_dmamap_sync(sc->sc_dmat, chan->chan_dmamp, 0, chan->chan_cmd_sz, 342 BUS_DMASYNC_PREWRITE); 343 344 /* set the address of the dma command */ 345 if (sc->flags & F_APBH_DMA) 346 reg = 347 HW_APB_CHN_NXTCMDAR(HW_APBH_CH0_NXTCMDAR, chan->chan_index); 348 else 349 reg = 350 HW_APB_CHN_NXTCMDAR(HW_APBX_CH0_NXTCMDAR, chan->chan_index); 351 DMA_WR(sc, reg, chan->chan_ds[0].ds_addr); 352 353 /* increase semaphore to start dma transfer */ 354 if (sc->flags & F_APBH_DMA) { 355 reg = HW_APB_CHN_SEMA(HW_APBH_CH0_SEMA, chan->chan_index); 356 val = __SHIFTIN(1, HW_APBH_CH0_SEMA_INCREMENT_SEMA); 357 } else { 358 reg = HW_APB_CHN_SEMA(HW_APBX_CH0_SEMA, chan->chan_index); 359 val = __SHIFTIN(1, HW_APBX_CH0_SEMA_INCREMENT_SEMA); 360 } 361 DMA_WR(sc, reg, val); 362 363 return 0; 364 } 365 366 static void 367 imx23_apbdma_fdt_halt(device_t dev, void *priv) 368 { 369 /* do nothing */ 370 } 371 372 static int 373 imx23_apbdma_intr(void *frame) 374 { 375 struct imx23_apbdma_channel *chan = frame; 376 struct imx23_apbdma_softc *sc = chan->chan_parent; 377 unsigned int reason = 0; 378 379 /* Check if this was command complete IRQ. */ 380 if (DMA_RD(sc, HW_APB_CTRL1) & (1<<chan->chan_index)) 381 reason = DMA_IRQ_CMDCMPLT; 382 383 /* Check if error was set. */ 384 if (DMA_RD(sc, HW_APB_CTRL2) & (1<<chan->chan_index)) { 385 if (DMA_RD(sc, HW_APB_CTRL2) & (1<<chan->chan_index)<<16) 386 reason = DMA_IRQ_BUS_ERROR; 387 else 388 reason = DMA_IRQ_TERM; 389 } 390 391 if (reason) { 392 /* acknowledge error */ 393 DMA_WR(sc, HW_APB_CTRL2_CLR, (1<<chan->chan_index)); 394 } else { 395 /* acknowledge completion */ 396 if (sc->flags & F_APBH_DMA) { 397 DMA_WR(sc, HW_APB_CTRL1_CLR, (1<<chan->chan_index)); 398 } else { 399 DMA_WR(sc, HW_APB_CTRL1_CLR, (1<<chan->chan_index)); 400 } 401 } 402 403 bus_dmamap_sync(sc->sc_dmat, chan->chan_dmamp, 0, chan->chan_cmd_sz, 404 BUS_DMASYNC_POSTWRITE); 405 406 if (chan->chan_cb != NULL) { 407 chan->chan_cb(chan->chan_cbarg); 408 } 409 410 if (reason == DMA_IRQ_TERM) { 411 /* reset the dma channel */ 412 if (sc->flags & F_APBH_DMA) { 413 DMA_WR(sc, HW_APB_CTRL0_SET, 414 __SHIFTIN((1 << chan->chan_index), 415 HW_APBH_CTRL0_RESET_CHANNEL)); 416 while (DMA_RD(sc, HW_APB_CTRL0) & HW_APBH_CTRL0_RESET_CHANNEL) 417 continue; 418 } else { 419 DMA_WR(sc, HW_APBX_CHANNEL_CTRL_SET, 420 __SHIFTIN((1 << chan->chan_index), 421 HW_APBH_CTRL0_RESET_CHANNEL)); 422 while (DMA_RD(sc, HW_APBX_CHANNEL_CTRL) & (1 << chan->chan_index)) 423 continue; 424 } 425 } 426 427 return 1; 428 } 429 430 static int 431 imx23_apbdma_match(device_t parent, cfdata_t match, void *aux) 432 { 433 struct fdt_attach_args *const faa = aux; 434 435 return of_compatible_match(faa->faa_phandle, compat_data); 436 } 437 438 static void 439 imx23_apbdma_attach(device_t parent, device_t self, void *aux) 440 { 441 struct imx23_apbdma_softc *const sc = device_private(self); 442 struct fdt_attach_args *const faa = aux; 443 const int phandle = faa->faa_phandle; 444 const struct imx23_apbdma_fdt_config *config; 445 int len; 446 char intrstr[128]; 447 448 // find out if we are for APBH or APBX 449 config = of_compatible_lookup(phandle, compat_data)->data; 450 sc->flags = config->flags; 451 452 sc->sc_dev = self; 453 sc->sc_iot = faa->faa_bst; 454 sc->sc_dmat = faa->faa_dmat; 455 456 /* Map control registers */ 457 bus_addr_t addr; 458 bus_size_t size; 459 if (fdtbus_get_reg(phandle, 0, &addr, &size) != 0) { 460 aprint_error(": couldn't get register address\n"); 461 return; 462 } 463 if (bus_space_map(faa->faa_bst, addr, size, 0, &sc->sc_ioh)) { 464 aprint_error(": couldn't map registers\n"); 465 return; 466 } 467 468 /* establish interrupts */ 469 const u_int *specifier = fdtbus_get_prop(phandle, "interrupts", &len); 470 KASSERT(len == sizeof(u_int)*config->num_channels); 471 for (int i = 0; i < config->num_channels; i++) { 472 u_int irq = be32toh(specifier[i]); 473 474 /* irq = 0 means channel is unused*/ 475 if (irq == 0) 476 continue; 477 478 /* Some channels share an irq. Only establish it once. */ 479 bool skip = false; 480 for (int j = 0; j<i;j++) { 481 if (be32toh(specifier[j]) == irq) { 482 skip = true; 483 break; 484 } 485 } 486 if (skip) { 487 continue; 488 } 489 490 if (!fdtbus_intr_str(phandle, i, intrstr, sizeof(intrstr))) { 491 aprint_error(": failed to decode interrupt\n"); 492 return; 493 } 494 void *ih = fdtbus_intr_establish_xname( 495 phandle, i, IPL_SCHED, FDT_INTR_MPSAFE, imx23_apbdma_intr, 496 &sc->sc_chan[i], device_xname(self)); 497 if (ih == NULL) { 498 aprint_error_dev( 499 self, ": couldn't install interrupt handler %s\n", 500 intrstr); 501 return; 502 } 503 } 504 505 /* initialize our controller */ 506 for (int i = 0; i < AHB_MAX_DMA_CHANNELS; i++) { 507 sc->sc_chan[i].chan_index = i; 508 sc->sc_chan[i].chan_parent = sc; 509 } 510 imx23_apbdma_reset(sc); 511 imx23_apbdma_init(sc); 512 513 fdtbus_register_dma_controller(self, phandle, 514 &imx23_apbdma_fdt_dma_funcs); 515 516 if (sc->flags & F_APBH_DMA) { 517 aprint_normal(": type=apbh\n"); 518 } else if (sc->flags & F_APBX_DMA) { 519 aprint_normal(": type=apbx\n"); 520 } 521 } 522 523 /* 524 * Reset the APB{H,X}DMA block. 525 * 526 * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block" 527 */ 528 void 529 imx23_apbdma_reset(struct imx23_apbdma_softc *sc) 530 { 531 unsigned int loop; 532 533 /* 534 * Prepare for soft-reset by making sure that SFTRST is not currently 535 * asserted. Also clear CLKGATE so we can wait for its assertion below. 536 */ 537 DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST); 538 539 /* Wait at least a microsecond for SFTRST to deassert. */ 540 loop = 0; 541 while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) || 542 (loop < APBDMA_SOFT_RST_LOOP)) 543 loop++; 544 545 /* Clear CLKGATE so we can wait for its assertion below. */ 546 DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE); 547 548 /* Soft-reset the block. */ 549 DMA_WR(sc, HW_APB_CTRL0_SET, HW_APB_CTRL0_SFTRST); 550 551 /* Wait until clock is in the gated state. */ 552 while (!(DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE)) 553 continue; 554 555 /* Bring block out of reset. */ 556 DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST); 557 558 loop = 0; 559 while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) || 560 (loop < APBDMA_SOFT_RST_LOOP)) 561 loop++; 562 563 DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE); 564 565 /* Wait until clock is in the NON-gated state. */ 566 while (DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE) 567 continue; 568 } 569 570 /* 571 * Initialize APB{H,X}DMA block. 572 */ 573 static void 574 imx23_apbdma_init(struct imx23_apbdma_softc *sc) 575 { 576 577 if (sc->flags & F_APBH_DMA) { 578 DMA_WR(sc, HW_APBH_CTRL0_SET, HW_APBH_CTRL0_AHB_BURST8_EN); 579 DMA_WR(sc, HW_APBH_CTRL0_SET, HW_APBH_CTRL0_APB_BURST4_EN); 580 } 581 } 582