Home | History | Annotate | Line # | Download | only in imx
      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