diff options
Diffstat (limited to 'sys/dev/xilinx/axidma.c')
| -rw-r--r-- | sys/dev/xilinx/axidma.c | 528 |
1 files changed, 45 insertions, 483 deletions
diff --git a/sys/dev/xilinx/axidma.c b/sys/dev/xilinx/axidma.c index 77a46c712980..29757a335dee 100644 --- a/sys/dev/xilinx/axidma.c +++ b/sys/dev/xilinx/axidma.c @@ -1,7 +1,7 @@ /*- * SPDX-License-Identifier: BSD-2-Clause * - * Copyright (c) 2019 Ruslan Bukin <br@bsdpad.com> + * Copyright (c) 2019-2025 Ruslan Bukin <br@bsdpad.com> * * This software was developed by SRI International and the University of * Cambridge Computer Laboratory (Department of Computer Science and @@ -30,13 +30,11 @@ * SUCH DAMAGE. */ -/* Xilinx AXI DMA controller driver. */ +/* + * Xilinx AXI Ethernet DMA controller driver. + */ -#include <sys/cdefs.h> -#include "opt_platform.h" -#include <sys/param.h> #include <sys/systm.h> -#include <sys/conf.h> #include <sys/bus.h> #include <sys/kernel.h> #include <sys/module.h> @@ -44,67 +42,32 @@ #include <machine/bus.h> -#include <vm/vm.h> -#include <vm/vm_extern.h> -#include <vm/vm_page.h> - -#ifdef FDT -#include <dev/fdt/fdt_common.h> #include <dev/ofw/ofw_bus.h> #include <dev/ofw/ofw_bus_subr.h> -#endif -#include <dev/xdma/xdma.h> #include <dev/xilinx/axidma.h> -#include "xdma_if.h" +#include "axidma_if.h" -#define READ4(_sc, _reg) \ +#define AXIDMA_RD4(_sc, _reg) \ bus_space_read_4(_sc->bst, _sc->bsh, _reg) -#define WRITE4(_sc, _reg, _val) \ +#define AXIDMA_WR4(_sc, _reg, _val) \ bus_space_write_4(_sc->bst, _sc->bsh, _reg, _val) -#define READ8(_sc, _reg) \ +#define AXIDMA_RD8(_sc, _reg) \ bus_space_read_8(_sc->bst, _sc->bsh, _reg) -#define WRITE8(_sc, _reg, _val) \ +#define AXIDMA_WR8(_sc, _reg, _val) \ bus_space_write_8(_sc->bst, _sc->bsh, _reg, _val) -#define AXIDMA_DEBUG -#undef AXIDMA_DEBUG - -#ifdef AXIDMA_DEBUG -#define dprintf(fmt, ...) printf(fmt, ##__VA_ARGS__) -#else -#define dprintf(fmt, ...) -#endif - -extern struct bus_space memmap_bus; - -struct axidma_channel { - struct axidma_softc *sc; - xdma_channel_t *xchan; - bool used; - int idx_head; - int idx_tail; - - struct axidma_desc **descs; - vm_paddr_t *descs_phys; - uint32_t descs_num; +#define dprintf(fmt, ...) - vm_size_t mem_size; - vm_offset_t mem_paddr; - vm_offset_t mem_vaddr; - - uint32_t descs_used_count; -}; +#define AXIDMA_MAX_CHANNELS 2 struct axidma_softc { device_t dev; - struct resource *res[3]; + struct resource *res[1 + AXIDMA_MAX_CHANNELS]; bus_space_tag_t bst; bus_space_handle_t bsh; - void *ih[2]; - struct axidma_desc desc; - struct axidma_channel channels[AXIDMA_NCHANNELS]; + void *ih[AXIDMA_MAX_CHANNELS]; }; static struct resource_spec axidma_spec[] = { @@ -114,144 +77,19 @@ static struct resource_spec axidma_spec[] = { { -1, 0 } }; -#define HWTYPE_NONE 0 -#define HWTYPE_STD 1 - static struct ofw_compat_data compat_data[] = { - { "xlnx,eth-dma", HWTYPE_STD }, - { NULL, HWTYPE_NONE }, + { "xlnx,eth-dma", 1 }, + { NULL, 0 }, }; -static int axidma_probe(device_t dev); -static int axidma_attach(device_t dev); -static int axidma_detach(device_t dev); - -static inline uint32_t -axidma_next_desc(struct axidma_channel *chan, uint32_t curidx) -{ - - return ((curidx + 1) % chan->descs_num); -} - -static void -axidma_intr(struct axidma_softc *sc, - struct axidma_channel *chan) -{ - xdma_transfer_status_t status; - xdma_transfer_status_t st; - struct axidma_fdt_data *data; - xdma_controller_t *xdma; - struct axidma_desc *desc; - struct xdma_channel *xchan; - uint32_t tot_copied; - int pending; - int errors; - - xchan = chan->xchan; - xdma = xchan->xdma; - data = xdma->data; - - pending = READ4(sc, AXI_DMASR(data->id)); - WRITE4(sc, AXI_DMASR(data->id), pending); - - errors = (pending & (DMASR_DMAINTERR | DMASR_DMASLVERR - | DMASR_DMADECOREERR | DMASR_SGINTERR - | DMASR_SGSLVERR | DMASR_SGDECERR)); - - dprintf("%s: AXI_DMASR %x\n", __func__, - READ4(sc, AXI_DMASR(data->id))); - dprintf("%s: AXI_CURDESC %x\n", __func__, - READ4(sc, AXI_CURDESC(data->id))); - dprintf("%s: AXI_TAILDESC %x\n", __func__, - READ4(sc, AXI_TAILDESC(data->id))); - - tot_copied = 0; - - while (chan->idx_tail != chan->idx_head) { - desc = chan->descs[chan->idx_tail]; - cpu_dcache_wbinv_range((vm_offset_t)desc, - sizeof(struct axidma_desc)); - - if ((desc->status & BD_STATUS_CMPLT) == 0) - break; - - st.error = errors; - st.transferred = desc->status & BD_CONTROL_LEN_M; - tot_copied += st.transferred; - xchan_seg_done(xchan, &st); - - chan->idx_tail = axidma_next_desc(chan, chan->idx_tail); - atomic_subtract_int(&chan->descs_used_count, 1); - } - - /* Finish operation */ - status.error = errors; - status.transferred = tot_copied; - xdma_callback(chan->xchan, &status); -} - -static void -axidma_intr_rx(void *arg) -{ - struct axidma_softc *sc; - struct axidma_channel *chan; - - dprintf("%s\n", __func__); - - sc = arg; - chan = &sc->channels[AXIDMA_RX_CHAN]; - - axidma_intr(sc, chan); -} - -static void -axidma_intr_tx(void *arg) -{ - struct axidma_softc *sc; - struct axidma_channel *chan; - - dprintf("%s\n", __func__); - - sc = arg; - chan = &sc->channels[AXIDMA_TX_CHAN]; - - axidma_intr(sc, chan); -} - -static int -axidma_reset(struct axidma_softc *sc, int chan_id) -{ - int timeout; - - WRITE4(sc, AXI_DMACR(chan_id), DMACR_RESET); - - timeout = 100; - do { - if ((READ4(sc, AXI_DMACR(chan_id)) & DMACR_RESET) == 0) - break; - } while (timeout--); - - dprintf("timeout %d\n", timeout); - - if (timeout == 0) - return (-1); - - dprintf("%s: read control after reset: %x\n", - __func__, READ4(sc, AXI_DMACR(chan_id))); - - return (0); -} - static int axidma_probe(device_t dev) { - int hwtype; if (!ofw_bus_status_okay(dev)) return (ENXIO); - hwtype = ofw_bus_search_compatible(dev, compat_data)->ocd_data; - if (hwtype == HWTYPE_NONE) + if (!ofw_bus_search_compatible(dev, compat_data)->ocd_data) return (ENXIO); device_set_desc(dev, "Xilinx AXI DMA"); @@ -264,7 +102,6 @@ axidma_attach(device_t dev) { struct axidma_softc *sc; phandle_t xref, node; - int err; sc = device_get_softc(dev); sc->dev = dev; @@ -278,22 +115,6 @@ axidma_attach(device_t dev) sc->bst = rman_get_bustag(sc->res[0]); sc->bsh = rman_get_bushandle(sc->res[0]); - /* Setup interrupt handler */ - err = bus_setup_intr(dev, sc->res[1], INTR_TYPE_MISC | INTR_MPSAFE, - NULL, axidma_intr_tx, sc, &sc->ih[0]); - if (err) { - device_printf(dev, "Unable to alloc interrupt resource.\n"); - return (ENXIO); - } - - /* Setup interrupt handler */ - err = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE, - NULL, axidma_intr_rx, sc, &sc->ih[1]); - if (err) { - device_printf(dev, "Unable to alloc interrupt resource.\n"); - return (ENXIO); - } - node = ofw_bus_get_node(dev); xref = OF_xref_from_node(node); OF_device_register_xref(xref, dev); @@ -302,331 +123,72 @@ axidma_attach(device_t dev) } static int -axidma_detach(device_t dev) +axidma_reset(device_t dev, int chan_id) { struct axidma_softc *sc; + int timeout; sc = device_get_softc(dev); - bus_teardown_intr(dev, sc->res[1], sc->ih[0]); - bus_teardown_intr(dev, sc->res[2], sc->ih[1]); - bus_release_resources(dev, axidma_spec, sc->res); - - return (0); -} - -static int -axidma_desc_free(struct axidma_softc *sc, struct axidma_channel *chan) -{ - struct xdma_channel *xchan; - - xchan = chan->xchan; - - free(chan->descs, M_DEVBUF); - free(chan->descs_phys, M_DEVBUF); - - pmap_kremove_device(chan->mem_vaddr, chan->mem_size); - kva_free(chan->mem_vaddr, chan->mem_size); - vmem_free(xchan->vmem, chan->mem_paddr, chan->mem_size); - - return (0); -} - -static int -axidma_desc_alloc(struct axidma_softc *sc, struct xdma_channel *xchan, - uint32_t desc_size) -{ - struct axidma_channel *chan; - int nsegments; - int i; - - chan = (struct axidma_channel *)xchan->chan; - nsegments = chan->descs_num; - - chan->descs = malloc(nsegments * sizeof(struct axidma_desc *), - M_DEVBUF, M_NOWAIT | M_ZERO); - if (chan->descs == NULL) { - device_printf(sc->dev, - "%s: Can't allocate memory.\n", __func__); - return (-1); - } - - chan->descs_phys = malloc(nsegments * sizeof(bus_dma_segment_t), - M_DEVBUF, M_NOWAIT | M_ZERO); - chan->mem_size = desc_size * nsegments; - if (vmem_alloc(xchan->vmem, chan->mem_size, M_FIRSTFIT | M_NOWAIT, - &chan->mem_paddr)) { - device_printf(sc->dev, "Failed to allocate memory.\n"); - return (-1); - } - chan->mem_vaddr = kva_alloc(chan->mem_size); - pmap_kenter(chan->mem_vaddr, chan->mem_size, chan->mem_paddr, - VM_MEMATTR_DEFAULT); - - device_printf(sc->dev, "Allocated chunk %lx %lu\n", - chan->mem_paddr, chan->mem_size); - - for (i = 0; i < nsegments; i++) { - chan->descs[i] = (struct axidma_desc *) - ((uint64_t)chan->mem_vaddr + desc_size * i); - chan->descs_phys[i] = chan->mem_paddr + desc_size * i; - } + AXIDMA_WR4(sc, AXI_DMACR(chan_id), DMACR_RESET); - return (0); -} + timeout = 100; -static int -axidma_channel_alloc(device_t dev, struct xdma_channel *xchan) -{ - xdma_controller_t *xdma; - struct axidma_fdt_data *data; - struct axidma_channel *chan; - struct axidma_softc *sc; + do { + if ((AXIDMA_RD4(sc, AXI_DMACR(chan_id)) & DMACR_RESET) == 0) + break; + } while (timeout--); - sc = device_get_softc(dev); + dprintf("timeout %d\n", timeout); - if (xchan->caps & XCHAN_CAP_BUSDMA) { - device_printf(sc->dev, - "Error: busdma operation is not implemented."); + if (timeout == 0) return (-1); - } - xdma = xchan->xdma; - data = xdma->data; - - chan = &sc->channels[data->id]; - if (chan->used == false) { - if (axidma_reset(sc, data->id) != 0) - return (-1); - chan->xchan = xchan; - xchan->caps |= XCHAN_CAP_BOUNCE; - xchan->chan = (void *)chan; - chan->sc = sc; - chan->used = true; - chan->idx_head = 0; - chan->idx_tail = 0; - chan->descs_used_count = 0; - chan->descs_num = AXIDMA_DESCS_NUM; - - return (0); - } - - return (-1); -} - -static int -axidma_channel_free(device_t dev, struct xdma_channel *xchan) -{ - struct axidma_channel *chan; - struct axidma_softc *sc; - - sc = device_get_softc(dev); - - chan = (struct axidma_channel *)xchan->chan; - - axidma_desc_free(sc, chan); - - chan->used = false; - - return (0); -} - -static int -axidma_channel_capacity(device_t dev, xdma_channel_t *xchan, - uint32_t *capacity) -{ - struct axidma_channel *chan; - uint32_t c; - - chan = (struct axidma_channel *)xchan->chan; - - /* At least one descriptor must be left empty. */ - c = (chan->descs_num - chan->descs_used_count - 1); - - *capacity = c; + dprintf("%s: read control after reset: %x\n", + __func__, AXIDMA_RD4(sc, AXI_DMACR(chan_id))); return (0); } -static int -axidma_channel_submit_sg(device_t dev, struct xdma_channel *xchan, - struct xdma_sglist *sg, uint32_t sg_n) +static struct resource * +axidma_memres(device_t dev) { - xdma_controller_t *xdma; - struct axidma_fdt_data *data; - struct axidma_channel *chan; - struct axidma_desc *desc; struct axidma_softc *sc; - uint32_t src_addr; - uint32_t dst_addr; - uint32_t addr; - uint32_t len; - uint32_t tmp; - int i; - - dprintf("%s: sg_n %d\n", __func__, sg_n); sc = device_get_softc(dev); - chan = (struct axidma_channel *)xchan->chan; - xdma = xchan->xdma; - data = xdma->data; - - if (sg_n == 0) - return (0); - - tmp = 0; - - for (i = 0; i < sg_n; i++) { - src_addr = (uint32_t)sg[i].src_addr; - dst_addr = (uint32_t)sg[i].dst_addr; - len = (uint32_t)sg[i].len; - - dprintf("%s(%d): src %x dst %x len %d\n", __func__, - data->id, src_addr, dst_addr, len); - - desc = chan->descs[chan->idx_head]; - if (sg[i].direction == XDMA_MEM_TO_DEV) - desc->phys = src_addr; - else - desc->phys = dst_addr; - desc->status = 0; - desc->control = len; - if (sg[i].first == 1) - desc->control |= BD_CONTROL_TXSOF; - if (sg[i].last == 1) - desc->control |= BD_CONTROL_TXEOF; - - cpu_dcache_wbinv_range((vm_offset_t)desc, - sizeof(struct axidma_desc)); - - tmp = chan->idx_head; - - atomic_add_int(&chan->descs_used_count, 1); - chan->idx_head = axidma_next_desc(chan, chan->idx_head); - } - - dprintf("%s(%d): _curdesc %x\n", __func__, data->id, - READ8(sc, AXI_CURDESC(data->id))); - dprintf("%s(%d): _curdesc %x\n", __func__, data->id, - READ8(sc, AXI_CURDESC(data->id))); - dprintf("%s(%d): status %x\n", __func__, data->id, - READ4(sc, AXI_DMASR(data->id))); - - addr = chan->descs_phys[tmp]; - WRITE8(sc, AXI_TAILDESC(data->id), addr); - - return (0); + return (sc->res[0]); } static int -axidma_channel_prep_sg(device_t dev, struct xdma_channel *xchan) +axidma_setup_cb(device_t dev, int chan_id, void (*cb)(void *), void *arg) { - xdma_controller_t *xdma; - struct axidma_fdt_data *data; - struct axidma_channel *chan; - struct axidma_desc *desc; struct axidma_softc *sc; - uint32_t addr; - uint32_t reg; - int ret; - int i; + int error; sc = device_get_softc(dev); - chan = (struct axidma_channel *)xchan->chan; - xdma = xchan->xdma; - data = xdma->data; - - dprintf("%s(%d)\n", __func__, data->id); - - ret = axidma_desc_alloc(sc, xchan, sizeof(struct axidma_desc)); - if (ret != 0) { - device_printf(sc->dev, - "%s: Can't allocate descriptors.\n", __func__); - return (-1); - } - - for (i = 0; i < chan->descs_num; i++) { - desc = chan->descs[i]; - bzero(desc, sizeof(struct axidma_desc)); - - if (i == (chan->descs_num - 1)) - desc->next = chan->descs_phys[0]; - else - desc->next = chan->descs_phys[i + 1]; - desc->status = 0; - desc->control = 0; - - dprintf("%s(%d): desc %d vaddr %lx next paddr %x\n", __func__, - data->id, i, (uint64_t)desc, le32toh(desc->next)); - } - - addr = chan->descs_phys[0]; - WRITE8(sc, AXI_CURDESC(data->id), addr); - - reg = READ4(sc, AXI_DMACR(data->id)); - reg |= DMACR_IOC_IRQEN | DMACR_DLY_IRQEN | DMACR_ERR_IRQEN; - WRITE4(sc, AXI_DMACR(data->id), reg); - reg |= DMACR_RS; - WRITE4(sc, AXI_DMACR(data->id), reg); - - return (0); -} - -static int -axidma_channel_control(device_t dev, xdma_channel_t *xchan, int cmd) -{ - - switch (cmd) { - case XDMA_CMD_BEGIN: - case XDMA_CMD_TERMINATE: - case XDMA_CMD_PAUSE: - /* TODO: implement me */ - return (-1); - } - - return (0); -} + if (sc->ih[chan_id] != NULL) + return (EEXIST); -#ifdef FDT -static int -axidma_ofw_md_data(device_t dev, pcell_t *cells, int ncells, void **ptr) -{ - struct axidma_fdt_data *data; - - if (ncells != 1) - return (-1); - - data = malloc(sizeof(struct axidma_fdt_data), - M_DEVBUF, (M_WAITOK | M_ZERO)); - data->id = cells[0]; - - *ptr = data; + error = bus_setup_intr(dev, sc->res[chan_id + 1], + INTR_TYPE_MISC | INTR_MPSAFE, NULL, cb, arg, + &sc->ih[chan_id]); + if (error) + device_printf(dev, "Unable to alloc interrupt resource.\n"); - return (0); + return (error); } -#endif static device_method_t axidma_methods[] = { /* Device interface */ DEVMETHOD(device_probe, axidma_probe), DEVMETHOD(device_attach, axidma_attach), - DEVMETHOD(device_detach, axidma_detach), - - /* xDMA Interface */ - DEVMETHOD(xdma_channel_alloc, axidma_channel_alloc), - DEVMETHOD(xdma_channel_free, axidma_channel_free), - DEVMETHOD(xdma_channel_control, axidma_channel_control), - - /* xDMA SG Interface */ - DEVMETHOD(xdma_channel_capacity, axidma_channel_capacity), - DEVMETHOD(xdma_channel_prep_sg, axidma_channel_prep_sg), - DEVMETHOD(xdma_channel_submit_sg, axidma_channel_submit_sg), -#ifdef FDT - DEVMETHOD(xdma_ofw_md_data, axidma_ofw_md_data), -#endif + /* Axidma interface */ + DEVMETHOD(axidma_reset, axidma_reset), + DEVMETHOD(axidma_memres, axidma_memres), + DEVMETHOD(axidma_setup_cb, axidma_setup_cb), DEVMETHOD_END }; |
