Index: sys/dev/altera/softdma/a_api.h =================================================================== --- /dev/null +++ sys/dev/altera/softdma/a_api.h @@ -0,0 +1,96 @@ +/*- + * Copyright (c) 2012 Bjoern A. Zeeb + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-11-C-0249) + * ("MRC2"), as part of the DARPA MRC research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + * + * $FreeBSD$ + */ +/* + * Altera, Embedded Peripherals IP, User Guide, v. 11.0, June 2011. + * UG-01085-11.0. + */ + +#ifndef _A_API_H +#define _A_API_H + +/* Table 16-1. Memory Map. */ +#define A_ONCHIP_FIFO_MEM_CORE_DATA 0x00 +#define A_ONCHIP_FIFO_MEM_CORE_METADATA 0x04 + +#define A_ONCHIP_FIFO_MEM_CORE_SOP (1<<0) +#define A_ONCHIP_FIFO_MEM_CORE_EOP (1<<1) +#define A_ONCHIP_FIFO_MEM_CORE_EMPTY_MASK 0x000000f7 +#define A_ONCHIP_FIFO_MEM_CORE_EMPTY_SHIFT 2 + /* Reserved (1<<7) */ +#define A_ONCHIP_FIFO_MEM_CORE_CHANNEL_MASK 0x0000ff00 +#define A_ONCHIP_FIFO_MEM_CORE_CHANNEL_SHIFT 8 +#define A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK 0x00ff0000 +#define A_ONCHIP_FIFO_MEM_CORE_ERROR_SHIFT 16 + /* Reserved 0xff000000 */ + +/* Table 16-3. FIFO Status Register Memory Map. */ +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_FILL_LEVEL 0x00 +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_I_STATUS 0x04 +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT 0x08 +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_INT_ENABLE 0x0c +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_ALMOSTFULL 0x10 +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_ALMOSTEMPTY 0x14 + +/* Table 16-5. Status Bit Field Descriptions. */ +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_FULL (1<<0) +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_EMPTY (1<<1) +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_ALMOSTFULL (1<<2) +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_ALMOSTEMPTY (1<<3) +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_OVERFLOW (1<<4) +#define A_ONCHIP_FIFO_MEM_CORE_STATUS_UNDERFLOW (1<<5) + +/* Table 16-6. Event Bit Field Descriptions. */ +/* XXX Datasheet has incorrect bit fields. Validate. */ +#define A_ONCHIP_FIFO_MEM_CORE_EVENT_FULL (1<<0) +#define A_ONCHIP_FIFO_MEM_CORE_EVENT_EMPTY (1<<1) +#define A_ONCHIP_FIFO_MEM_CORE_EVENT_ALMOSTFULL (1<<2) +#define A_ONCHIP_FIFO_MEM_CORE_EVENT_ALMOSTEMPTY (1<<3) +#define A_ONCHIP_FIFO_MEM_CORE_EVENT_OVERFLOW (1<<4) +#define A_ONCHIP_FIFO_MEM_CORE_EVENT_UNDERFLOW (1<<5) + +/* Table 16-7. InterruptEnable Bit Field Descriptions. */ +/* XXX Datasheet has incorrect bit fields. Validate. */ +#define A_ONCHIP_FIFO_MEM_CORE_INTR_FULL (1<<0) +#define A_ONCHIP_FIFO_MEM_CORE_INTR_EMPTY (1<<1) +#define A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTFULL (1<<2) +#define A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTEMPTY (1<<3) +#define A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW (1<<4) +#define A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW (1<<5) +#define A_ONCHIP_FIFO_MEM_CORE_INTR_ALL \ + (A_ONCHIP_FIFO_MEM_CORE_INTR_EMPTY| \ + A_ONCHIP_FIFO_MEM_CORE_INTR_FULL| \ + A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTEMPTY| \ + A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTFULL| \ + A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW| \ + A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW) + +#endif /* _A_API_H */ Index: sys/dev/altera/softdma/softdma.c =================================================================== --- /dev/null +++ sys/dev/altera/softdma/softdma.c @@ -0,0 +1,863 @@ +/*- + * Copyright (c) 2017 Ruslan Bukin + * All rights reserved. + * + * This software was developed by SRI International and the University of + * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237 + * ("CTSRD"), as part of the DARPA CRASH research programme. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +/* Altera FIFO driver with xDMA(4) interface. */ + +#include +__FBSDID("$FreeBSD$"); + +#include "opt_platform.h" +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#ifdef FDT +#include +#include +#include +#endif + +#include + +#include +#include "xdma_if.h" + +#define AVALON_FIFO_TX_BASIC_OPTS_DEPTH 16 +#define SOFTDMA_NCHANNELS 1 +#define CONTROL_GEN_SOP (1 << 0) +#define CONTROL_GEN_EOP (1 << 1) +#define CONTROL_OWN (1 << 31) + +#define ATSE_RX_EVENTS (A_ONCHIP_FIFO_MEM_CORE_INTR_FULL |\ + A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW |\ + A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW) +#define ATSE_TX_EVENTS (A_ONCHIP_FIFO_MEM_CORE_INTR_EMPTY |\ + A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW |\ + A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW) + +struct softdma_channel { + struct softdma_softc *sc; + struct mtx mtx; + xdma_channel_t *xchan; + struct proc *p; + int used; + int index; + int run; + uint32_t idx_tail; + uint32_t idx_head; + struct softdma_desc *descs; + + uint32_t descs_num; + uint32_t descs_used_count; +}; + +struct softdma_desc { + uint64_t src_addr; + uint64_t dst_addr; + uint32_t len; + uint32_t access_width; + uint32_t count; + uint16_t src_incr; + uint16_t dst_incr; + uint32_t direction; + struct softdma_desc *next; + uint32_t transfered; + uint32_t status; + uint32_t reserved; + uint32_t control; +}; + +struct softdma_softc { + device_t dev; + struct resource *res[3]; + bus_space_tag_t bst; + bus_space_handle_t bsh; + bus_space_tag_t bst_c; + bus_space_handle_t bsh_c; + void *ih; + struct softdma_channel channels[SOFTDMA_NCHANNELS]; +}; + +static struct resource_spec softdma_spec[] = { + { SYS_RES_MEMORY, 0, RF_ACTIVE }, /* fifo */ + { SYS_RES_MEMORY, 1, RF_ACTIVE }, /* core */ + { SYS_RES_IRQ, 0, RF_ACTIVE }, + { -1, 0 } +}; + +static int softdma_probe(device_t dev); +static int softdma_attach(device_t dev); +static int softdma_detach(device_t dev); + +static inline uint32_t +softdma_next_desc(struct softdma_channel *chan, uint32_t curidx) +{ + + return ((curidx + 1) % chan->descs_num); +} + +static void +softdma_mem_write(struct softdma_softc *sc, uint32_t reg, uint32_t val) +{ + + bus_write_4(sc->res[0], reg, htole32(val)); +} + +static uint32_t +softdma_mem_read(struct softdma_softc *sc, uint32_t reg) +{ + uint32_t val; + + val = bus_read_4(sc->res[0], reg); + + return (le32toh(val)); +} + +static void +softdma_memc_write(struct softdma_softc *sc, uint32_t reg, uint32_t val) +{ + + bus_write_4(sc->res[1], reg, htole32(val)); +} + +static uint32_t +softdma_memc_read(struct softdma_softc *sc, uint32_t reg) +{ + uint32_t val; + + val = bus_read_4(sc->res[1], reg); + + return (le32toh(val)); +} + +static uint32_t +softdma_fill_level(struct softdma_softc *sc) +{ + uint32_t val; + + val = softdma_memc_read(sc, + A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_FILL_LEVEL); + + return (val); +} + +static void +softdma_intr(void *arg) +{ + struct softdma_channel *chan; + struct softdma_softc *sc; + int reg; + int err; + + sc = arg; + + chan = &sc->channels[0]; + + reg = softdma_memc_read(sc, A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT); + + if (reg & (A_ONCHIP_FIFO_MEM_CORE_EVENT_OVERFLOW | + A_ONCHIP_FIFO_MEM_CORE_EVENT_UNDERFLOW)) { + /* Errors */ + err = (((reg & A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK) >> \ + A_ONCHIP_FIFO_MEM_CORE_ERROR_SHIFT) & 0xff); + } + + if (reg != 0) { + softdma_memc_write(sc, A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT, reg); + chan->run = 1; + wakeup(chan); + } +} + +static int +softdma_probe(device_t dev) +{ + + if (!ofw_bus_status_okay(dev)) + return (ENXIO); + + if (!ofw_bus_is_compatible(dev, "freebsd,softdma")) + return (ENXIO); + + device_set_desc(dev, "SoftDMA"); + + return (BUS_PROBE_DEFAULT); +} + +static int +softdma_attach(device_t dev) +{ + struct softdma_softc *sc; + phandle_t xref, node; + int err; + + sc = device_get_softc(dev); + sc->dev = dev; + + if (bus_alloc_resources(dev, softdma_spec, sc->res)) { + device_printf(dev, "could not allocate resources for device\n"); + return (ENXIO); + } + + /* FIFO memory interface */ + sc->bst = rman_get_bustag(sc->res[0]); + sc->bsh = rman_get_bushandle(sc->res[0]); + + /* FIFO control memory interface */ + sc->bst_c = rman_get_bustag(sc->res[1]); + sc->bsh_c = rman_get_bushandle(sc->res[1]); + + /* Setup interrupt handler */ + err = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE, + NULL, softdma_intr, sc, &sc->ih); + 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); + + return (0); +} + +static int +softdma_detach(device_t dev) +{ + struct softdma_softc *sc; + + sc = device_get_softc(dev); + + return (0); +} + +static int +softdma_process_tx(struct softdma_channel *chan, struct softdma_desc *desc) +{ + struct softdma_softc *sc; + uint32_t src_offs, dst_offs; + uint32_t reg; + uint32_t fill_level; + uint32_t leftm; + uint32_t tmp; + uint32_t val; + uint32_t c; + + sc = chan->sc; + + fill_level = softdma_fill_level(sc); + while (fill_level == AVALON_FIFO_TX_BASIC_OPTS_DEPTH) { + fill_level = softdma_fill_level(sc); + } + + /* Set start of packet. */ + if (desc->control & CONTROL_GEN_SOP) { + reg = 0; + reg |= A_ONCHIP_FIFO_MEM_CORE_SOP; + softdma_mem_write(sc, A_ONCHIP_FIFO_MEM_CORE_METADATA, reg); + } + + src_offs = dst_offs = 0; + c = 0; + while ((desc->len - c) >= 4) { + val = *(uint32_t *)(desc->src_addr + src_offs); + bus_write_4(sc->res[0], A_ONCHIP_FIFO_MEM_CORE_DATA, val); + if (desc->src_incr) + src_offs += 4; + if (desc->dst_incr) + dst_offs += 4; + fill_level += 1; + + while (fill_level == AVALON_FIFO_TX_BASIC_OPTS_DEPTH) { + fill_level = softdma_fill_level(sc); + } + c += 4; + } + + val = 0; + leftm = (desc->len - c); + + switch (leftm) { + case 1: + val = *(uint8_t *)(desc->src_addr + src_offs); + val <<= 24; + src_offs += 1; + break; + case 2: + case 3: + val = *(uint16_t *)(desc->src_addr + src_offs); + val <<= 16; + src_offs += 2; + + if (leftm == 3) { + tmp = *(uint8_t *)(desc->src_addr + src_offs); + val |= (tmp << 8); + src_offs += 1; + } + break; + case 0: + default: + break; + } + + /* Set end of packet. */ + reg = 0; + if (desc->control & CONTROL_GEN_EOP) { + reg |= A_ONCHIP_FIFO_MEM_CORE_EOP; + } + reg |= ((4 - leftm) << A_ONCHIP_FIFO_MEM_CORE_EMPTY_SHIFT); + softdma_mem_write(sc, A_ONCHIP_FIFO_MEM_CORE_METADATA, reg); + + /* Ensure there is a FIFO entry available. */ + fill_level = softdma_fill_level(sc); + while (fill_level == AVALON_FIFO_TX_BASIC_OPTS_DEPTH) { + fill_level = softdma_fill_level(sc); + }; + + /* Final write */ + bus_write_4(sc->res[0], A_ONCHIP_FIFO_MEM_CORE_DATA, val); + + return (dst_offs); +} + +static int +softdma_process_rx(struct softdma_channel *chan, struct softdma_desc *desc) +{ + uint32_t src_offs, dst_offs; + struct softdma_softc *sc; + uint32_t fill_level; + uint32_t empty; + uint32_t meta; + uint32_t data; + int sop_rcvd; + int timeout; + size_t len; + int error; + + sc = chan->sc; + empty = 0; + src_offs = dst_offs = 0; + error = 0; + + fill_level = softdma_fill_level(sc); + if (fill_level == 0) { + /* Nothing to receive. */ + return (0); + } + + len = desc->len; + + sop_rcvd = 0; + while (fill_level) { + empty = 0; + data = bus_read_4(sc->res[0], A_ONCHIP_FIFO_MEM_CORE_DATA); + meta = softdma_mem_read(sc, A_ONCHIP_FIFO_MEM_CORE_METADATA); + + if (meta & A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK) { + error = 1; + break; + } + + if ((meta & A_ONCHIP_FIFO_MEM_CORE_CHANNEL_MASK) != 0) { + error = 1; + break; + } + + if (meta & A_ONCHIP_FIFO_MEM_CORE_SOP) { + sop_rcvd = 1; + } + + if (meta & A_ONCHIP_FIFO_MEM_CORE_EOP) { + empty = (meta & A_ONCHIP_FIFO_MEM_CORE_EMPTY_MASK) >> + A_ONCHIP_FIFO_MEM_CORE_EMPTY_SHIFT; + } + + if (sop_rcvd == 0) { + error = 1; + break; + } + + if (empty == 0) { + *(uint32_t *)(desc->dst_addr + dst_offs) = data; + dst_offs += 4; + } else if (empty == 1) { + *(uint16_t *)(desc->dst_addr + dst_offs) = ((data >> 16) & 0xffff); + dst_offs += 2; + + *(uint8_t *)(desc->dst_addr + dst_offs) = ((data >> 8) & 0xff); + dst_offs += 1; + } else { + panic("empty %d\n", empty); + } + + if (meta & A_ONCHIP_FIFO_MEM_CORE_EOP) { + break; + } + + fill_level = softdma_fill_level(sc); + timeout = 100; + while (fill_level == 0 && timeout--) { + fill_level = softdma_fill_level(sc); + } + if (timeout == 0) { + /* No EOP received. Broken packet. */ + error = 1; + break; + } + } + + if (error) { + return (-1); + } + + return (dst_offs); +} + +static uint32_t +softdma_process_descriptors(struct softdma_channel *chan, xdma_transfer_status_t *status) +{ + struct xdma_channel *xchan; + struct softdma_desc *desc; + struct softdma_softc *sc; + xdma_transfer_status_t st; + int ret; + + sc = chan->sc; + + xchan = chan->xchan; + + desc = &chan->descs[chan->idx_tail]; + + while (desc != NULL) { + + if ((desc->control & CONTROL_OWN) == 0) { + break; + } + + if (desc->direction == XDMA_MEM_TO_DEV) { + ret = softdma_process_tx(chan, desc); + } else { + ret = softdma_process_rx(chan, desc); + if (ret == 0) { + /* No new data available. */ + break; + } + } + + /* Descriptor processed. */ + desc->control = 0; + + if (ret >= 0) { + st.error = 0; + st.transferred = ret; + } else { + st.error = ret; + st.transferred = 0; + } + + xchan_seg_done(xchan, &st); + atomic_subtract_int(&chan->descs_used_count, 1); + + if (ret >= 0) { + status->transferred += ret; + } else { + status->error = 1; + break; + } + + chan->idx_tail = softdma_next_desc(chan, chan->idx_tail); + + /* Process next descriptor, if any. */ + desc = desc->next; + } + + return (0); +} + +static void +softdma_worker(void *arg) +{ + xdma_transfer_status_t status; + struct softdma_channel *chan; + struct softdma_softc *sc; + + chan = arg; + + sc = chan->sc; + + while (1) { + mtx_lock(&chan->mtx); + + do { + mtx_sleep(chan, &chan->mtx, 0, "softdma_wait", hz / 2); + } while (chan->run == 0); + + status.error = 0; + status.transferred = 0; + + softdma_process_descriptors(chan, &status); + + /* Finish operation */ + chan->run = 0; + xdma_callback(chan->xchan, &status); + + mtx_unlock(&chan->mtx); + } + +} + +static int +softdma_proc_create(struct softdma_channel *chan) +{ + struct softdma_softc *sc; + + sc = chan->sc; + + if (chan->p != NULL) { + /* Already created */ + return (0); + } + + mtx_init(&chan->mtx, "SoftDMA", NULL, MTX_DEF); + + if (kproc_create(softdma_worker, (void *)chan, &chan->p, 0, 0, + "softdma_worker") != 0) { + device_printf(sc->dev, + "%s: Failed to create worker thread.\n", __func__); + return (-1); + } + + return (0); +} + +static int +softdma_channel_alloc(device_t dev, struct xdma_channel *xchan) +{ + struct softdma_channel *chan; + struct softdma_softc *sc; + int i; + + sc = device_get_softc(dev); + + for (i = 0; i < SOFTDMA_NCHANNELS; i++) { + chan = &sc->channels[i]; + if (chan->used == 0) { + chan->xchan = xchan; + xchan->chan = (void *)chan; + chan->index = i; + chan->idx_head = 0; + chan->idx_tail = 0; + chan->descs_used_count = 0; + chan->descs_num = 1024; + chan->sc = sc; + + if (softdma_proc_create(chan) != 0) { + return (-1); + } + + chan->used = 1; + + return (0); + } + } + + return (-1); +} + +static int +softdma_channel_free(device_t dev, struct xdma_channel *xchan) +{ + struct softdma_channel *chan; + struct softdma_softc *sc; + + sc = device_get_softc(dev); + + chan = (struct softdma_channel *)xchan->chan; + + if (chan->descs != NULL) { + free(chan->descs, M_DEVBUF); + } + + chan->used = 0; + + return (0); +} + +static int +softdma_desc_alloc(struct xdma_channel *xchan) +{ + struct softdma_channel *chan; + uint32_t nsegments; + + chan = (struct softdma_channel *)xchan->chan; + + nsegments = chan->descs_num; + + chan->descs = malloc(nsegments * sizeof(struct softdma_desc), + M_DEVBUF, (M_WAITOK | M_ZERO)); + + return (0); +} + +static int +softdma_channel_prep_sg(device_t dev, struct xdma_channel *xchan) +{ + struct softdma_channel *chan; + struct softdma_desc *desc; + struct softdma_softc *sc; + int ret; + int i; + + sc = device_get_softc(dev); + + chan = (struct softdma_channel *)xchan->chan; + + ret = softdma_desc_alloc(xchan); + 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]; + + if (i == (chan->descs_num - 1)) { + desc->next = &chan->descs[0]; + } else { + desc->next = &chan->descs[i+1]; + } + } + + return (0); +} + +static int +softdma_channel_capacity(device_t dev, xdma_channel_t *xchan, + uint32_t *capacity) +{ + struct softdma_channel *chan; + uint32_t c; + + chan = (struct softdma_channel *)xchan->chan; + + /* At least one descriptor must be left empty. */ + c = (chan->descs_num - chan->descs_used_count - 1); + + *capacity = c; + + return (0); +} + +static int +softdma_channel_submit_sg(device_t dev, struct xdma_channel *xchan, + struct xdma_sglist *sg, uint32_t sg_n) +{ + struct softdma_channel *chan; + struct softdma_desc *desc; + struct softdma_softc *sc; + uint32_t enqueued; + uint32_t saved_dir; + uint32_t tmp; + uint32_t len; + int i; + + sc = device_get_softc(dev); + + chan = (struct softdma_channel *)xchan->chan; + + enqueued = 0; + + for (i = 0; i < sg_n; i++) { + len = (uint32_t)sg[i].len; + + desc = &chan->descs[chan->idx_head]; + desc->src_addr = sg[i].src_addr; + desc->dst_addr = sg[i].dst_addr; + if (sg[i].direction == XDMA_MEM_TO_DEV) { + desc->src_incr = 1; + desc->dst_incr = 0; + } else { + desc->src_incr = 0; + desc->dst_incr = 1; + } + desc->direction = sg[i].direction; + saved_dir = sg[i].direction; + desc->len = len; + desc->transfered = 0; + desc->status = 0; + desc->reserved = 0; + desc->control = 0; + + if (sg[i].first == 1) { + desc->control |= CONTROL_GEN_SOP; + } + + if (sg[i].last == 1) { + desc->control |= CONTROL_GEN_EOP; + } + + tmp = chan->idx_head; + chan->idx_head = softdma_next_desc(chan, chan->idx_head); + atomic_add_int(&chan->descs_used_count, 1); + desc->control |= CONTROL_OWN; + enqueued += 1; + } + + if (enqueued == 0) { + return (0); + } + + if (saved_dir == XDMA_MEM_TO_DEV) { + chan->run = 1; + wakeup(chan); + } else { + softdma_memc_write(sc, A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_INT_ENABLE, ATSE_RX_EVENTS); + } + + return (0); +} + +static int +softdma_channel_prep_cyclic(device_t dev, struct xdma_channel *xchan) +{ + + return (0); +} + +static int +softdma_channel_prep_memcpy(device_t dev, struct xdma_channel *xchan) +{ + struct softdma_channel *chan; + struct softdma_desc *desc; + struct softdma_softc *sc; + xdma_config_t *conf; + int ret; + + sc = device_get_softc(dev); + + chan = (struct softdma_channel *)xchan->chan; + + ret = softdma_desc_alloc(xchan); + if (ret != 0) { + device_printf(sc->dev, + "%s: Can't allocate descriptors.\n", __func__); + return (-1); + } + + conf = &xchan->conf; + + desc = &chan->descs[0]; + + desc->src_addr = conf->src_addr; + desc->dst_addr = conf->dst_addr; + desc->len = conf->block_len; + desc->src_incr = 1; + desc->dst_incr = 1; + desc->next = NULL; + + return (0); +} + +static int +softdma_channel_control(device_t dev, xdma_channel_t *xchan, int cmd) +{ + struct softdma_channel *chan; + struct softdma_softc *sc; + + sc = device_get_softc(dev); + + chan = (struct softdma_channel *)xchan->chan; + + switch (cmd) { + case XDMA_CMD_BEGIN: + case XDMA_CMD_TERMINATE: + case XDMA_CMD_PAUSE: + /* TODO: implement me */ + return (-1); + } + + return (0); +} + +#ifdef FDT +static int +softdma_ofw_md_data(device_t dev, pcell_t *cells, int ncells, void **ptr) +{ + + return (0); +} +#endif + +static device_method_t softdma_methods[] = { + /* Device interface */ + DEVMETHOD(device_probe, softdma_probe), + DEVMETHOD(device_attach, softdma_attach), + DEVMETHOD(device_detach, softdma_detach), + + /* xDMA Interface */ + DEVMETHOD(xdma_channel_alloc, softdma_channel_alloc), + DEVMETHOD(xdma_channel_free, softdma_channel_free), + DEVMETHOD(xdma_channel_prep_cyclic, softdma_channel_prep_cyclic), + DEVMETHOD(xdma_channel_prep_memcpy, softdma_channel_prep_memcpy), + DEVMETHOD(xdma_channel_control, softdma_channel_control), + + DEVMETHOD(xdma_channel_prep_sg, softdma_channel_prep_sg), + DEVMETHOD(xdma_channel_submit_sg, softdma_channel_submit_sg), + DEVMETHOD(xdma_channel_capacity, softdma_channel_capacity), +#ifdef FDT + DEVMETHOD(xdma_ofw_md_data, softdma_ofw_md_data), +#endif + + DEVMETHOD_END +}; + +static driver_t softdma_driver = { + "softdma", + softdma_methods, + sizeof(struct softdma_softc), +}; + +static devclass_t softdma_devclass; + +EARLY_DRIVER_MODULE(softdma, simplebus, softdma_driver, softdma_devclass, 0, 0, + BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);