vf_edma.c revision 266170
1181834Sroberto/*- 2280849Scy * Copyright (c) 2014 Ruslan Bukin <br@bsdpad.com> 3280849Scy * All rights reserved. 4181834Sroberto * 5181834Sroberto * Redistribution and use in source and binary forms, with or without 6181834Sroberto * modification, are permitted provided that the following conditions 7280849Scy * are met: 8280849Scy * 1. Redistributions of source code must retain the above copyright 9280849Scy * notice, this list of conditions and the following disclaimer. 10181834Sroberto * 2. Redistributions in binary form must reproduce the above copyright 11181834Sroberto * notice, this list of conditions and the following disclaimer in the 12280849Scy * documentation and/or other materials provided with the distribution. 13280849Scy * 14285169Scy * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND 15181834Sroberto * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 16280849Scy * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE 17280849Scy * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE 18280849Scy * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 19181834Sroberto * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS 20280849Scy * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) 21280849Scy * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT 22181834Sroberto * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY 23280849Scy * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF 24280849Scy * SUCH DAMAGE. 25181834Sroberto */ 26280849Scy 27181834Sroberto/* 28280849Scy * Vybrid Family Enhanced Direct Memory Access Controller (eDMA) 29280849Scy * Chapter 21, Vybrid Reference Manual, Rev. 5, 07/2013 30280849Scy */ 31181834Sroberto 32181834Sroberto#include <sys/cdefs.h> 33285169Scy__FBSDID("$FreeBSD: stable/10/sys/arm/freescale/vybrid/vf_edma.c 266170 2014-05-15 18:38:19Z ian $"); 34285169Scy 35285169Scy#include <sys/param.h> 36285169Scy#include <sys/systm.h> 37285169Scy#include <sys/bus.h> 38280849Scy#include <sys/kernel.h> 39280849Scy#include <sys/module.h> 40280849Scy#include <sys/malloc.h> 41181834Sroberto#include <sys/rman.h> 42280849Scy#include <sys/timeet.h> 43280849Scy#include <sys/timetc.h> 44181834Sroberto#include <sys/watchdog.h> 45181834Sroberto 46280849Scy#include <dev/fdt/fdt_common.h> 47181834Sroberto#include <dev/ofw/openfirm.h> 48181834Sroberto#include <dev/ofw/ofw_bus.h> 49280849Scy#include <dev/ofw/ofw_bus_subr.h> 50181834Sroberto 51181834Sroberto#include <machine/bus.h> 52280849Scy#include <machine/fdt.h> 53181834Sroberto#include <machine/cpu.h> 54181834Sroberto#include <machine/intr.h> 55280849Scy 56181834Sroberto#include <arm/freescale/vybrid/vf_edma.h> 57181834Sroberto#include <arm/freescale/vybrid/vf_dmamux.h> 58280849Scy#include <arm/freescale/vybrid/vf_common.h> 59181834Sroberto 60181834Srobertostruct edma_channel { 61280849Scy uint32_t enabled; 62181834Sroberto uint32_t mux_num; 63181834Sroberto uint32_t mux_src; 64280849Scy uint32_t mux_chn; 65181834Sroberto uint32_t (*ih) (void *, int); 66181834Sroberto void *ih_user; 67280849Scy}; 68181834Sroberto 69280849Scystatic struct edma_channel edma_map[EDMA_NUM_CHANNELS]; 70280849Scy 71280849Scystatic struct resource_spec edma_spec[] = { 72181834Sroberto { SYS_RES_MEMORY, 0, RF_ACTIVE }, 73280849Scy { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* TCD */ 74181834Sroberto { SYS_RES_IRQ, 0, RF_ACTIVE }, /* Transfer complete */ 75181834Sroberto { SYS_RES_IRQ, 1, RF_ACTIVE }, /* Error Interrupt */ 76280849Scy { -1, 0 } 77280849Scy}; 78280849Scy 79280849Scystatic int 80280849Scyedma_probe(device_t dev) 81280849Scy{ 82280849Scy 83280849Scy if (!ofw_bus_status_okay(dev)) 84280849Scy return (ENXIO); 85280849Scy 86280849Scy if (!ofw_bus_is_compatible(dev, "fsl,mvf600-edma")) 87280849Scy return (ENXIO); 88280849Scy 89280849Scy device_set_desc(dev, "Vybrid Family eDMA Controller"); 90280849Scy return (BUS_PROBE_DEFAULT); 91280849Scy} 92280849Scy 93280849Scystatic void 94280849Scyedma_transfer_complete_intr(void *arg) 95280849Scy{ 96280849Scy struct edma_channel *ch; 97280849Scy struct edma_softc *sc; 98280849Scy int interrupts; 99280849Scy int i; 100280849Scy 101280849Scy sc = arg; 102280849Scy 103280849Scy interrupts = READ4(sc, DMA_INT); 104280849Scy WRITE1(sc, DMA_CINT, CINT_CAIR); 105181834Sroberto 106181834Sroberto for (i = 0; i < EDMA_NUM_CHANNELS; i++) { 107181834Sroberto if (interrupts & (0x1 << i)) { 108181834Sroberto ch = &edma_map[i]; 109285169Scy if (ch->enabled == 1) { 110181834Sroberto if (ch->ih != NULL) { 111181834Sroberto ch->ih(ch->ih_user, i); 112181834Sroberto } 113181834Sroberto } 114181834Sroberto } 115280849Scy } 116181834Sroberto} 117181834Sroberto 118181834Srobertostatic void 119181834Srobertoedma_err_intr(void *arg) 120181834Sroberto{ 121181834Sroberto struct edma_softc *sc; 122280849Scy int reg; 123280849Scy 124181834Sroberto sc = arg; 125280849Scy 126280849Scy reg = READ4(sc, DMA_ERR); 127181834Sroberto 128280849Scy#if 0 129280849Scy device_printf(sc->dev, "DMA_ERR 0x%08x, ES 0x%08x\n", 130181834Sroberto reg, READ4(sc, DMA_ES)); 131280849Scy#endif 132181834Sroberto 133181834Sroberto WRITE1(sc, DMA_CERR, CERR_CAEI); 134181834Sroberto} 135181834Sroberto 136280849Scystatic int 137280849Scychannel_free(struct edma_softc *sc, int chnum) 138280849Scy{ 139280849Scy struct edma_channel *ch; 140280849Scy 141181834Sroberto ch = &edma_map[chnum]; 142181834Sroberto ch->enabled = 0; 143181834Sroberto 144181834Sroberto dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 0); 145280849Scy 146181834Sroberto return (0); 147280849Scy} 148181834Sroberto 149280849Scystatic int 150280849Scychannel_configure(struct edma_softc *sc, int mux_grp, int mux_src) 151280849Scy{ 152280849Scy struct edma_channel *ch; 153280849Scy int channel_first; 154181834Sroberto int mux_num; 155280849Scy int chnum; 156181834Sroberto int i; 157181834Sroberto 158181834Sroberto if ((sc->device_id == 0 && mux_grp == 1) || \ 159280849Scy (sc->device_id == 1 && mux_grp == 0)) { 160280849Scy channel_first = NCHAN_PER_MUX; 161280849Scy mux_num = (sc->device_id * 2) + 1; 162280849Scy } else { 163181834Sroberto channel_first = 0; 164181834Sroberto mux_num = sc->device_id * 2; 165181834Sroberto }; 166280849Scy 167181834Sroberto /* Take first unused eDMA channel */ 168280849Scy ch = NULL; 169280849Scy for (i = channel_first; i < (channel_first + NCHAN_PER_MUX); i++) { 170280849Scy ch = &edma_map[i]; 171280849Scy if (ch->enabled == 0) { 172280849Scy break; 173181834Sroberto } 174280849Scy ch = NULL; 175181834Sroberto }; 176181834Sroberto 177181834Sroberto if (ch == NULL) { 178280849Scy /* Can't find free channel */ 179181834Sroberto return (-1); 180280849Scy }; 181280849Scy 182280849Scy chnum = i; 183280849Scy 184280849Scy ch->enabled = 1; 185181834Sroberto ch->mux_num = mux_num; 186280849Scy ch->mux_src = mux_src; 187280849Scy ch->mux_chn = (chnum - channel_first); /* 0 to 15 */ 188280849Scy 189280849Scy dmamux_configure(ch->mux_num, ch->mux_src, ch->mux_chn, 1); 190280849Scy 191181834Sroberto return (chnum); 192280849Scy} 193181834Sroberto 194181834Srobertostatic int 195181834Srobertodma_stop(struct edma_softc *sc, int chnum) 196280849Scy{ 197280849Scy int reg; 198280849Scy 199280849Scy reg = READ4(sc, DMA_ERQ); 200280849Scy reg &= ~(0x1 << chnum); 201181834Sroberto WRITE4(sc, DMA_ERQ, reg); 202280849Scy 203280849Scy return (0); 204280849Scy} 205280849Scy 206280849Scystatic int 207280849Scydma_setup(struct edma_softc *sc, struct tcd_conf *tcd) 208280849Scy{ 209280849Scy struct edma_channel *ch; 210280849Scy int chnum; 211280849Scy int reg; 212280849Scy 213280849Scy chnum = tcd->channel; 214181834Sroberto 215181834Sroberto ch = &edma_map[chnum]; 216280849Scy ch->ih = tcd->ih; 217280849Scy ch->ih_user = tcd->ih_user; 218280849Scy 219280849Scy TCD_WRITE4(sc, DMA_TCDn_SADDR(chnum), tcd->saddr); 220280849Scy TCD_WRITE4(sc, DMA_TCDn_DADDR(chnum), tcd->daddr); 221280849Scy 222280849Scy reg = (tcd->smod << TCD_ATTR_SMOD_SHIFT); 223280849Scy reg |= (tcd->dmod << TCD_ATTR_DMOD_SHIFT); 224280849Scy reg |= (tcd->ssize << TCD_ATTR_SSIZE_SHIFT); 225280849Scy reg |= (tcd->dsize << TCD_ATTR_DSIZE_SHIFT); 226280849Scy TCD_WRITE2(sc, DMA_TCDn_ATTR(chnum), reg); 227280849Scy 228280849Scy TCD_WRITE2(sc, DMA_TCDn_SOFF(chnum), tcd->soff); 229280849Scy TCD_WRITE2(sc, DMA_TCDn_DOFF(chnum), tcd->doff); 230280849Scy TCD_WRITE4(sc, DMA_TCDn_SLAST(chnum), tcd->slast); 231280849Scy TCD_WRITE4(sc, DMA_TCDn_DLASTSGA(chnum), tcd->dlast_sga); 232280849Scy TCD_WRITE4(sc, DMA_TCDn_NBYTES_MLOFFYES(chnum), tcd->nbytes); 233280849Scy 234181834Sroberto reg = tcd->nmajor; /* Current Major Iteration Count */ 235280849Scy TCD_WRITE2(sc, DMA_TCDn_CITER_ELINKNO(chnum), reg); 236280849Scy TCD_WRITE2(sc, DMA_TCDn_BITER_ELINKNO(chnum), reg); 237280849Scy 238280849Scy reg = (TCD_CSR_INTMAJOR); 239280849Scy if(tcd->majorelink == 1) { 240280849Scy reg |= TCD_CSR_MAJORELINK; 241280849Scy reg |= (tcd->majorelinkch << TCD_CSR_MAJORELINKCH_SHIFT); 242280849Scy } 243280849Scy TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg); 244280849Scy 245280849Scy /* Enable requests */ 246280849Scy reg = READ4(sc, DMA_ERQ); 247280849Scy reg |= (0x1 << chnum); 248280849Scy WRITE4(sc, DMA_ERQ, reg); 249280849Scy 250280849Scy /* Enable error interrupts */ 251280849Scy reg = READ4(sc, DMA_EEI); 252280849Scy reg |= (0x1 << chnum); 253280849Scy WRITE4(sc, DMA_EEI, reg); 254280849Scy 255280849Scy return (0); 256280849Scy} 257280849Scy 258280849Scystatic int 259280849Scydma_request(struct edma_softc *sc, int chnum) 260280849Scy{ 261280849Scy int reg; 262280849Scy 263280849Scy /* Start */ 264280849Scy reg = TCD_READ2(sc, DMA_TCDn_CSR(chnum)); 265280849Scy reg |= TCD_CSR_START; 266280849Scy TCD_WRITE2(sc, DMA_TCDn_CSR(chnum), reg); 267280849Scy 268280849Scy return (0); 269280849Scy} 270280849Scy 271280849Scystatic int 272280849Scyedma_attach(device_t dev) 273280849Scy{ 274280849Scy struct edma_softc *sc; 275280849Scy phandle_t node; 276280849Scy int dts_value; 277280849Scy int len; 278280849Scy 279280849Scy sc = device_get_softc(dev); 280280849Scy sc->dev = dev; 281280849Scy 282280849Scy if ((node = ofw_bus_get_node(sc->dev)) == -1) 283280849Scy return (ENXIO); 284280849Scy 285280849Scy if ((len = OF_getproplen(node, "device-id")) <= 0) 286280849Scy return (ENXIO); 287181834Sroberto 288280849Scy OF_getprop(node, "device-id", &dts_value, len); 289181834Sroberto sc->device_id = fdt32_to_cpu(dts_value); 290280849Scy 291181834Sroberto sc->dma_stop = dma_stop; 292181834Sroberto sc->dma_setup = dma_setup; 293181834Sroberto sc->dma_request = dma_request; 294280849Scy sc->channel_configure = channel_configure; 295181834Sroberto sc->channel_free = channel_free; 296181834Sroberto 297280849Scy if (bus_alloc_resources(dev, edma_spec, sc->res)) { 298280849Scy device_printf(dev, "could not allocate resources\n"); 299181834Sroberto return (ENXIO); 300280849Scy } 301181834Sroberto 302280849Scy /* Memory interface */ 303280849Scy sc->bst = rman_get_bustag(sc->res[0]); 304181834Sroberto sc->bsh = rman_get_bushandle(sc->res[0]); 305280849Scy sc->bst_tcd = rman_get_bustag(sc->res[1]); 306280849Scy sc->bsh_tcd = rman_get_bushandle(sc->res[1]); 307181834Sroberto 308181834Sroberto /* Setup interrupt handlers */ 309181834Sroberto if (bus_setup_intr(dev, sc->res[2], INTR_TYPE_BIO | INTR_MPSAFE, 310280849Scy NULL, edma_transfer_complete_intr, sc, &sc->tc_ih)) { 311280849Scy device_printf(dev, "Unable to alloc DMA intr resource.\n"); 312181834Sroberto return (ENXIO); 313181834Sroberto } 314280849Scy 315280849Scy if (bus_setup_intr(dev, sc->res[3], INTR_TYPE_BIO | INTR_MPSAFE, 316280849Scy NULL, edma_err_intr, sc, &sc->err_ih)) { 317280849Scy device_printf(dev, "Unable to alloc DMA Err intr resource.\n"); 318280849Scy return (ENXIO); 319280849Scy } 320280849Scy 321181834Sroberto return (0); 322280849Scy} 323181834Sroberto 324280849Scystatic device_method_t edma_methods[] = { 325181834Sroberto DEVMETHOD(device_probe, edma_probe), 326181834Sroberto DEVMETHOD(device_attach, edma_attach), 327181834Sroberto { 0, 0 } 328280849Scy}; 329181834Sroberto 330181834Srobertostatic driver_t edma_driver = { 331181834Sroberto "edma", 332280849Scy edma_methods, 333280849Scy sizeof(struct edma_softc), 334280849Scy}; 335181834Sroberto 336280849Scystatic devclass_t edma_devclass; 337280849Scy 338181834SrobertoDRIVER_MODULE(edma, simplebus, edma_driver, edma_devclass, 0, 0); 339181834Sroberto