1 /* $Id: imx23_apbdma.c,v 1.2 2012/12/16 19:40:00 jkunz 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/bus.h> 34 #include <sys/device.h> 35 #include <sys/errno.h> 36 #include <sys/kmem.h> 37 #include <sys/queue.h> 38 #include <sys/systm.h> 39 40 #include <arm/imx/imx23_apbdmareg.h> 41 #include <arm/imx/imx23_apbhdmareg.h> 42 #include <arm/imx/imx23_apbxdmareg.h> 43 #include <arm/imx/imx23_apbdma.h> 44 #include <arm/imx/imx23var.h> 45 46 static int apbdma_match(device_t, cfdata_t, void *); 47 static void apbdma_attach(device_t, device_t, void *); 48 static int apbdma_activate(device_t, enum devact); 49 50 #define APBDMA_SOFT_RST_LOOP 455 /* At least 1 us ... */ 51 #define DMACTRL_RD(sc, reg) \ 52 bus_space_read_4(sc->sc_iot, sc->sc_hdl, (reg)) 53 #define DMACTRL_WR(sc, reg, val) \ 54 bus_space_write_4(sc->sc_iot, sc->sc_hdl, (reg), (val)) 55 56 struct apbdma_softc { 57 device_t sc_dev; 58 bus_space_tag_t sc_iot; 59 bus_space_handle_t sc_hdl; 60 bus_dma_tag_t sc_dmat; 61 bus_dmamap_t sc_dmamp; 62 struct imx23_dma_channel *sc_channel; 63 int n_channel; 64 }; 65 66 struct imx23_dma_cmd { 67 uint32_t next_cmd; 68 uint32_t cmd; 69 uint32_t buffer; 70 uint32_t pio[CMDPIOWORDS_MAX]; 71 SIMPLEQ_ENTRY(imx23_dma_cmd) entries; 72 }; 73 74 struct imx23_dma_channel { 75 SIMPLEQ_HEAD(simplehead, imx23_dma_cmd) head; 76 struct simplehead *headp; 77 struct apbdma_softc *sc; 78 }; 79 80 CFATTACH_DECL3_NEW(apbdma, 81 sizeof(struct apbdma_softc), 82 apbdma_match, 83 apbdma_attach, 84 NULL, 85 apbdma_activate, 86 NULL, 87 NULL, 88 0); 89 90 static void apbdma_reset(struct apbdma_softc *); 91 92 static int 93 apbdma_match(device_t parent, cfdata_t match, void *aux) 94 { 95 struct apb_attach_args *aa = aux; 96 97 if (aa->aa_addr == HW_APBHDMA_BASE && aa->aa_size == HW_APBHDMA_SIZE) 98 return 1; 99 100 if (aa->aa_addr == HW_APBXDMA_BASE && aa->aa_size == HW_APBXDMA_SIZE) 101 return 1; 102 103 return 0; 104 } 105 106 static void 107 apbdma_attach(device_t parent, device_t self, void *aux) 108 { 109 struct apb_attach_args *aa = aux; 110 struct apbdma_softc *sc = device_private(self); 111 //struct apb_softc *scp = device_private(parent); 112 113 // static int apbdma_attached = 0; 114 // struct imx23_dma_channel *chan; 115 // int i; 116 int error; 117 118 // if (apbdma_attached) 119 // return; 120 121 sc->sc_dev = self; 122 sc->sc_iot = aa->aa_iot; 123 sc->sc_dmat = aa->aa_dmat; 124 125 /* 126 * Parent bus softc has a pointer to DMA controller device_t for 127 * specific bus. As different busses need different instances of the 128 * DMA driver. The apb_softc.dmac is set up here. Now device drivers 129 * which use DMA can pass apb_softc.dmac from their parent to apbdma 130 * functions. 131 */ 132 if (bus_space_map(sc->sc_iot, 133 aa->aa_addr, aa->aa_size, 0, &(sc->sc_hdl))) { 134 aprint_error_dev(sc->sc_dev, "unable to map bus space\n"); 135 return; 136 } 137 138 error = bus_dmamap_create(sc->sc_dmat, PAGE_SIZE, 1, 139 PAGE_SIZE, 0, BUS_DMA_NOWAIT|BUS_DMA_ALLOCNOW, &sc->sc_dmamp); 140 if (error) { 141 aprint_error_dev(sc->sc_dev, 142 "couldn't create dma map. (error=%d)\n", error); 143 return; 144 } 145 #ifdef notyet 146 if (aa->aa_addr == HW_APBHDMA_BASE && aa->aa_size == HW_APBHDMA_SIZE) { 147 sc->sc_channel = kmem_alloc(sizeof(struct imx23_dma_channel) 148 * APBH_DMA_N_CHANNELS, KM_SLEEP); 149 sc->n_channel = APBH_DMA_N_CHANNELS; 150 } 151 152 if (aa->aa_addr == HW_APBXDMA_BASE && aa->aa_size == HW_APBXDMA_SIZE) { 153 sc->sc_channel = kmem_alloc(sizeof(struct imx23_dma_channel) 154 * APBX_DMA_N_CHANNELS, KM_SLEEP); 155 sc->n_channel = APBX_DMA_N_CHANNELS; 156 } 157 158 if (sc->sc_channel == NULL) { 159 aprint_error_dev(sc->sc_dev, "unable to allocate memory for" 160 " DMA channel structures\n"); 161 return; 162 } 163 164 for (i=0; i < sc->n_channel; i++) { 165 chan = (struct imx23_dma_channel *)sc->sc_channel+i; 166 chan->sc = sc; 167 SIMPLEQ_INIT(&chan->head); 168 } 169 #endif 170 apbdma_reset(sc); 171 // apbdma_attached = 1; 172 173 aprint_normal("\n"); 174 175 return; 176 } 177 178 static int 179 apbdma_activate(device_t self, enum devact act) 180 { 181 return EOPNOTSUPP; 182 } 183 184 /* 185 * Reset the APB{H,X}DMA block. 186 * 187 * Inspired by i.MX23 RM "39.3.10 Correct Way to Soft Reset a Block" 188 */ 189 static void 190 apbdma_reset(struct apbdma_softc *sc) 191 { 192 unsigned int loop; 193 194 /* 195 * Prepare for soft-reset by making sure that SFTRST is not currently 196 * asserted. Also clear CLKGATE so we can wait for its assertion below. 197 */ 198 DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST); 199 200 /* Wait at least a microsecond for SFTRST to deassert. */ 201 loop = 0; 202 while ((DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) || 203 (loop < APBDMA_SOFT_RST_LOOP)) 204 { 205 loop++; 206 } 207 208 /* Clear CLKGATE so we can wait for its assertion below. */ 209 DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE); 210 211 /* Soft-reset the block. */ 212 DMACTRL_WR(sc, HW_APB_CTRL0_SET, HW_APB_CTRL0_SFTRST); 213 214 /* Wait until clock is in the gated state. */ 215 while (!(DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE)); 216 217 /* Bring block out of reset. */ 218 DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_SFTRST); 219 220 loop = 0; 221 while ((DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_SFTRST) || 222 (loop < APBDMA_SOFT_RST_LOOP)) 223 { 224 loop++; 225 } 226 227 DMACTRL_WR(sc, HW_APB_CTRL0_CLR, HW_APB_CTRL0_CLKGATE); 228 229 /* Wait until clock is in the NON-gated state. */ 230 while (DMACTRL_RD(sc, HW_APB_CTRL0) & HW_APB_CTRL0_CLKGATE); 231 232 return; 233 } 234 235 /* 236 * Allocate DMA safe memory for commands. 237 */ 238 void * 239 apbdma_dmamem_alloc(device_t dmac, int channel, bus_size_t size) 240 { 241 struct apbdma_softc *sc = device_private(dmac); 242 bus_dma_segment_t segs[1]; /* bus_dmamem_free needs. */ 243 int rsegs; 244 int error; 245 void *ptr = NULL; /* bus_dmamem_unmap needs (size also) */ 246 247 if (size > PAGE_SIZE) 248 return NULL; 249 250 error = bus_dmamem_alloc(sc->sc_dmat, size, PAGE_SIZE, 0, segs, 1, 251 &rsegs, BUS_DMA_NOWAIT); 252 if (error) 253 goto out; 254 //XXX: 255 printf("segs[0].ds_addr=%lx, segs[0].ds_len=%lx, rsegs=%d\n", segs[0].ds_addr, segs[0].ds_len, rsegs); 256 257 error = bus_dmamem_map(sc->sc_dmat, segs, 1, size, &ptr, 258 BUS_DMA_NOWAIT); 259 if (error) 260 goto free; 261 //XXX: 262 printf("segs[0].ds_addr=%lx, segs[0].ds_len=%lx, ptr=%p\n", segs[0].ds_addr, segs[0].ds_len, ptr); 263 264 error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamp, ptr, size, NULL, 265 BUS_DMA_NOWAIT | BUS_DMA_WRITE); 266 if (error) 267 goto unmap; 268 269 bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamp, 0, size, 270 BUS_DMASYNC_PREWRITE); 271 272 // return usable memory 273 unmap: 274 bus_dmamem_unmap(sc->sc_dmat, ptr, size); 275 free: 276 bus_dmamem_free(sc->sc_dmat, segs, 1); 277 out: 278 return NULL; 279 } 280