Home | History | Annotate | Line # | Download | only in imx
imx23_apbdma.c revision 1.2.6.3
      1 /* $Id: imx23_apbdma.c,v 1.2.6.3 2013/06/23 06:20:00 tls 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/mutex.h>
     38 #include <sys/kmem.h>
     39 #include <sys/systm.h>
     40 
     41 #include <arm/imx/imx23_apbdma.h>
     42 #include <arm/imx/imx23_apbdmareg.h>
     43 #include <arm/imx/imx23_apbdmavar.h>
     44 #include <arm/imx/imx23_apbhdmareg.h>
     45 #include <arm/imx/imx23_apbxdmareg.h>
     46 #include <arm/imx/imx23var.h>
     47 
     48 static int	apbdma_match(device_t, cfdata_t, void *);
     49 static void	apbdma_attach(device_t, device_t, void *);
     50 static int	apbdma_activate(device_t, enum devact);
     51 
     52 CFATTACH_DECL3_NEW(apbdma,
     53 	sizeof(struct apbdma_softc),
     54 	apbdma_match,
     55 	apbdma_attach,
     56 	NULL,
     57 	apbdma_activate,
     58 	NULL,
     59 	NULL,
     60 	0);
     61 
     62 static void	apbdma_reset(struct apbdma_softc *);
     63 static void	apbdma_init(struct apbdma_softc *);
     64 
     65 #define DMA_RD(sc, reg)							\
     66 		bus_space_read_4(sc->sc_iot, sc->sc_ioh, (reg))
     67 #define DMA_WR(sc, reg, val)						\
     68 		bus_space_write_4(sc->sc_iot, sc->sc_ioh, (reg), (val))
     69 
     70 #define APBDMA_SOFT_RST_LOOP 455 /* At least 1 us ... */
     71 
     72 static int
     73 apbdma_match(device_t parent, cfdata_t match, void *aux)
     74 {
     75 	struct apb_attach_args *aa = aux;
     76 
     77 	if (aa->aa_addr == HW_APBHDMA_BASE && aa->aa_size == HW_APBHDMA_SIZE)
     78 			return 1;
     79 
     80 	if (aa->aa_addr == HW_APBXDMA_BASE && aa->aa_size == HW_APBXDMA_SIZE)
     81 			return 1;
     82 
     83 	return 0;
     84 }
     85 
     86 static void
     87 apbdma_attach(device_t parent, device_t self, void *aux)
     88 {
     89 	struct apb_attach_args *aa = aux;
     90 	struct apbdma_softc *sc = device_private(self);
     91 	struct apb_softc *sc_parent = device_private(parent);
     92 	static u_int apbdma_attached = 0;
     93 
     94 	if ((strncmp(device_xname(parent), "apbh", 4) == 0) &&
     95 	    (apbdma_attached & F_AHBH_DMA))
     96 		return;
     97 	if ((strncmp(device_xname(parent), "apbx", 4) == 0) &&
     98 	    (apbdma_attached & F_AHBX_DMA))
     99 		return;
    100 
    101 	sc->sc_dev = self;
    102 	sc->sc_iot = aa->aa_iot;
    103 	sc->sc_dmat = aa->aa_dmat;
    104 
    105 	if (bus_space_map(sc->sc_iot,
    106 	    aa->aa_addr, aa->aa_size, 0, &sc->sc_ioh)) {
    107 		aprint_error_dev(sc->sc_dev, "unable to map bus space\n");
    108 		return;
    109 	}
    110 
    111 	if (strncmp(device_xname(parent), "apbh", 4) == 0)
    112 		sc->flags = F_AHBH_DMA;
    113 
    114 	if (strncmp(device_xname(parent), "apbx", 4) == 0)
    115 		sc->flags = F_AHBX_DMA;
    116 
    117 	apbdma_reset(sc);
    118 	apbdma_init(sc);
    119 
    120 	if (sc->flags & F_AHBH_DMA)
    121 		apbdma_attached |= F_AHBH_DMA;
    122 	if (sc->flags & F_AHBX_DMA)
    123 		apbdma_attached |= F_AHBX_DMA;
    124 
    125 	sc_parent->dmac = self;
    126 
    127 	/* Initialize mutex to control concurrent access from the drivers. */
    128 	mutex_init(&sc->sc_lock, MUTEX_DEFAULT, IPL_HIGH);
    129 
    130 	aprint_normal("\n");
    131 
    132 	return;
    133 }
    134 
    135 static int
    136 apbdma_activate(device_t self, enum devact act)
    137 {
    138 	return EOPNOTSUPP;
    139 }
    140 
    141 /*
    142  * Reset the APB{H,X}DMA block.
    143  *
    144  * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block"
    145  */
    146 static void
    147 apbdma_reset(struct apbdma_softc *sc)
    148 {
    149 	unsigned int loop;
    150 
    151 	/*
    152 	 * Prepare for soft-reset by making sure that SFTRST is not currently
    153 	 * asserted. Also clear CLKGATE so we can wait for its assertion below.
    154 	 */
    155 	DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
    156 
    157 	/* Wait at least a microsecond for SFTRST to deassert. */
    158 	loop = 0;
    159 	while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
    160 	    (loop < APBDMA_SOFT_RST_LOOP))
    161 		loop++;
    162 
    163 	/* Clear CLKGATE so we can wait for its assertion below. */
    164 	DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
    165 
    166 	/* Soft-reset the block. */
    167 	DMA_WR(sc, HW_APB_CTRL0_SET, HW_APB_CTRL0_SFTRST);
    168 
    169 	/* Wait until clock is in the gated state. */
    170 	while (!(DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE));
    171 
    172 	/* Bring block out of reset. */
    173 	DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST);
    174 
    175 	loop = 0;
    176 	while ((DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) ||
    177 	    (loop < APBDMA_SOFT_RST_LOOP))
    178 		loop++;
    179 
    180 	DMA_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE);
    181 
    182 	/* Wait until clock is in the NON-gated state. */
    183 	while (DMA_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE);
    184 
    185 	return;
    186 }
    187 
    188 /*
    189  * Initialize APB{H,X}DMA block.
    190  */
    191 static void
    192 apbdma_init(struct apbdma_softc *sc)
    193 {
    194 
    195 	if (sc->flags & F_AHBH_DMA) {
    196 		DMA_WR(sc, HW_APBH_CTRL0_SET, HW_APBH_CTRL0_AHB_BURST8_EN);
    197 		DMA_WR(sc, HW_APBH_CTRL0_SET, HW_APBH_CTRL0_APB_BURST4_EN);
    198 	}
    199 	return;
    200 }
    201 
    202 /*
    203  * Chain DMA commands together.
    204  *
    205  * Set src->next point to trg's physical DMA mapped address.
    206  */
    207 void
    208 apbdma_cmd_chain(apbdma_command_t src, apbdma_command_t trg, void *buf,
    209     bus_dmamap_t dmap)
    210 {
    211 	int i;
    212 	bus_size_t daddr;
    213 	bus_addr_t trg_offset;
    214 
    215 	trg_offset = (bus_addr_t)trg - (bus_addr_t)buf;
    216 	daddr = 0;
    217 
    218 	for (i = 0; i < dmap->dm_nsegs; i++) {
    219 		daddr += dmap->dm_segs[i].ds_len;
    220 		if (trg_offset < daddr) {
    221 			src->next = (void *)(dmap->dm_segs[i].ds_addr +
    222 			    (trg_offset - (daddr - dmap->dm_segs[i].ds_len)));
    223 			break;
    224 		}
    225 	}
    226 
    227 	return;
    228 }
    229 
    230 /*
    231  * Set DMA command buffer.
    232  *
    233  * Set cmd->buffer point to physical DMA address at offset in DMA map.
    234  */
    235 void
    236 apbdma_cmd_buf(apbdma_command_t cmd, bus_addr_t offset, bus_dmamap_t dmap)
    237 {
    238 	int i;
    239 	bus_size_t daddr;
    240 
    241 	daddr = 0;
    242 
    243 	for (i = 0; i < dmap->dm_nsegs; i++) {
    244 		daddr += dmap->dm_segs[i].ds_len;
    245 		if (offset < daddr) {
    246 			cmd->buffer = (void *)(dmap->dm_segs[i].ds_addr +
    247 			    (offset - (daddr - dmap->dm_segs[i].ds_len)));
    248 			break;
    249 		}
    250 	}
    251 
    252 	return;
    253 }
    254 
    255 /*
    256  * Initialize DMA channel.
    257  */
    258 void
    259 apbdma_chan_init(struct apbdma_softc *sc, unsigned int channel)
    260 {
    261 
    262 	mutex_enter(&sc->sc_lock);
    263 
    264 	/* Enable CMDCMPLT_IRQ. */
    265 	DMA_WR(sc, HW_APB_CTRL1_SET, (1<<channel)<<16);
    266 
    267 	mutex_exit(&sc->sc_lock);
    268 
    269 	return;
    270 }
    271 
    272 /*
    273  * Set command chain for DMA channel.
    274  */
    275 #define HW_APB_CHN_NXTCMDAR(base, channel)	(base + (0x70 * channel))
    276 void
    277 apbdma_chan_set_chain(struct apbdma_softc *sc, unsigned int channel,
    278 	bus_dmamap_t dmap)
    279 {
    280 	uint32_t reg;
    281 
    282 	if (sc->flags & F_AHBH_DMA)
    283 		reg = HW_APB_CHN_NXTCMDAR(HW_APBH_CH0_NXTCMDAR, channel);
    284 	else
    285 		reg = HW_APB_CHN_NXTCMDAR(HW_APBX_CH0_NXTCMDAR, channel);
    286 
    287 	mutex_enter(&sc->sc_lock);
    288 	DMA_WR(sc, reg, dmap->dm_segs[0].ds_addr);
    289 	mutex_exit(&sc->sc_lock);
    290 
    291 	return;
    292 }
    293 
    294 /*
    295  * Initiate DMA transfer.
    296  */
    297 #define HW_APB_CHN_SEMA(base, channel)	(base + (0x70 * channel))
    298 void
    299 apbdma_run(struct apbdma_softc *sc, unsigned int channel)
    300 {
    301 	uint32_t reg;
    302 	uint8_t val;
    303 
    304 	if (sc->flags & F_AHBH_DMA) {
    305 		reg = HW_APB_CHN_SEMA(HW_APBH_CH0_SEMA, channel);
    306 		val = __SHIFTIN(1, HW_APBH_CH0_SEMA_INCREMENT_SEMA);
    307 	 } else {
    308 		reg = HW_APB_CHN_SEMA(HW_APBX_CH0_SEMA, channel);
    309 		val = __SHIFTIN(1, HW_APBX_CH0_SEMA_INCREMENT_SEMA);
    310 	}
    311 
    312 	mutex_enter(&sc->sc_lock);
    313 	DMA_WR(sc, reg, val);
    314 	mutex_exit(&sc->sc_lock);
    315 
    316 	return;
    317 }
    318 
    319 /*
    320  * Acknowledge command complete IRQ.
    321  */
    322 void
    323 apbdma_ack_intr(struct apbdma_softc *sc, unsigned int channel)
    324 {
    325 
    326 	mutex_enter(&sc->sc_lock);
    327 	DMA_WR(sc, HW_APB_CTRL1_CLR, (1<<channel));
    328 	mutex_exit(&sc->sc_lock);
    329 
    330 	return;
    331 }
    332 
    333 /*
    334  * Acknowledge error IRQ.
    335  */
    336 void
    337 apbdma_ack_error_intr(struct apbdma_softc *sc, unsigned int channel)
    338 {
    339 
    340 	mutex_enter(&sc->sc_lock);
    341 	DMA_WR(sc, HW_APB_CTRL2_CLR, (1<<channel));
    342 	mutex_exit(&sc->sc_lock);
    343 
    344 	return;
    345 }
    346 
    347 /*
    348  * Return reason for the IRQ.
    349  */
    350 unsigned int
    351 apbdma_intr_status(struct apbdma_softc *sc, unsigned int channel)
    352 {
    353 	unsigned int reason;
    354 
    355 	reason = 0;
    356 
    357 	mutex_enter(&sc->sc_lock);
    358 
    359 	/* Check if this was command complete IRQ. */
    360 	if (DMA_RD(sc, HW_APB_CTRL1) & (1<<channel))
    361 		reason = DMA_IRQ_CMDCMPLT;
    362 
    363 	/* Check if error was set. */
    364 	if (DMA_RD(sc, HW_APB_CTRL2) & (1<<channel)) {
    365 		if (DMA_RD(sc, HW_APB_CTRL2) & (1<<channel)<<16)
    366 			reason = DMA_IRQ_BUS_ERROR;
    367 		else
    368 			reason = DMA_IRQ_TERM;
    369 	}
    370 
    371 	mutex_exit(&sc->sc_lock);
    372 
    373 	return reason;
    374 }
    375 
    376 /*
    377  * Reset DMA channel.
    378  * Use only for devices on APBH bus.
    379  */
    380 void
    381 apbdma_chan_reset(struct apbdma_softc *sc, unsigned int channel)
    382 {
    383 
    384 	mutex_enter(&sc->sc_lock);
    385 
    386 	DMA_WR(sc, HW_APB_CTRL0_SET,
    387 	    __SHIFTIN((1<<channel), HW_APBH_CTRL0_RESET_CHANNEL));
    388 	while(DMA_RD(sc, HW_APB_CTRL0) & HW_APBH_CTRL0_RESET_CHANNEL);
    389 
    390 	mutex_exit(&sc->sc_lock);
    391 
    392 	return;
    393 }
    394