xref: /freebsd-src/sys/dev/xilinx/axidma.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
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