1 /*- 2 * SPDX-License-Identifier: BSD-2-Clause-FreeBSD 3 * 4 * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 5 * All rights reserved. 6 * 7 * Redistribution and use in source and binary forms, with or without 8 * modification, are permitted provided that the following conditions 9 * are met: 10 * 1. Redistributions of source code must retain the above copyright 11 * notice, this list of conditions and the following disclaimer. 12 * 2. Redistributions in binary form must reproduce the above copyright 13 * notice, this list of conditions and the following disclaimer in the 14 * documentation and/or other materials provided with the distribution. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 17 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 18 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 19 * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 20 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 21 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 22 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 23 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 24 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 25 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 26 * SUCH DAMAGE. 27 */ 28 29 /* 30 * Vybrid Family Enhanced Direct Memory Access Controller (eDMA) 31 * Chapter 21, Vybrid Reference Manual, Rev. 5, 07/2013 32 */ 33 34 #include <sys/cdefs.h> 35 __FBSDID("$FreeBSD$"); 36 37 #include <sys/param.h> 38 #include <sys/systm.h> 39 #include <sys/bus.h> 40 #include <sys/kernel.h> 41 #include <sys/module.h> 42 #include <sys/malloc.h> 43 #include <sys/rman.h> 44 #include <sys/timeet.h> 45 #include <sys/timetc.h> 46 #include <sys/watchdog.h> 47 48 #include <dev/ofw/openfirm.h> 49 #include <dev/ofw/ofw_bus.h> 50 #include <dev/ofw/ofw_bus_subr.h> 51 52 #include <machine/bus.h> 53 #include <machine/cpu.h> 54 #include <machine/intr.h> 55 56 #include <arm/freescale/vybrid/vf_edma.h> 57 #include <arm/freescale/vybrid/vf_dmamux.h> 58 #include <arm/freescale/vybrid/vf_common.h> 59 60 struct edma_channel { 61 uint32_t enabled; 62 uint32_t mux_num; 63 uint32_t mux_src; 64 uint32_t mux_chn; 65 uint32_t (*ih) (void *, int); 66 void *ih_user; 67 }; 68 69 static struct edma_channel edma_map[EDMA_NUM_CHANNELS]; 70 71 static struct resource_spec edma_spec[] = { 72 { SYS_RES_MEMORY, 0, RF_ACTIVE }, 73 { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* TCD */ 74 { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Transfer complete */ 75 { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Error Interrupt */ 76 { -1, 0 } 77 }; 78 79 static int 80 edma_probe(device_t dev) 81 { 82 83 if (!ofw_bus_status_okay(dev)) 84 return (ENXIO); 85 86 if (!ofw_bus_is_compatible(dev, "fsl,mvf600-edma")) 87 return (ENXIO); 88 89 device_set_desc(dev, "Vybrid Family eDMA Controller"); 90 return (BUS_PROBE_DEFAULT); 91 } 92 93 static void 94 edma_transfer_complete_intr(void *arg) 95 { 96 struct edma_channel *ch; 97 struct edma_softc *sc; 98 int interrupts; 99 int i; 100 101 sc = arg; 102 103 interrupts = READ4(sc, DMA_INT); 104 WRITE1(sc, DMA_CINT, CINT_CAIR); 105 106 for (i = 0; i < EDMA_NUM_CHANNELS; i++) { 107 if (interrupts & (0x1 << i)) { 108 ch = &edma_map[i]; 109 if (ch->enabled == 1) { 110 if (ch->ih != NULL) { 111 ch->ih(ch->ih_user, i); 112 } 113 } 114 } 115 } 116 } 117 118 static void 119 edma_err_intr(void *arg) 120 { 121 struct edma_softc *sc; 122 #if 0 123 int reg; 124 #endif 125 126 sc = arg; 127 128 /* reg = */ READ4(sc, DMA_ERR); 129 130 #if 0 131 device_printf(sc->dev, "DMA_ERR 0x%08x, ES 0x%08x\n", 132 reg, READ4(sc, DMA_ES)); 133 #endif 134 135 WRITE1(sc, DMA_CERR, CERR_CAEI); 136 } 137 138 static int 139 channel_free(struct edma_softc *sc, int chnum) 140 { 141 struct edma_channel *ch; 142 143 ch = &edma_map[chnum]; 144 ch->enabled = 0; 145 146 dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 0); 147 148 return (0); 149 } 150 151 static int 152 channel_configure(struct edma_softc *sc, int mux_grp, int mux_src) 153 { 154 struct edma_channel *ch; 155 int channel_first; 156 int mux_num; 157 int chnum; 158 int i; 159 160 if ((sc->device_id == 0 && mux_grp == 1) || \ 161 (sc->device_id == 1 && mux_grp == 0)) { 162 channel_first = NCHAN_PER_MUX; 163 mux_num = (sc->device_id * 2) + 1; 164 } else { 165 channel_first = 0; 166 mux_num = sc->device_id * 2; 167 } 168 169 /* Take first unused eDMA channel */ 170 ch = NULL; 171 for (i = channel_first; i < (channel_first + NCHAN_PER_MUX); i++) { 172 ch = &edma_map[i]; 173 if (ch->enabled == 0) { 174 break; 175 } 176 ch = NULL; 177 } 178 179 if (ch == NULL) { 180 /* Can't find free channel */ 181 return (-1); 182 } 183 184 chnum = i; 185 186 ch->enabled = 1; 187 ch->mux_num = mux_num; 188 ch->mux_src = mux_src; 189 ch->mux_chn = (chnum - channel_first); /* 0 to 15 */ 190 191 dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 1); 192 193 return (chnum); 194 } 195 196 static int 197 dma_stop(struct edma_softc *sc, int chnum) 198 { 199 int reg; 200 201 reg = READ4(sc, DMA_ERQ); 202 reg &= ~(0x1 << chnum); 203 WRITE4(sc, DMA_ERQ, reg); 204 205 return (0); 206 } 207 208 static int 209 dma_setup(struct edma_softc *sc, struct tcd_conf *tcd) 210 { 211 struct edma_channel *ch; 212 int chnum; 213 int reg; 214 215 chnum = tcd->channel; 216 217 ch = &edma_map[chnum]; 218 ch->ih = tcd->ih; 219 ch->ih_user = tcd->ih_user; 220 221 TCD_WRITE4(sc, DMA_TCDn_SADDR(chnum), tcd->saddr); 222 TCD_WRITE4(sc, DMA_TCDn_DADDR(chnum), tcd->daddr); 223 224 reg = (tcd->smod << TCD_ATTR_SMOD_SHIFT); 225 reg |= (tcd->dmod << TCD_ATTR_DMOD_SHIFT); 226 reg |= (tcd->ssize << TCD_ATTR_SSIZE_SHIFT); 227 reg |= (tcd->dsize << TCD_ATTR_DSIZE_SHIFT); 228 TCD_WRITE2(sc, DMA_TCDn_ATTR(chnum), reg); 229 230 TCD_WRITE2(sc, DMA_TCDn_SOFF(chnum), tcd->soff); 231 TCD_WRITE2(sc, DMA_TCDn_DOFF(chnum), tcd->doff); 232 TCD_WRITE4(sc, DMA_TCDn_SLAST(chnum), tcd->slast); 233 TCD_WRITE4(sc, DMA_TCDn_DLASTSGA(chnum), tcd->dlast_sga); 234 TCD_WRITE4(sc, DMA_TCDn_NBYTES_MLOFFYES(chnum), tcd->nbytes); 235 236 reg = tcd->nmajor; /* Current Major Iteration Count */ 237 TCD_WRITE2(sc, DMA_TCDn_CITER_ELINKNO(chnum), reg); 238 TCD_WRITE2(sc, DMA_TCDn_BITER_ELINKNO(chnum), reg); 239 240 reg = (TCD_CSR_INTMAJOR); 241 if(tcd->majorelink == 1) { 242 reg |= TCD_CSR_MAJORELINK; 243 reg |= (tcd->majorelinkch << TCD_CSR_MAJORELINKCH_SHIFT); 244 } 245 TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg); 246 247 /* Enable requests */ 248 reg = READ4(sc, DMA_ERQ); 249 reg |= (0x1 << chnum); 250 WRITE4(sc, DMA_ERQ, reg); 251 252 /* Enable error interrupts */ 253 reg = READ4(sc, DMA_EEI); 254 reg |= (0x1 << chnum); 255 WRITE4(sc, DMA_EEI, reg); 256 257 return (0); 258 } 259 260 static int 261 dma_request(struct edma_softc *sc, int chnum) 262 { 263 int reg; 264 265 /* Start */ 266 reg = TCD_READ2(sc, DMA_TCDn_CSR(chnum)); 267 reg |= TCD_CSR_START; 268 TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg); 269 270 return (0); 271 } 272 273 static int 274 edma_attach(device_t dev) 275 { 276 struct edma_softc *sc; 277 phandle_t node; 278 int dts_value; 279 int len; 280 281 sc = device_get_softc(dev); 282 sc->dev = dev; 283 284 if ((node = ofw_bus_get_node(sc->dev)) == -1) 285 return (ENXIO); 286 287 if ((len = OF_getproplen(node, "device-id")) <= 0) 288 return (ENXIO); 289 290 OF_getencprop(node, "device-id", &dts_value, len); 291 sc->device_id = dts_value; 292 293 sc->dma_stop = dma_stop; 294 sc->dma_setup = dma_setup; 295 sc->dma_request = dma_request; 296 sc->channel_configure = channel_configure; 297 sc->channel_free = channel_free; 298 299 if (bus_alloc_resources(dev, edma_spec, sc->res)) { 300 device_printf(dev, "could not allocate resources\n"); 301 return (ENXIO); 302 } 303 304 /* Memory interface */ 305 sc->bst = rman_get_bustag(sc->res[0]); 306 sc->bsh = rman_get_bushandle(sc->res[0]); 307 sc->bst_tcd = rman_get_bustag(sc->res[1]); 308 sc->bsh_tcd = rman_get_bushandle(sc->res[1]); 309 310 /* Setup interrupt handlers */ 311 if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_BIO | INTR_MPSAFE, 312 NULL, edma_transfer_complete_intr, sc, &sc->tc_ih)) { 313 device_printf(dev, "Unable to alloc DMA intr resource.\n"); 314 return (ENXIO); 315 } 316 317 if (bus_setup_intr(dev, sc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, 318 NULL, edma_err_intr, sc, &sc->err_ih)) { 319 device_printf(dev, "Unable to alloc DMA Err intr resource.\n"); 320 return (ENXIO); 321 } 322 323 return (0); 324 } 325 326 static device_method_t edma_methods[] = { 327 DEVMETHOD(device_probe, edma_probe), 328 DEVMETHOD(device_attach, edma_attach), 329 { 0, 0 } 330 }; 331 332 static driver_t edma_driver = { 333 "edma", 334 edma_methods, 335 sizeof(struct edma_softc), 336 }; 337 338 DRIVER_MODULE(edma, simplebus, edma_driver, 0, 0); 339