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