15939d8a1SRuslan Bukin /*-
25939d8a1SRuslan Bukin * SPDX-License-Identifier: BSD-2-Clause
35939d8a1SRuslan Bukin *
45939d8a1SRuslan Bukin * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com>
55939d8a1SRuslan Bukin *
65939d8a1SRuslan Bukin * This software was developed by SRI International and the University of
75939d8a1SRuslan Bukin * Cambridge Computer Laboratory (Department of Computer Science and
85939d8a1SRuslan Bukin * Technology) under DARPA contract HR0011-18-C-0016 ("ECATS"), as part of the
95939d8a1SRuslan Bukin * DARPA SSITH research programme.
105939d8a1SRuslan Bukin *
115939d8a1SRuslan Bukin * Redistribution and use in source and binary forms, with or without
125939d8a1SRuslan Bukin * modification, are permitted provided that the following conditions
135939d8a1SRuslan Bukin * are met:
145939d8a1SRuslan Bukin * 1. Redistributions of source code must retain the above copyright
155939d8a1SRuslan Bukin * notice, this list of conditions and the following disclaimer.
165939d8a1SRuslan Bukin * 2. Redistributions in binary form must reproduce the above copyright
175939d8a1SRuslan Bukin * notice, this list of conditions and the following disclaimer in the
185939d8a1SRuslan Bukin * documentation and/or other materials provided with the distribution.
195939d8a1SRuslan Bukin *
205939d8a1SRuslan Bukin * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
215939d8a1SRuslan Bukin * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
225939d8a1SRuslan Bukin * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
235939d8a1SRuslan Bukin * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
245939d8a1SRuslan Bukin * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
255939d8a1SRuslan Bukin * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
265939d8a1SRuslan Bukin * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
275939d8a1SRuslan Bukin * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
285939d8a1SRuslan Bukin * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
295939d8a1SRuslan Bukin * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
305939d8a1SRuslan Bukin * SUCH DAMAGE.
315939d8a1SRuslan Bukin */
325939d8a1SRuslan Bukin
335939d8a1SRuslan Bukin /* Xilinx AXI DMA controller driver. */
345939d8a1SRuslan Bukin
355939d8a1SRuslan Bukin #include <sys/cdefs.h>
365939d8a1SRuslan Bukin #include "opt_platform.h"
375939d8a1SRuslan Bukin #include <sys/param.h>
385939d8a1SRuslan Bukin #include <sys/systm.h>
395939d8a1SRuslan Bukin #include <sys/conf.h>
405939d8a1SRuslan Bukin #include <sys/bus.h>
415939d8a1SRuslan Bukin #include <sys/kernel.h>
425939d8a1SRuslan Bukin #include <sys/module.h>
435939d8a1SRuslan Bukin #include <sys/rman.h>
445939d8a1SRuslan Bukin
455939d8a1SRuslan Bukin #include <machine/bus.h>
465939d8a1SRuslan Bukin
475939d8a1SRuslan Bukin #include <vm/vm.h>
485939d8a1SRuslan Bukin #include <vm/vm_extern.h>
495939d8a1SRuslan Bukin #include <vm/vm_page.h>
505939d8a1SRuslan Bukin
515939d8a1SRuslan Bukin #ifdef FDT
525939d8a1SRuslan Bukin #include <dev/fdt/fdt_common.h>
535939d8a1SRuslan Bukin #include <dev/ofw/ofw_bus.h>
545939d8a1SRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
555939d8a1SRuslan Bukin #endif
565939d8a1SRuslan Bukin
575939d8a1SRuslan Bukin #include <dev/xdma/xdma.h>
585939d8a1SRuslan Bukin #include <dev/xilinx/axidma.h>
595939d8a1SRuslan Bukin
605939d8a1SRuslan Bukin #include "xdma_if.h"
615939d8a1SRuslan Bukin
62a8692c16SRuslan Bukin #define READ4(_sc, _reg) \
63a8692c16SRuslan Bukin bus_space_read_4(_sc->bst, _sc->bsh, _reg)
64a8692c16SRuslan Bukin #define WRITE4(_sc, _reg, _val) \
65a8692c16SRuslan Bukin bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val)
66a8692c16SRuslan Bukin #define READ8(_sc, _reg) \
67a8692c16SRuslan Bukin bus_space_read_8(_sc->bst, _sc->bsh, _reg)
68a8692c16SRuslan Bukin #define WRITE8(_sc, _reg, _val) \
69a8692c16SRuslan Bukin bus_space_write_8(_sc->bst, _sc->bsh, _reg, _val)
70a8692c16SRuslan Bukin
715939d8a1SRuslan Bukin #define AXIDMA_DEBUG
725939d8a1SRuslan Bukin #undef AXIDMA_DEBUG
735939d8a1SRuslan Bukin
745939d8a1SRuslan Bukin #ifdef AXIDMA_DEBUG
755939d8a1SRuslan Bukin #define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__)
765939d8a1SRuslan Bukin #else
775939d8a1SRuslan Bukin #define dprintf(fmt, ...)
785939d8a1SRuslan Bukin #endif
795939d8a1SRuslan Bukin
805939d8a1SRuslan Bukin extern struct bus_space memmap_bus;
815939d8a1SRuslan Bukin
825939d8a1SRuslan Bukin struct axidma_channel {
835939d8a1SRuslan Bukin struct axidma_softc *sc;
845939d8a1SRuslan Bukin xdma_channel_t *xchan;
855939d8a1SRuslan Bukin bool used;
865939d8a1SRuslan Bukin int idx_head;
875939d8a1SRuslan Bukin int idx_tail;
885939d8a1SRuslan Bukin
895939d8a1SRuslan Bukin struct axidma_desc **descs;
905939d8a1SRuslan Bukin vm_paddr_t *descs_phys;
915939d8a1SRuslan Bukin uint32_t descs_num;
925939d8a1SRuslan Bukin
935939d8a1SRuslan Bukin vm_size_t mem_size;
945939d8a1SRuslan Bukin vm_offset_t mem_paddr;
955939d8a1SRuslan Bukin vm_offset_t mem_vaddr;
965939d8a1SRuslan Bukin
975939d8a1SRuslan Bukin uint32_t descs_used_count;
985939d8a1SRuslan Bukin };
995939d8a1SRuslan Bukin
1005939d8a1SRuslan Bukin struct axidma_softc {
1015939d8a1SRuslan Bukin device_t dev;
1025939d8a1SRuslan Bukin struct resource *res[3];
1035939d8a1SRuslan Bukin bus_space_tag_t bst;
1045939d8a1SRuslan Bukin bus_space_handle_t bsh;
1055939d8a1SRuslan Bukin void *ih[2];
1065939d8a1SRuslan Bukin struct axidma_desc desc;
1075939d8a1SRuslan Bukin struct axidma_channel channels[AXIDMA_NCHANNELS];
1085939d8a1SRuslan Bukin };
1095939d8a1SRuslan Bukin
1105939d8a1SRuslan Bukin static struct resource_spec axidma_spec[] = {
1115939d8a1SRuslan Bukin { SYS_RES_MEMORY, 0, RF_ACTIVE },
1125939d8a1SRuslan Bukin { SYS_RES_IRQ, 0, RF_ACTIVE },
1135939d8a1SRuslan Bukin { SYS_RES_IRQ, 1, RF_ACTIVE },
1145939d8a1SRuslan Bukin { -1, 0 }
1155939d8a1SRuslan Bukin };
1165939d8a1SRuslan Bukin
1175939d8a1SRuslan Bukin #define HWTYPE_NONE 0
1185939d8a1SRuslan Bukin #define HWTYPE_STD 1
1195939d8a1SRuslan Bukin
1205939d8a1SRuslan Bukin static struct ofw_compat_data compat_data[] = {
1215939d8a1SRuslan Bukin { "xlnx,eth-dma", HWTYPE_STD },
1225939d8a1SRuslan Bukin { NULL, HWTYPE_NONE },
1235939d8a1SRuslan Bukin };
1245939d8a1SRuslan Bukin
1255939d8a1SRuslan Bukin static int axidma_probe(device_t dev);
1265939d8a1SRuslan Bukin static int axidma_attach(device_t dev);
1275939d8a1SRuslan Bukin static int axidma_detach(device_t dev);
1285939d8a1SRuslan Bukin
1295939d8a1SRuslan Bukin static inline uint32_t
axidma_next_desc(struct axidma_channel * chan,uint32_t curidx)1305939d8a1SRuslan Bukin axidma_next_desc(struct axidma_channel *chan, uint32_t curidx)
1315939d8a1SRuslan Bukin {
1325939d8a1SRuslan Bukin
1335939d8a1SRuslan Bukin return ((curidx + 1) % chan->descs_num);
1345939d8a1SRuslan Bukin }
1355939d8a1SRuslan Bukin
1365939d8a1SRuslan Bukin static void
axidma_intr(struct axidma_softc * sc,struct axidma_channel * chan)1375939d8a1SRuslan Bukin axidma_intr(struct axidma_softc *sc,
1385939d8a1SRuslan Bukin struct axidma_channel *chan)
1395939d8a1SRuslan Bukin {
1405939d8a1SRuslan Bukin xdma_transfer_status_t status;
1415939d8a1SRuslan Bukin xdma_transfer_status_t st;
1425939d8a1SRuslan Bukin struct axidma_fdt_data *data;
1435939d8a1SRuslan Bukin xdma_controller_t *xdma;
1445939d8a1SRuslan Bukin struct axidma_desc *desc;
1455939d8a1SRuslan Bukin struct xdma_channel *xchan;
1465939d8a1SRuslan Bukin uint32_t tot_copied;
1475939d8a1SRuslan Bukin int pending;
1485939d8a1SRuslan Bukin int errors;
1495939d8a1SRuslan Bukin
1505939d8a1SRuslan Bukin xchan = chan->xchan;
1515939d8a1SRuslan Bukin xdma = xchan->xdma;
1525939d8a1SRuslan Bukin data = xdma->data;
1535939d8a1SRuslan Bukin
1545939d8a1SRuslan Bukin pending = READ4(sc, AXI_DMASR(data->id));
1555939d8a1SRuslan Bukin WRITE4(sc, AXI_DMASR(data->id), pending);
1565939d8a1SRuslan Bukin
1575939d8a1SRuslan Bukin errors = (pending & (DMASR_DMAINTERR | DMASR_DMASLVERR
1585939d8a1SRuslan Bukin | DMASR_DMADECOREERR | DMASR_SGINTERR
1595939d8a1SRuslan Bukin | DMASR_SGSLVERR | DMASR_SGDECERR));
1605939d8a1SRuslan Bukin
1615939d8a1SRuslan Bukin dprintf("%s: AXI_DMASR %x\n", __func__,
1625939d8a1SRuslan Bukin READ4(sc, AXI_DMASR(data->id)));
1635939d8a1SRuslan Bukin dprintf("%s: AXI_CURDESC %x\n", __func__,
1645939d8a1SRuslan Bukin READ4(sc, AXI_CURDESC(data->id)));
1655939d8a1SRuslan Bukin dprintf("%s: AXI_TAILDESC %x\n", __func__,
1665939d8a1SRuslan Bukin READ4(sc, AXI_TAILDESC(data->id)));
1675939d8a1SRuslan Bukin
1685939d8a1SRuslan Bukin tot_copied = 0;
1695939d8a1SRuslan Bukin
1705939d8a1SRuslan Bukin while (chan->idx_tail != chan->idx_head) {
1715939d8a1SRuslan Bukin desc = chan->descs[chan->idx_tail];
1725939d8a1SRuslan Bukin if ((desc->status & BD_STATUS_CMPLT) == 0)
1735939d8a1SRuslan Bukin break;
1745939d8a1SRuslan Bukin
1755939d8a1SRuslan Bukin st.error = errors;
1765939d8a1SRuslan Bukin st.transferred = desc->status & BD_CONTROL_LEN_M;
1775939d8a1SRuslan Bukin tot_copied += st.transferred;
1785939d8a1SRuslan Bukin xchan_seg_done(xchan, &st);
1795939d8a1SRuslan Bukin
1805939d8a1SRuslan Bukin chan->idx_tail = axidma_next_desc(chan, chan->idx_tail);
1815939d8a1SRuslan Bukin atomic_subtract_int(&chan->descs_used_count, 1);
1825939d8a1SRuslan Bukin }
1835939d8a1SRuslan Bukin
1845939d8a1SRuslan Bukin /* Finish operation */
1855939d8a1SRuslan Bukin status.error = errors;
1865939d8a1SRuslan Bukin status.transferred = tot_copied;
1875939d8a1SRuslan Bukin xdma_callback(chan->xchan, &status);
1885939d8a1SRuslan Bukin }
1895939d8a1SRuslan Bukin
1905939d8a1SRuslan Bukin static void
axidma_intr_rx(void * arg)1915939d8a1SRuslan Bukin axidma_intr_rx(void *arg)
1925939d8a1SRuslan Bukin {
1935939d8a1SRuslan Bukin struct axidma_softc *sc;
1945939d8a1SRuslan Bukin struct axidma_channel *chan;
1955939d8a1SRuslan Bukin
1965939d8a1SRuslan Bukin dprintf("%s\n", __func__);
1975939d8a1SRuslan Bukin
1985939d8a1SRuslan Bukin sc = arg;
1995939d8a1SRuslan Bukin chan = &sc->channels[AXIDMA_RX_CHAN];
2005939d8a1SRuslan Bukin
2015939d8a1SRuslan Bukin axidma_intr(sc, chan);
2025939d8a1SRuslan Bukin }
2035939d8a1SRuslan Bukin
2045939d8a1SRuslan Bukin static void
axidma_intr_tx(void * arg)2055939d8a1SRuslan Bukin axidma_intr_tx(void *arg)
2065939d8a1SRuslan Bukin {
2075939d8a1SRuslan Bukin struct axidma_softc *sc;
2085939d8a1SRuslan Bukin struct axidma_channel *chan;
2095939d8a1SRuslan Bukin
2105939d8a1SRuslan Bukin dprintf("%s\n", __func__);
2115939d8a1SRuslan Bukin
2125939d8a1SRuslan Bukin sc = arg;
2135939d8a1SRuslan Bukin chan = &sc->channels[AXIDMA_TX_CHAN];
2145939d8a1SRuslan Bukin
2155939d8a1SRuslan Bukin axidma_intr(sc, chan);
2165939d8a1SRuslan Bukin }
2175939d8a1SRuslan Bukin
2185939d8a1SRuslan Bukin static int
axidma_reset(struct axidma_softc * sc,int chan_id)2195939d8a1SRuslan Bukin axidma_reset(struct axidma_softc *sc, int chan_id)
2205939d8a1SRuslan Bukin {
2215939d8a1SRuslan Bukin int timeout;
2225939d8a1SRuslan Bukin
2235939d8a1SRuslan Bukin WRITE4(sc, AXI_DMACR(chan_id), DMACR_RESET);
2245939d8a1SRuslan Bukin
2255939d8a1SRuslan Bukin timeout = 100;
2265939d8a1SRuslan Bukin do {
2275939d8a1SRuslan Bukin if ((READ4(sc, AXI_DMACR(chan_id)) & DMACR_RESET) == 0)
2285939d8a1SRuslan Bukin break;
2295939d8a1SRuslan Bukin } while (timeout--);
2305939d8a1SRuslan Bukin
2315939d8a1SRuslan Bukin dprintf("timeout %d\n", timeout);
2325939d8a1SRuslan Bukin
2335939d8a1SRuslan Bukin if (timeout == 0)
2345939d8a1SRuslan Bukin return (-1);
2355939d8a1SRuslan Bukin
2365939d8a1SRuslan Bukin dprintf("%s: read control after reset: %x\n",
2375939d8a1SRuslan Bukin __func__, READ4(sc, AXI_DMACR(chan_id)));
2385939d8a1SRuslan Bukin
2395939d8a1SRuslan Bukin return (0);
2405939d8a1SRuslan Bukin }
2415939d8a1SRuslan Bukin
2425939d8a1SRuslan Bukin static int
axidma_probe(device_t dev)2435939d8a1SRuslan Bukin axidma_probe(device_t dev)
2445939d8a1SRuslan Bukin {
2455939d8a1SRuslan Bukin int hwtype;
2465939d8a1SRuslan Bukin
2475939d8a1SRuslan Bukin if (!ofw_bus_status_okay(dev))
2485939d8a1SRuslan Bukin return (ENXIO);
2495939d8a1SRuslan Bukin
2505939d8a1SRuslan Bukin hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data;
2515939d8a1SRuslan Bukin if (hwtype == HWTYPE_NONE)
2525939d8a1SRuslan Bukin return (ENXIO);
2535939d8a1SRuslan Bukin
2545939d8a1SRuslan Bukin device_set_desc(dev, "Xilinx AXI DMA");
2555939d8a1SRuslan Bukin
2565939d8a1SRuslan Bukin return (BUS_PROBE_DEFAULT);
2575939d8a1SRuslan Bukin }
2585939d8a1SRuslan Bukin
2595939d8a1SRuslan Bukin static int
axidma_attach(device_t dev)2605939d8a1SRuslan Bukin axidma_attach(device_t dev)
2615939d8a1SRuslan Bukin {
2625939d8a1SRuslan Bukin struct axidma_softc *sc;
2635939d8a1SRuslan Bukin phandle_t xref, node;
2645939d8a1SRuslan Bukin int err;
2655939d8a1SRuslan Bukin
2665939d8a1SRuslan Bukin sc = device_get_softc(dev);
2675939d8a1SRuslan Bukin sc->dev = dev;
2685939d8a1SRuslan Bukin
2695939d8a1SRuslan Bukin if (bus_alloc_resources(dev, axidma_spec, sc->res)) {
2705939d8a1SRuslan Bukin device_printf(dev, "could not allocate resources.\n");
2715939d8a1SRuslan Bukin return (ENXIO);
2725939d8a1SRuslan Bukin }
2735939d8a1SRuslan Bukin
2745939d8a1SRuslan Bukin /* CSR memory interface */
2755939d8a1SRuslan Bukin sc->bst = rman_get_bustag(sc->res[0]);
2765939d8a1SRuslan Bukin sc->bsh = rman_get_bushandle(sc->res[0]);
2775939d8a1SRuslan Bukin
2785939d8a1SRuslan Bukin /* Setup interrupt handler */
2795939d8a1SRuslan Bukin err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE,
2805939d8a1SRuslan Bukin NULL, axidma_intr_tx, sc, &sc->ih[0]);
2815939d8a1SRuslan Bukin if (err) {
2825939d8a1SRuslan Bukin device_printf(dev, "Unable to alloc interrupt resource.\n");
2835939d8a1SRuslan Bukin return (ENXIO);
2845939d8a1SRuslan Bukin }
2855939d8a1SRuslan Bukin
2865939d8a1SRuslan Bukin /* Setup interrupt handler */
2875939d8a1SRuslan Bukin err = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE,
2885939d8a1SRuslan Bukin NULL, axidma_intr_rx, sc, &sc->ih[1]);
2895939d8a1SRuslan Bukin if (err) {
2905939d8a1SRuslan Bukin device_printf(dev, "Unable to alloc interrupt resource.\n");
2915939d8a1SRuslan Bukin return (ENXIO);
2925939d8a1SRuslan Bukin }
2935939d8a1SRuslan Bukin
2945939d8a1SRuslan Bukin node = ofw_bus_get_node(dev);
2955939d8a1SRuslan Bukin xref = OF_xref_from_node(node);
2965939d8a1SRuslan Bukin OF_device_register_xref(xref, dev);
2975939d8a1SRuslan Bukin
2985939d8a1SRuslan Bukin return (0);
2995939d8a1SRuslan Bukin }
3005939d8a1SRuslan Bukin
3015939d8a1SRuslan Bukin static int
axidma_detach(device_t dev)3025939d8a1SRuslan Bukin axidma_detach(device_t dev)
3035939d8a1SRuslan Bukin {
3045939d8a1SRuslan Bukin struct axidma_softc *sc;
3055939d8a1SRuslan Bukin
3065939d8a1SRuslan Bukin sc = device_get_softc(dev);
3075939d8a1SRuslan Bukin
3085939d8a1SRuslan Bukin bus_teardown_intr(dev, sc->res[1], sc->ih[0]);
3095939d8a1SRuslan Bukin bus_teardown_intr(dev, sc->res[2], sc->ih[1]);
3105939d8a1SRuslan Bukin bus_release_resources(dev, axidma_spec, sc->res);
3115939d8a1SRuslan Bukin
3125939d8a1SRuslan Bukin return (0);
3135939d8a1SRuslan Bukin }
3145939d8a1SRuslan Bukin
3155939d8a1SRuslan Bukin static int
axidma_desc_free(struct axidma_softc * sc,struct axidma_channel * chan)3165939d8a1SRuslan Bukin axidma_desc_free(struct axidma_softc *sc, struct axidma_channel *chan)
3175939d8a1SRuslan Bukin {
3185939d8a1SRuslan Bukin struct xdma_channel *xchan;
3195939d8a1SRuslan Bukin
3205939d8a1SRuslan Bukin xchan = chan->xchan;
3215939d8a1SRuslan Bukin
3225939d8a1SRuslan Bukin free(chan->descs, M_DEVBUF);
3235939d8a1SRuslan Bukin free(chan->descs_phys, M_DEVBUF);
3245939d8a1SRuslan Bukin
3255939d8a1SRuslan Bukin pmap_kremove_device(chan->mem_vaddr, chan->mem_size);
3265939d8a1SRuslan Bukin kva_free(chan->mem_vaddr, chan->mem_size);
3275939d8a1SRuslan Bukin vmem_free(xchan->vmem, chan->mem_paddr, chan->mem_size);
3285939d8a1SRuslan Bukin
3295939d8a1SRuslan Bukin return (0);
3305939d8a1SRuslan Bukin }
3315939d8a1SRuslan Bukin
3325939d8a1SRuslan Bukin static int
axidma_desc_alloc(struct axidma_softc * sc,struct xdma_channel * xchan,uint32_t desc_size)3335939d8a1SRuslan Bukin axidma_desc_alloc(struct axidma_softc *sc, struct xdma_channel *xchan,
3345939d8a1SRuslan Bukin uint32_t desc_size)
3355939d8a1SRuslan Bukin {
3365939d8a1SRuslan Bukin struct axidma_channel *chan;
3375939d8a1SRuslan Bukin int nsegments;
3385939d8a1SRuslan Bukin int i;
3395939d8a1SRuslan Bukin
3405939d8a1SRuslan Bukin chan = (struct axidma_channel *)xchan->chan;
3415939d8a1SRuslan Bukin nsegments = chan->descs_num;
3425939d8a1SRuslan Bukin
3435939d8a1SRuslan Bukin chan->descs = malloc(nsegments * sizeof(struct axidma_desc *),
3445939d8a1SRuslan Bukin M_DEVBUF, M_NOWAIT | M_ZERO);
3455939d8a1SRuslan Bukin if (chan->descs == NULL) {
3465939d8a1SRuslan Bukin device_printf(sc->dev,
3475939d8a1SRuslan Bukin "%s: Can't allocate memory.\n", __func__);
3485939d8a1SRuslan Bukin return (-1);
3495939d8a1SRuslan Bukin }
3505939d8a1SRuslan Bukin
3515939d8a1SRuslan Bukin chan->descs_phys = malloc(nsegments * sizeof(bus_dma_segment_t),
3525939d8a1SRuslan Bukin M_DEVBUF, M_NOWAIT | M_ZERO);
3535939d8a1SRuslan Bukin chan->mem_size = desc_size * nsegments;
3545939d8a1SRuslan Bukin if (vmem_alloc(xchan->vmem, chan->mem_size, M_FIRSTFIT | M_NOWAIT,
3555939d8a1SRuslan Bukin &chan->mem_paddr)) {
3565939d8a1SRuslan Bukin device_printf(sc->dev, "Failed to allocate memory.\n");
3575939d8a1SRuslan Bukin return (-1);
3585939d8a1SRuslan Bukin }
3595939d8a1SRuslan Bukin chan->mem_vaddr = kva_alloc(chan->mem_size);
3605939d8a1SRuslan Bukin pmap_kenter_device(chan->mem_vaddr, chan->mem_size, chan->mem_paddr);
3615939d8a1SRuslan Bukin
3627c7b8f57SMitchell Horne device_printf(sc->dev, "Allocated chunk %lx %lu\n",
3635939d8a1SRuslan Bukin chan->mem_paddr, chan->mem_size);
3645939d8a1SRuslan Bukin
3655939d8a1SRuslan Bukin for (i = 0; i < nsegments; i++) {
3665939d8a1SRuslan Bukin chan->descs[i] = (struct axidma_desc *)
3675939d8a1SRuslan Bukin ((uint64_t)chan->mem_vaddr + desc_size * i);
3685939d8a1SRuslan Bukin chan->descs_phys[i] = chan->mem_paddr + desc_size * i;
3695939d8a1SRuslan Bukin }
3705939d8a1SRuslan Bukin
3715939d8a1SRuslan Bukin return (0);
3725939d8a1SRuslan Bukin }
3735939d8a1SRuslan Bukin
3745939d8a1SRuslan Bukin static int
axidma_channel_alloc(device_t dev,struct xdma_channel * xchan)3755939d8a1SRuslan Bukin axidma_channel_alloc(device_t dev, struct xdma_channel *xchan)
3765939d8a1SRuslan Bukin {
3775939d8a1SRuslan Bukin xdma_controller_t *xdma;
3785939d8a1SRuslan Bukin struct axidma_fdt_data *data;
3795939d8a1SRuslan Bukin struct axidma_channel *chan;
3805939d8a1SRuslan Bukin struct axidma_softc *sc;
3815939d8a1SRuslan Bukin
3825939d8a1SRuslan Bukin sc = device_get_softc(dev);
3835939d8a1SRuslan Bukin
3845939d8a1SRuslan Bukin if (xchan->caps & XCHAN_CAP_BUSDMA) {
3855939d8a1SRuslan Bukin device_printf(sc->dev,
3865939d8a1SRuslan Bukin "Error: busdma operation is not implemented.");
3875939d8a1SRuslan Bukin return (-1);
3885939d8a1SRuslan Bukin }
3895939d8a1SRuslan Bukin
3905939d8a1SRuslan Bukin xdma = xchan->xdma;
3915939d8a1SRuslan Bukin data = xdma->data;
3925939d8a1SRuslan Bukin
3935939d8a1SRuslan Bukin chan = &sc->channels[data->id];
3945939d8a1SRuslan Bukin if (chan->used == false) {
3955939d8a1SRuslan Bukin if (axidma_reset(sc, data->id) != 0)
3965939d8a1SRuslan Bukin return (-1);
3975939d8a1SRuslan Bukin chan->xchan = xchan;
3980c340d7eSRuslan Bukin xchan->caps |= XCHAN_CAP_BOUNCE;
3995939d8a1SRuslan Bukin xchan->chan = (void *)chan;
4005939d8a1SRuslan Bukin chan->sc = sc;
4015939d8a1SRuslan Bukin chan->used = true;
4025939d8a1SRuslan Bukin chan->idx_head = 0;
4035939d8a1SRuslan Bukin chan->idx_tail = 0;
4045939d8a1SRuslan Bukin chan->descs_used_count = 0;
4055939d8a1SRuslan Bukin chan->descs_num = AXIDMA_DESCS_NUM;
4065939d8a1SRuslan Bukin
4075939d8a1SRuslan Bukin return (0);
4085939d8a1SRuslan Bukin }
4095939d8a1SRuslan Bukin
4105939d8a1SRuslan Bukin return (-1);
4115939d8a1SRuslan Bukin }
4125939d8a1SRuslan Bukin
4135939d8a1SRuslan Bukin static int
axidma_channel_free(device_t dev,struct xdma_channel * xchan)4145939d8a1SRuslan Bukin axidma_channel_free(device_t dev, struct xdma_channel *xchan)
4155939d8a1SRuslan Bukin {
4165939d8a1SRuslan Bukin struct axidma_channel *chan;
4175939d8a1SRuslan Bukin struct axidma_softc *sc;
4185939d8a1SRuslan Bukin
4195939d8a1SRuslan Bukin sc = device_get_softc(dev);
4205939d8a1SRuslan Bukin
4215939d8a1SRuslan Bukin chan = (struct axidma_channel *)xchan->chan;
4225939d8a1SRuslan Bukin
4235939d8a1SRuslan Bukin axidma_desc_free(sc, chan);
4245939d8a1SRuslan Bukin
4255939d8a1SRuslan Bukin chan->used = false;
4265939d8a1SRuslan Bukin
4275939d8a1SRuslan Bukin return (0);
4285939d8a1SRuslan Bukin }
4295939d8a1SRuslan Bukin
4305939d8a1SRuslan Bukin static int
axidma_channel_capacity(device_t dev,xdma_channel_t * xchan,uint32_t * capacity)4315939d8a1SRuslan Bukin axidma_channel_capacity(device_t dev, xdma_channel_t *xchan,
4325939d8a1SRuslan Bukin uint32_t *capacity)
4335939d8a1SRuslan Bukin {
4345939d8a1SRuslan Bukin struct axidma_channel *chan;
4355939d8a1SRuslan Bukin uint32_t c;
4365939d8a1SRuslan Bukin
4375939d8a1SRuslan Bukin chan = (struct axidma_channel *)xchan->chan;
4385939d8a1SRuslan Bukin
4395939d8a1SRuslan Bukin /* At least one descriptor must be left empty. */
4405939d8a1SRuslan Bukin c = (chan->descs_num - chan->descs_used_count - 1);
4415939d8a1SRuslan Bukin
4425939d8a1SRuslan Bukin *capacity = c;
4435939d8a1SRuslan Bukin
4445939d8a1SRuslan Bukin return (0);
4455939d8a1SRuslan Bukin }
4465939d8a1SRuslan Bukin
4475939d8a1SRuslan Bukin static int
axidma_channel_submit_sg(device_t dev,struct xdma_channel * xchan,struct xdma_sglist * sg,uint32_t sg_n)4485939d8a1SRuslan Bukin axidma_channel_submit_sg(device_t dev, struct xdma_channel *xchan,
4495939d8a1SRuslan Bukin struct xdma_sglist *sg, uint32_t sg_n)
4505939d8a1SRuslan Bukin {
4515939d8a1SRuslan Bukin xdma_controller_t *xdma;
4525939d8a1SRuslan Bukin struct axidma_fdt_data *data;
4535939d8a1SRuslan Bukin struct axidma_channel *chan;
4545939d8a1SRuslan Bukin struct axidma_desc *desc;
4555939d8a1SRuslan Bukin struct axidma_softc *sc;
4565939d8a1SRuslan Bukin uint32_t src_addr;
4575939d8a1SRuslan Bukin uint32_t dst_addr;
4585939d8a1SRuslan Bukin uint32_t addr;
4595939d8a1SRuslan Bukin uint32_t len;
4605939d8a1SRuslan Bukin uint32_t tmp;
4615939d8a1SRuslan Bukin int i;
4625939d8a1SRuslan Bukin
4635939d8a1SRuslan Bukin dprintf("%s: sg_n %d\n", __func__, sg_n);
4645939d8a1SRuslan Bukin
4655939d8a1SRuslan Bukin sc = device_get_softc(dev);
4665939d8a1SRuslan Bukin
4675939d8a1SRuslan Bukin chan = (struct axidma_channel *)xchan->chan;
4685939d8a1SRuslan Bukin xdma = xchan->xdma;
4695939d8a1SRuslan Bukin data = xdma->data;
4705939d8a1SRuslan Bukin
4715939d8a1SRuslan Bukin if (sg_n == 0)
4725939d8a1SRuslan Bukin return (0);
4735939d8a1SRuslan Bukin
4745939d8a1SRuslan Bukin tmp = 0;
4755939d8a1SRuslan Bukin
4765939d8a1SRuslan Bukin for (i = 0; i < sg_n; i++) {
4775939d8a1SRuslan Bukin src_addr = (uint32_t)sg[i].src_addr;
4785939d8a1SRuslan Bukin dst_addr = (uint32_t)sg[i].dst_addr;
4795939d8a1SRuslan Bukin len = (uint32_t)sg[i].len;
4805939d8a1SRuslan Bukin
4815939d8a1SRuslan Bukin dprintf("%s(%d): src %x dst %x len %d\n", __func__,
4825939d8a1SRuslan Bukin data->id, src_addr, dst_addr, len);
4835939d8a1SRuslan Bukin
4845939d8a1SRuslan Bukin desc = chan->descs[chan->idx_head];
4855939d8a1SRuslan Bukin if (sg[i].direction == XDMA_MEM_TO_DEV)
4865939d8a1SRuslan Bukin desc->phys = src_addr;
4875939d8a1SRuslan Bukin else
4885939d8a1SRuslan Bukin desc->phys = dst_addr;
4895939d8a1SRuslan Bukin desc->status = 0;
4905939d8a1SRuslan Bukin desc->control = len;
4915939d8a1SRuslan Bukin if (sg[i].first == 1)
4925939d8a1SRuslan Bukin desc->control |= BD_CONTROL_TXSOF;
4935939d8a1SRuslan Bukin if (sg[i].last == 1)
4945939d8a1SRuslan Bukin desc->control |= BD_CONTROL_TXEOF;
4955939d8a1SRuslan Bukin
4965939d8a1SRuslan Bukin tmp = chan->idx_head;
4975939d8a1SRuslan Bukin
4985939d8a1SRuslan Bukin atomic_add_int(&chan->descs_used_count, 1);
4995939d8a1SRuslan Bukin chan->idx_head = axidma_next_desc(chan, chan->idx_head);
5005939d8a1SRuslan Bukin }
5015939d8a1SRuslan Bukin
5025939d8a1SRuslan Bukin dprintf("%s(%d): _curdesc %x\n", __func__, data->id,
5035939d8a1SRuslan Bukin READ8(sc, AXI_CURDESC(data->id)));
5045939d8a1SRuslan Bukin dprintf("%s(%d): _curdesc %x\n", __func__, data->id,
5055939d8a1SRuslan Bukin READ8(sc, AXI_CURDESC(data->id)));
5065939d8a1SRuslan Bukin dprintf("%s(%d): status %x\n", __func__, data->id,
5075939d8a1SRuslan Bukin READ4(sc, AXI_DMASR(data->id)));
5085939d8a1SRuslan Bukin
5095939d8a1SRuslan Bukin addr = chan->descs_phys[tmp];
5105939d8a1SRuslan Bukin WRITE8(sc, AXI_TAILDESC(data->id), addr);
5115939d8a1SRuslan Bukin
5125939d8a1SRuslan Bukin return (0);
5135939d8a1SRuslan Bukin }
5145939d8a1SRuslan Bukin
5155939d8a1SRuslan Bukin static int
axidma_channel_prep_sg(device_t dev,struct xdma_channel * xchan)5165939d8a1SRuslan Bukin axidma_channel_prep_sg(device_t dev, struct xdma_channel *xchan)
5175939d8a1SRuslan Bukin {
5185939d8a1SRuslan Bukin xdma_controller_t *xdma;
5195939d8a1SRuslan Bukin struct axidma_fdt_data *data;
5205939d8a1SRuslan Bukin struct axidma_channel *chan;
5215939d8a1SRuslan Bukin struct axidma_desc *desc;
5225939d8a1SRuslan Bukin struct axidma_softc *sc;
5235939d8a1SRuslan Bukin uint32_t addr;
5245939d8a1SRuslan Bukin uint32_t reg;
5255939d8a1SRuslan Bukin int ret;
5265939d8a1SRuslan Bukin int i;
5275939d8a1SRuslan Bukin
5285939d8a1SRuslan Bukin sc = device_get_softc(dev);
5295939d8a1SRuslan Bukin
5305939d8a1SRuslan Bukin chan = (struct axidma_channel *)xchan->chan;
5315939d8a1SRuslan Bukin xdma = xchan->xdma;
5325939d8a1SRuslan Bukin data = xdma->data;
5335939d8a1SRuslan Bukin
5345939d8a1SRuslan Bukin dprintf("%s(%d)\n", __func__, data->id);
5355939d8a1SRuslan Bukin
5365939d8a1SRuslan Bukin ret = axidma_desc_alloc(sc, xchan, sizeof(struct axidma_desc));
5375939d8a1SRuslan Bukin if (ret != 0) {
5385939d8a1SRuslan Bukin device_printf(sc->dev,
5395939d8a1SRuslan Bukin "%s: Can't allocate descriptors.\n", __func__);
5405939d8a1SRuslan Bukin return (-1);
5415939d8a1SRuslan Bukin }
5425939d8a1SRuslan Bukin
5435939d8a1SRuslan Bukin for (i = 0; i < chan->descs_num; i++) {
5445939d8a1SRuslan Bukin desc = chan->descs[i];
5455939d8a1SRuslan Bukin bzero(desc, sizeof(struct axidma_desc));
5465939d8a1SRuslan Bukin
5475939d8a1SRuslan Bukin if (i == (chan->descs_num - 1))
5485939d8a1SRuslan Bukin desc->next = chan->descs_phys[0];
5495939d8a1SRuslan Bukin else
5505939d8a1SRuslan Bukin desc->next = chan->descs_phys[i + 1];
5515939d8a1SRuslan Bukin desc->status = 0;
5525939d8a1SRuslan Bukin desc->control = 0;
5535939d8a1SRuslan Bukin
5545939d8a1SRuslan Bukin dprintf("%s(%d): desc %d vaddr %lx next paddr %x\n", __func__,
5555939d8a1SRuslan Bukin data->id, i, (uint64_t)desc, le32toh(desc->next));
5565939d8a1SRuslan Bukin }
5575939d8a1SRuslan Bukin
5585939d8a1SRuslan Bukin addr = chan->descs_phys[0];
5595939d8a1SRuslan Bukin WRITE8(sc, AXI_CURDESC(data->id), addr);
5605939d8a1SRuslan Bukin
5615939d8a1SRuslan Bukin reg = READ4(sc, AXI_DMACR(data->id));
5625939d8a1SRuslan Bukin reg |= DMACR_IOC_IRQEN | DMACR_DLY_IRQEN | DMACR_ERR_IRQEN;
5635939d8a1SRuslan Bukin WRITE4(sc, AXI_DMACR(data->id), reg);
5645939d8a1SRuslan Bukin reg |= DMACR_RS;
5655939d8a1SRuslan Bukin WRITE4(sc, AXI_DMACR(data->id), reg);
5665939d8a1SRuslan Bukin
5675939d8a1SRuslan Bukin return (0);
5685939d8a1SRuslan Bukin }
5695939d8a1SRuslan Bukin
5705939d8a1SRuslan Bukin static int
axidma_channel_control(device_t dev,xdma_channel_t * xchan,int cmd)5715939d8a1SRuslan Bukin axidma_channel_control(device_t dev, xdma_channel_t *xchan, int cmd)
5725939d8a1SRuslan Bukin {
5735939d8a1SRuslan Bukin
5745939d8a1SRuslan Bukin switch (cmd) {
5755939d8a1SRuslan Bukin case XDMA_CMD_BEGIN:
5765939d8a1SRuslan Bukin case XDMA_CMD_TERMINATE:
5775939d8a1SRuslan Bukin case XDMA_CMD_PAUSE:
5785939d8a1SRuslan Bukin /* TODO: implement me */
5795939d8a1SRuslan Bukin return (-1);
5805939d8a1SRuslan Bukin }
5815939d8a1SRuslan Bukin
5825939d8a1SRuslan Bukin return (0);
5835939d8a1SRuslan Bukin }
5845939d8a1SRuslan Bukin
5855939d8a1SRuslan Bukin #ifdef FDT
5865939d8a1SRuslan Bukin static int
axidma_ofw_md_data(device_t dev,pcell_t * cells,int ncells,void ** ptr)5875939d8a1SRuslan Bukin axidma_ofw_md_data(device_t dev, pcell_t *cells, int ncells, void **ptr)
5885939d8a1SRuslan Bukin {
5895939d8a1SRuslan Bukin struct axidma_fdt_data *data;
5905939d8a1SRuslan Bukin
5915939d8a1SRuslan Bukin if (ncells != 1)
5925939d8a1SRuslan Bukin return (-1);
5935939d8a1SRuslan Bukin
5945939d8a1SRuslan Bukin data = malloc(sizeof(struct axidma_fdt_data),
5955939d8a1SRuslan Bukin M_DEVBUF, (M_WAITOK | M_ZERO));
5965939d8a1SRuslan Bukin data->id = cells[0];
5975939d8a1SRuslan Bukin
5985939d8a1SRuslan Bukin *ptr = data;
5995939d8a1SRuslan Bukin
6005939d8a1SRuslan Bukin return (0);
6015939d8a1SRuslan Bukin }
6025939d8a1SRuslan Bukin #endif
6035939d8a1SRuslan Bukin
6045939d8a1SRuslan Bukin static device_method_t axidma_methods[] = {
6055939d8a1SRuslan Bukin /* Device interface */
6065939d8a1SRuslan Bukin DEVMETHOD(device_probe, axidma_probe),
6075939d8a1SRuslan Bukin DEVMETHOD(device_attach, axidma_attach),
6085939d8a1SRuslan Bukin DEVMETHOD(device_detach, axidma_detach),
6095939d8a1SRuslan Bukin
6105939d8a1SRuslan Bukin /* xDMA Interface */
6115939d8a1SRuslan Bukin DEVMETHOD(xdma_channel_alloc, axidma_channel_alloc),
6125939d8a1SRuslan Bukin DEVMETHOD(xdma_channel_free, axidma_channel_free),
6135939d8a1SRuslan Bukin DEVMETHOD(xdma_channel_control, axidma_channel_control),
6145939d8a1SRuslan Bukin
6155939d8a1SRuslan Bukin /* xDMA SG Interface */
6165939d8a1SRuslan Bukin DEVMETHOD(xdma_channel_capacity, axidma_channel_capacity),
6175939d8a1SRuslan Bukin DEVMETHOD(xdma_channel_prep_sg, axidma_channel_prep_sg),
6185939d8a1SRuslan Bukin DEVMETHOD(xdma_channel_submit_sg, axidma_channel_submit_sg),
6195939d8a1SRuslan Bukin
6205939d8a1SRuslan Bukin #ifdef FDT
6215939d8a1SRuslan Bukin DEVMETHOD(xdma_ofw_md_data, axidma_ofw_md_data),
6225939d8a1SRuslan Bukin #endif
6235939d8a1SRuslan Bukin
6245939d8a1SRuslan Bukin DEVMETHOD_END
6255939d8a1SRuslan Bukin };
6265939d8a1SRuslan Bukin
6275939d8a1SRuslan Bukin static driver_t axidma_driver = {
6285939d8a1SRuslan Bukin "axidma",
6295939d8a1SRuslan Bukin axidma_methods,
6305939d8a1SRuslan Bukin sizeof(struct axidma_softc),
6315939d8a1SRuslan Bukin };
6325939d8a1SRuslan Bukin
633*90b8b224SJohn Baldwin EARLY_DRIVER_MODULE(axidma, simplebus, axidma_driver, 0, 0,
6345939d8a1SRuslan Bukin BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);
635