xref: /freebsd/sys/dev/xdma/xdma_sg.c (revision 5a51e5e49dd1817c226e8ff8e2d01bdf16f18075)
13d5b3b0aSRuslan Bukin /*-
23d5b3b0aSRuslan Bukin  * Copyright (c) 2018 Ruslan Bukin <br@bsdpad.com>
33d5b3b0aSRuslan Bukin  * All rights reserved.
43d5b3b0aSRuslan Bukin  *
53d5b3b0aSRuslan Bukin  * This software was developed by SRI International and the University of
63d5b3b0aSRuslan Bukin  * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
73d5b3b0aSRuslan Bukin  * ("CTSRD"), as part of the DARPA CRASH research programme.
83d5b3b0aSRuslan Bukin  *
93d5b3b0aSRuslan Bukin  * Redistribution and use in source and binary forms, with or without
103d5b3b0aSRuslan Bukin  * modification, are permitted provided that the following conditions
113d5b3b0aSRuslan Bukin  * are met:
123d5b3b0aSRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
133d5b3b0aSRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
143d5b3b0aSRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
153d5b3b0aSRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
163d5b3b0aSRuslan Bukin  *    documentation and/or other materials provided with the distribution.
173d5b3b0aSRuslan Bukin  *
183d5b3b0aSRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
193d5b3b0aSRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
203d5b3b0aSRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
213d5b3b0aSRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
223d5b3b0aSRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
233d5b3b0aSRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
243d5b3b0aSRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
253d5b3b0aSRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
263d5b3b0aSRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
273d5b3b0aSRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
283d5b3b0aSRuslan Bukin  * SUCH DAMAGE.
293d5b3b0aSRuslan Bukin  */
303d5b3b0aSRuslan Bukin 
313d5b3b0aSRuslan Bukin #include <sys/cdefs.h>
323d5b3b0aSRuslan Bukin __FBSDID("$FreeBSD$");
333d5b3b0aSRuslan Bukin 
343d5b3b0aSRuslan Bukin #include "opt_platform.h"
353d5b3b0aSRuslan Bukin #include <sys/param.h>
363d5b3b0aSRuslan Bukin #include <sys/conf.h>
373d5b3b0aSRuslan Bukin #include <sys/bus.h>
383d5b3b0aSRuslan Bukin #include <sys/kernel.h>
393d5b3b0aSRuslan Bukin #include <sys/malloc.h>
403d5b3b0aSRuslan Bukin #include <sys/mbuf.h>
413d5b3b0aSRuslan Bukin #include <sys/sx.h>
423d5b3b0aSRuslan Bukin 
433d5b3b0aSRuslan Bukin #include <machine/bus.h>
443d5b3b0aSRuslan Bukin 
453d5b3b0aSRuslan Bukin #ifdef FDT
463d5b3b0aSRuslan Bukin #include <dev/fdt/fdt_common.h>
473d5b3b0aSRuslan Bukin #include <dev/ofw/ofw_bus.h>
483d5b3b0aSRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
493d5b3b0aSRuslan Bukin #endif
503d5b3b0aSRuslan Bukin 
513d5b3b0aSRuslan Bukin #include <dev/xdma/xdma.h>
523d5b3b0aSRuslan Bukin 
533d5b3b0aSRuslan Bukin #include <xdma_if.h>
543d5b3b0aSRuslan Bukin 
553d5b3b0aSRuslan Bukin struct seg_load_request {
563d5b3b0aSRuslan Bukin 	struct bus_dma_segment *seg;
573d5b3b0aSRuslan Bukin 	uint32_t nsegs;
583d5b3b0aSRuslan Bukin 	uint32_t error;
593d5b3b0aSRuslan Bukin };
603d5b3b0aSRuslan Bukin 
613d5b3b0aSRuslan Bukin static int
623d5b3b0aSRuslan Bukin _xchan_bufs_alloc(xdma_channel_t *xchan)
633d5b3b0aSRuslan Bukin {
643d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
653d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
663d5b3b0aSRuslan Bukin 	int i;
673d5b3b0aSRuslan Bukin 
683d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
693d5b3b0aSRuslan Bukin 
703d5b3b0aSRuslan Bukin 	for (i = 0; i < xchan->xr_num; i++) {
713d5b3b0aSRuslan Bukin 		xr = &xchan->xr_mem[i];
72*5a51e5e4SRuslan Bukin 		/* TODO: bounce buffer */
733d5b3b0aSRuslan Bukin 	}
743d5b3b0aSRuslan Bukin 
753d5b3b0aSRuslan Bukin 	return (0);
763d5b3b0aSRuslan Bukin }
773d5b3b0aSRuslan Bukin 
783d5b3b0aSRuslan Bukin static int
793d5b3b0aSRuslan Bukin _xchan_bufs_alloc_busdma(xdma_channel_t *xchan)
803d5b3b0aSRuslan Bukin {
813d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
823d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
833d5b3b0aSRuslan Bukin 	int err;
843d5b3b0aSRuslan Bukin 	int i;
853d5b3b0aSRuslan Bukin 
863d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
873d5b3b0aSRuslan Bukin 
883d5b3b0aSRuslan Bukin 	/* Create bus_dma tag */
893d5b3b0aSRuslan Bukin 	err = bus_dma_tag_create(
903d5b3b0aSRuslan Bukin 	    bus_get_dma_tag(xdma->dev),	/* Parent tag. */
913d5b3b0aSRuslan Bukin 	    xchan->alignment,		/* alignment */
923d5b3b0aSRuslan Bukin 	    xchan->boundary,		/* boundary */
933d5b3b0aSRuslan Bukin 	    xchan->lowaddr,		/* lowaddr */
943d5b3b0aSRuslan Bukin 	    xchan->highaddr,		/* highaddr */
953d5b3b0aSRuslan Bukin 	    NULL, NULL,			/* filter, filterarg */
963d5b3b0aSRuslan Bukin 	    xchan->maxsegsize * xchan->maxnsegs, /* maxsize */
973d5b3b0aSRuslan Bukin 	    xchan->maxnsegs,		/* nsegments */
983d5b3b0aSRuslan Bukin 	    xchan->maxsegsize,		/* maxsegsize */
993d5b3b0aSRuslan Bukin 	    0,				/* flags */
1003d5b3b0aSRuslan Bukin 	    NULL, NULL,			/* lockfunc, lockarg */
1013d5b3b0aSRuslan Bukin 	    &xchan->dma_tag_bufs);
1023d5b3b0aSRuslan Bukin 	if (err != 0) {
1033d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
1043d5b3b0aSRuslan Bukin 		    "%s: Can't create bus_dma tag.\n", __func__);
1053d5b3b0aSRuslan Bukin 		return (-1);
1063d5b3b0aSRuslan Bukin 	}
1073d5b3b0aSRuslan Bukin 
1083d5b3b0aSRuslan Bukin 	for (i = 0; i < xchan->xr_num; i++) {
1093d5b3b0aSRuslan Bukin 		xr = &xchan->xr_mem[i];
1103d5b3b0aSRuslan Bukin 		err = bus_dmamap_create(xchan->dma_tag_bufs, 0,
1113d5b3b0aSRuslan Bukin 		    &xr->buf.map);
1123d5b3b0aSRuslan Bukin 		if (err != 0) {
1133d5b3b0aSRuslan Bukin 			device_printf(xdma->dev,
1143d5b3b0aSRuslan Bukin 			    "%s: Can't create buf DMA map.\n", __func__);
1153d5b3b0aSRuslan Bukin 
1163d5b3b0aSRuslan Bukin 			/* Cleanup. */
1173d5b3b0aSRuslan Bukin 			bus_dma_tag_destroy(xchan->dma_tag_bufs);
1183d5b3b0aSRuslan Bukin 
1193d5b3b0aSRuslan Bukin 			return (-1);
1203d5b3b0aSRuslan Bukin 		}
1213d5b3b0aSRuslan Bukin 	}
1223d5b3b0aSRuslan Bukin 
1233d5b3b0aSRuslan Bukin 	return (0);
1243d5b3b0aSRuslan Bukin }
1253d5b3b0aSRuslan Bukin 
1263d5b3b0aSRuslan Bukin static int
1273d5b3b0aSRuslan Bukin xchan_bufs_alloc(xdma_channel_t *xchan)
1283d5b3b0aSRuslan Bukin {
1293d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
1303d5b3b0aSRuslan Bukin 	int ret;
1313d5b3b0aSRuslan Bukin 
1323d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
1333d5b3b0aSRuslan Bukin 
1343d5b3b0aSRuslan Bukin 	if (xdma == NULL) {
1353d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
1363d5b3b0aSRuslan Bukin 		    "%s: Channel was not allocated properly.\n", __func__);
1373d5b3b0aSRuslan Bukin 		return (-1);
1383d5b3b0aSRuslan Bukin 	}
1393d5b3b0aSRuslan Bukin 
1403d5b3b0aSRuslan Bukin 	if (xchan->caps & XCHAN_CAP_BUSDMA)
1413d5b3b0aSRuslan Bukin 		ret = _xchan_bufs_alloc_busdma(xchan);
1423d5b3b0aSRuslan Bukin 	else
1433d5b3b0aSRuslan Bukin 		ret = _xchan_bufs_alloc(xchan);
1443d5b3b0aSRuslan Bukin 	if (ret != 0) {
1453d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
1463d5b3b0aSRuslan Bukin 		    "%s: Can't allocate bufs.\n", __func__);
1473d5b3b0aSRuslan Bukin 		return (-1);
1483d5b3b0aSRuslan Bukin 	}
1493d5b3b0aSRuslan Bukin 
1503d5b3b0aSRuslan Bukin 	xchan->flags |= XCHAN_BUFS_ALLOCATED;
1513d5b3b0aSRuslan Bukin 
1523d5b3b0aSRuslan Bukin 	return (0);
1533d5b3b0aSRuslan Bukin }
1543d5b3b0aSRuslan Bukin 
1553d5b3b0aSRuslan Bukin static int
1563d5b3b0aSRuslan Bukin xchan_bufs_free(xdma_channel_t *xchan)
1573d5b3b0aSRuslan Bukin {
1583d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
1593d5b3b0aSRuslan Bukin 	struct xchan_buf *b;
1603d5b3b0aSRuslan Bukin 	int i;
1613d5b3b0aSRuslan Bukin 
1623d5b3b0aSRuslan Bukin 	if ((xchan->flags & XCHAN_BUFS_ALLOCATED) == 0)
1633d5b3b0aSRuslan Bukin 		return (-1);
1643d5b3b0aSRuslan Bukin 
1653d5b3b0aSRuslan Bukin 	if (xchan->caps & XCHAN_CAP_BUSDMA) {
1663d5b3b0aSRuslan Bukin 		for (i = 0; i < xchan->xr_num; i++) {
1673d5b3b0aSRuslan Bukin 			xr = &xchan->xr_mem[i];
1683d5b3b0aSRuslan Bukin 			b = &xr->buf;
1693d5b3b0aSRuslan Bukin 			bus_dmamap_destroy(xchan->dma_tag_bufs, b->map);
1703d5b3b0aSRuslan Bukin 		}
1713d5b3b0aSRuslan Bukin 		bus_dma_tag_destroy(xchan->dma_tag_bufs);
1723d5b3b0aSRuslan Bukin 	} else {
1733d5b3b0aSRuslan Bukin 		for (i = 0; i < xchan->xr_num; i++) {
1743d5b3b0aSRuslan Bukin 			xr = &xchan->xr_mem[i];
175*5a51e5e4SRuslan Bukin 			/* TODO: bounce buffer */
1763d5b3b0aSRuslan Bukin 		}
1773d5b3b0aSRuslan Bukin 	}
1783d5b3b0aSRuslan Bukin 
1793d5b3b0aSRuslan Bukin 	xchan->flags &= ~XCHAN_BUFS_ALLOCATED;
1803d5b3b0aSRuslan Bukin 
1813d5b3b0aSRuslan Bukin 	return (0);
1823d5b3b0aSRuslan Bukin }
1833d5b3b0aSRuslan Bukin 
1843d5b3b0aSRuslan Bukin void
1853d5b3b0aSRuslan Bukin xdma_channel_free_sg(xdma_channel_t *xchan)
1863d5b3b0aSRuslan Bukin {
1873d5b3b0aSRuslan Bukin 
1883d5b3b0aSRuslan Bukin 	xchan_bufs_free(xchan);
1893d5b3b0aSRuslan Bukin 	xchan_sglist_free(xchan);
1903d5b3b0aSRuslan Bukin 	xchan_bank_free(xchan);
1913d5b3b0aSRuslan Bukin }
1923d5b3b0aSRuslan Bukin 
1933d5b3b0aSRuslan Bukin /*
1943d5b3b0aSRuslan Bukin  * Prepare xchan for a scatter-gather transfer.
1953d5b3b0aSRuslan Bukin  * xr_num - xdma requests queue size,
1963d5b3b0aSRuslan Bukin  * maxsegsize - maximum allowed scatter-gather list element size in bytes
1973d5b3b0aSRuslan Bukin  */
1983d5b3b0aSRuslan Bukin int
1993d5b3b0aSRuslan Bukin xdma_prep_sg(xdma_channel_t *xchan, uint32_t xr_num,
2003d5b3b0aSRuslan Bukin     bus_size_t maxsegsize, bus_size_t maxnsegs,
2013d5b3b0aSRuslan Bukin     bus_size_t alignment, bus_addr_t boundary,
2023d5b3b0aSRuslan Bukin     bus_addr_t lowaddr, bus_addr_t highaddr)
2033d5b3b0aSRuslan Bukin {
2043d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
2053d5b3b0aSRuslan Bukin 	int ret;
2063d5b3b0aSRuslan Bukin 
2073d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
2083d5b3b0aSRuslan Bukin 
2093d5b3b0aSRuslan Bukin 	KASSERT(xdma != NULL, ("xdma is NULL"));
2103d5b3b0aSRuslan Bukin 
2113d5b3b0aSRuslan Bukin 	if (xchan->flags & XCHAN_CONFIGURED) {
2123d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
2133d5b3b0aSRuslan Bukin 		    "%s: Channel is already configured.\n", __func__);
2143d5b3b0aSRuslan Bukin 		return (-1);
2153d5b3b0aSRuslan Bukin 	}
2163d5b3b0aSRuslan Bukin 
2173d5b3b0aSRuslan Bukin 	xchan->xr_num = xr_num;
2183d5b3b0aSRuslan Bukin 	xchan->maxsegsize = maxsegsize;
2193d5b3b0aSRuslan Bukin 	xchan->maxnsegs = maxnsegs;
2203d5b3b0aSRuslan Bukin 	xchan->alignment = alignment;
2213d5b3b0aSRuslan Bukin 	xchan->boundary = boundary;
2223d5b3b0aSRuslan Bukin 	xchan->lowaddr = lowaddr;
2233d5b3b0aSRuslan Bukin 	xchan->highaddr = highaddr;
2243d5b3b0aSRuslan Bukin 
2253d5b3b0aSRuslan Bukin 	if (xchan->maxnsegs > XDMA_MAX_SEG) {
2263d5b3b0aSRuslan Bukin 		device_printf(xdma->dev, "%s: maxnsegs is too big\n",
2273d5b3b0aSRuslan Bukin 		    __func__);
2283d5b3b0aSRuslan Bukin 		return (-1);
2293d5b3b0aSRuslan Bukin 	}
2303d5b3b0aSRuslan Bukin 
2313d5b3b0aSRuslan Bukin 	xchan_bank_init(xchan);
2323d5b3b0aSRuslan Bukin 
2333d5b3b0aSRuslan Bukin 	/* Allocate sglist. */
2343d5b3b0aSRuslan Bukin 	ret = xchan_sglist_alloc(xchan);
2353d5b3b0aSRuslan Bukin 	if (ret != 0) {
2363d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
2373d5b3b0aSRuslan Bukin 		    "%s: Can't allocate sglist.\n", __func__);
2383d5b3b0aSRuslan Bukin 		return (-1);
2393d5b3b0aSRuslan Bukin 	}
2403d5b3b0aSRuslan Bukin 
241*5a51e5e4SRuslan Bukin 	/* Allocate buffers if required. */
242*5a51e5e4SRuslan Bukin 	if ((xchan->caps & XCHAN_CAP_NOBUFS) == 0) {
2433d5b3b0aSRuslan Bukin 		ret = xchan_bufs_alloc(xchan);
2443d5b3b0aSRuslan Bukin 		if (ret != 0) {
2453d5b3b0aSRuslan Bukin 			device_printf(xdma->dev,
2463d5b3b0aSRuslan Bukin 			    "%s: Can't allocate bufs.\n", __func__);
2473d5b3b0aSRuslan Bukin 
2483d5b3b0aSRuslan Bukin 			/* Cleanup */
2493d5b3b0aSRuslan Bukin 			xchan_sglist_free(xchan);
2503d5b3b0aSRuslan Bukin 			xchan_bank_free(xchan);
2513d5b3b0aSRuslan Bukin 
2523d5b3b0aSRuslan Bukin 			return (-1);
2533d5b3b0aSRuslan Bukin 		}
254*5a51e5e4SRuslan Bukin 	}
2553d5b3b0aSRuslan Bukin 
2563d5b3b0aSRuslan Bukin 	xchan->flags |= (XCHAN_CONFIGURED | XCHAN_TYPE_SG);
2573d5b3b0aSRuslan Bukin 
2583d5b3b0aSRuslan Bukin 	XCHAN_LOCK(xchan);
2593d5b3b0aSRuslan Bukin 	ret = XDMA_CHANNEL_PREP_SG(xdma->dma_dev, xchan);
2603d5b3b0aSRuslan Bukin 	if (ret != 0) {
2613d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
2623d5b3b0aSRuslan Bukin 		    "%s: Can't prepare SG transfer.\n", __func__);
2633d5b3b0aSRuslan Bukin 		XCHAN_UNLOCK(xchan);
2643d5b3b0aSRuslan Bukin 
2653d5b3b0aSRuslan Bukin 		return (-1);
2663d5b3b0aSRuslan Bukin 	}
2673d5b3b0aSRuslan Bukin 	XCHAN_UNLOCK(xchan);
2683d5b3b0aSRuslan Bukin 
2693d5b3b0aSRuslan Bukin 	return (0);
2703d5b3b0aSRuslan Bukin }
2713d5b3b0aSRuslan Bukin 
2723d5b3b0aSRuslan Bukin void
2733d5b3b0aSRuslan Bukin xchan_seg_done(xdma_channel_t *xchan,
2743d5b3b0aSRuslan Bukin     struct xdma_transfer_status *st)
2753d5b3b0aSRuslan Bukin {
2763d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
2773d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
2783d5b3b0aSRuslan Bukin 	struct xchan_buf *b;
2793d5b3b0aSRuslan Bukin 
2803d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
2813d5b3b0aSRuslan Bukin 
2823d5b3b0aSRuslan Bukin 	xr = TAILQ_FIRST(&xchan->processing);
2833d5b3b0aSRuslan Bukin 	if (xr == NULL)
2843d5b3b0aSRuslan Bukin 		panic("request not found\n");
2853d5b3b0aSRuslan Bukin 
2863d5b3b0aSRuslan Bukin 	b = &xr->buf;
2873d5b3b0aSRuslan Bukin 
2883d5b3b0aSRuslan Bukin 	atomic_subtract_int(&b->nsegs_left, 1);
2893d5b3b0aSRuslan Bukin 
2903d5b3b0aSRuslan Bukin 	if (b->nsegs_left == 0) {
2913d5b3b0aSRuslan Bukin 		if (xchan->caps & XCHAN_CAP_BUSDMA) {
2923d5b3b0aSRuslan Bukin 			if (xr->direction == XDMA_MEM_TO_DEV)
2933d5b3b0aSRuslan Bukin 				bus_dmamap_sync(xchan->dma_tag_bufs, b->map,
2943d5b3b0aSRuslan Bukin 				    BUS_DMASYNC_POSTWRITE);
2953d5b3b0aSRuslan Bukin 			else
2963d5b3b0aSRuslan Bukin 				bus_dmamap_sync(xchan->dma_tag_bufs, b->map,
2973d5b3b0aSRuslan Bukin 				    BUS_DMASYNC_POSTREAD);
2983d5b3b0aSRuslan Bukin 			bus_dmamap_unload(xchan->dma_tag_bufs, b->map);
2993d5b3b0aSRuslan Bukin 		}
3003d5b3b0aSRuslan Bukin 		xr->status.error = st->error;
3013d5b3b0aSRuslan Bukin 		xr->status.transferred = st->transferred;
3023d5b3b0aSRuslan Bukin 
3033d5b3b0aSRuslan Bukin 		QUEUE_PROC_LOCK(xchan);
3043d5b3b0aSRuslan Bukin 		TAILQ_REMOVE(&xchan->processing, xr, xr_next);
3053d5b3b0aSRuslan Bukin 		QUEUE_PROC_UNLOCK(xchan);
3063d5b3b0aSRuslan Bukin 
3073d5b3b0aSRuslan Bukin 		QUEUE_OUT_LOCK(xchan);
3083d5b3b0aSRuslan Bukin 		TAILQ_INSERT_TAIL(&xchan->queue_out, xr, xr_next);
3093d5b3b0aSRuslan Bukin 		QUEUE_OUT_UNLOCK(xchan);
3103d5b3b0aSRuslan Bukin 	}
3113d5b3b0aSRuslan Bukin }
3123d5b3b0aSRuslan Bukin 
3133d5b3b0aSRuslan Bukin static void
3143d5b3b0aSRuslan Bukin xdma_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
3153d5b3b0aSRuslan Bukin {
3163d5b3b0aSRuslan Bukin 	struct seg_load_request *slr;
3173d5b3b0aSRuslan Bukin 	struct bus_dma_segment *seg;
3183d5b3b0aSRuslan Bukin 	int i;
3193d5b3b0aSRuslan Bukin 
3203d5b3b0aSRuslan Bukin 	slr = arg;
3213d5b3b0aSRuslan Bukin 	seg = slr->seg;
3223d5b3b0aSRuslan Bukin 
3233d5b3b0aSRuslan Bukin 	if (error != 0) {
3243d5b3b0aSRuslan Bukin 		slr->error = error;
3253d5b3b0aSRuslan Bukin 		return;
3263d5b3b0aSRuslan Bukin 	}
3273d5b3b0aSRuslan Bukin 
3283d5b3b0aSRuslan Bukin 	slr->nsegs = nsegs;
3293d5b3b0aSRuslan Bukin 
3303d5b3b0aSRuslan Bukin 	for (i = 0; i < nsegs; i++) {
3313d5b3b0aSRuslan Bukin 		seg[i].ds_addr = segs[i].ds_addr;
3323d5b3b0aSRuslan Bukin 		seg[i].ds_len = segs[i].ds_len;
3333d5b3b0aSRuslan Bukin 	}
3343d5b3b0aSRuslan Bukin }
3353d5b3b0aSRuslan Bukin 
3363d5b3b0aSRuslan Bukin static int
3373d5b3b0aSRuslan Bukin _xdma_load_data_busdma(xdma_channel_t *xchan, struct xdma_request *xr,
3383d5b3b0aSRuslan Bukin     struct bus_dma_segment *seg)
3393d5b3b0aSRuslan Bukin {
3403d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
3413d5b3b0aSRuslan Bukin 	struct seg_load_request slr;
3423d5b3b0aSRuslan Bukin 	uint32_t nsegs;
3433d5b3b0aSRuslan Bukin 	void *addr;
3443d5b3b0aSRuslan Bukin 	int error;
3453d5b3b0aSRuslan Bukin 
3463d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
3473d5b3b0aSRuslan Bukin 
3483d5b3b0aSRuslan Bukin 	error = 0;
3493d5b3b0aSRuslan Bukin 	nsegs = 0;
3503d5b3b0aSRuslan Bukin 
3513d5b3b0aSRuslan Bukin 	switch (xr->req_type) {
3523d5b3b0aSRuslan Bukin 	case XR_TYPE_MBUF:
3533d5b3b0aSRuslan Bukin 		error = bus_dmamap_load_mbuf_sg(xchan->dma_tag_bufs,
3543d5b3b0aSRuslan Bukin 		    xr->buf.map, xr->m, seg, &nsegs, BUS_DMA_NOWAIT);
3553d5b3b0aSRuslan Bukin 		break;
3563d5b3b0aSRuslan Bukin 	case XR_TYPE_BIO:
3573d5b3b0aSRuslan Bukin 		slr.nsegs = 0;
3583d5b3b0aSRuslan Bukin 		slr.error = 0;
3593d5b3b0aSRuslan Bukin 		slr.seg = seg;
3603d5b3b0aSRuslan Bukin 		error = bus_dmamap_load_bio(xchan->dma_tag_bufs,
3613d5b3b0aSRuslan Bukin 		    xr->buf.map, xr->bp, xdma_dmamap_cb, &slr, BUS_DMA_NOWAIT);
3623d5b3b0aSRuslan Bukin 		if (slr.error != 0) {
3633d5b3b0aSRuslan Bukin 			device_printf(xdma->dma_dev,
3643d5b3b0aSRuslan Bukin 			    "%s: bus_dmamap_load failed, err %d\n",
3653d5b3b0aSRuslan Bukin 			    __func__, slr.error);
3663d5b3b0aSRuslan Bukin 			return (0);
3673d5b3b0aSRuslan Bukin 		}
3683d5b3b0aSRuslan Bukin 		nsegs = slr.nsegs;
3693d5b3b0aSRuslan Bukin 		break;
3703d5b3b0aSRuslan Bukin 	case XR_TYPE_VIRT:
3713d5b3b0aSRuslan Bukin 		switch (xr->direction) {
3723d5b3b0aSRuslan Bukin 		case XDMA_MEM_TO_DEV:
3733d5b3b0aSRuslan Bukin 			addr = (void *)xr->src_addr;
3743d5b3b0aSRuslan Bukin 			break;
3753d5b3b0aSRuslan Bukin 		case XDMA_DEV_TO_MEM:
3763d5b3b0aSRuslan Bukin 			addr = (void *)xr->dst_addr;
3773d5b3b0aSRuslan Bukin 			break;
3783d5b3b0aSRuslan Bukin 		default:
3793d5b3b0aSRuslan Bukin 			device_printf(xdma->dma_dev,
3803d5b3b0aSRuslan Bukin 			    "%s: Direction is not supported\n", __func__);
3813d5b3b0aSRuslan Bukin 			return (0);
3823d5b3b0aSRuslan Bukin 		}
3833d5b3b0aSRuslan Bukin 		slr.nsegs = 0;
3843d5b3b0aSRuslan Bukin 		slr.error = 0;
3853d5b3b0aSRuslan Bukin 		slr.seg = seg;
3863d5b3b0aSRuslan Bukin 		error = bus_dmamap_load(xchan->dma_tag_bufs, xr->buf.map,
3873d5b3b0aSRuslan Bukin 		    addr, (xr->block_len * xr->block_num),
3883d5b3b0aSRuslan Bukin 		    xdma_dmamap_cb, &slr, BUS_DMA_NOWAIT);
3893d5b3b0aSRuslan Bukin 		if (slr.error != 0) {
3903d5b3b0aSRuslan Bukin 			device_printf(xdma->dma_dev,
3913d5b3b0aSRuslan Bukin 			    "%s: bus_dmamap_load failed, err %d\n",
3923d5b3b0aSRuslan Bukin 			    __func__, slr.error);
3933d5b3b0aSRuslan Bukin 			return (0);
3943d5b3b0aSRuslan Bukin 		}
3953d5b3b0aSRuslan Bukin 		nsegs = slr.nsegs;
3963d5b3b0aSRuslan Bukin 		break;
3973d5b3b0aSRuslan Bukin 	default:
3983d5b3b0aSRuslan Bukin 		break;
3993d5b3b0aSRuslan Bukin 	}
4003d5b3b0aSRuslan Bukin 
4013d5b3b0aSRuslan Bukin 	if (error != 0) {
4023d5b3b0aSRuslan Bukin 		if (error == ENOMEM) {
4033d5b3b0aSRuslan Bukin 			/*
4043d5b3b0aSRuslan Bukin 			 * Out of memory. Try again later.
4053d5b3b0aSRuslan Bukin 			 * TODO: count errors.
4063d5b3b0aSRuslan Bukin 			 */
4073d5b3b0aSRuslan Bukin 		} else
4083d5b3b0aSRuslan Bukin 			device_printf(xdma->dma_dev,
4093d5b3b0aSRuslan Bukin 			    "%s: bus_dmamap_load failed with err %d\n",
4103d5b3b0aSRuslan Bukin 			    __func__, error);
4113d5b3b0aSRuslan Bukin 		return (0);
4123d5b3b0aSRuslan Bukin 	}
4133d5b3b0aSRuslan Bukin 
4143d5b3b0aSRuslan Bukin 	if (xr->direction == XDMA_MEM_TO_DEV)
4153d5b3b0aSRuslan Bukin 		bus_dmamap_sync(xchan->dma_tag_bufs, xr->buf.map,
4163d5b3b0aSRuslan Bukin 		    BUS_DMASYNC_PREWRITE);
4173d5b3b0aSRuslan Bukin 	else
4183d5b3b0aSRuslan Bukin 		bus_dmamap_sync(xchan->dma_tag_bufs, xr->buf.map,
4193d5b3b0aSRuslan Bukin 		    BUS_DMASYNC_PREREAD);
4203d5b3b0aSRuslan Bukin 
4213d5b3b0aSRuslan Bukin 	return (nsegs);
4223d5b3b0aSRuslan Bukin }
4233d5b3b0aSRuslan Bukin 
4243d5b3b0aSRuslan Bukin static int
4253d5b3b0aSRuslan Bukin _xdma_load_data(xdma_channel_t *xchan, struct xdma_request *xr,
4263d5b3b0aSRuslan Bukin     struct bus_dma_segment *seg)
4273d5b3b0aSRuslan Bukin {
4283d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
4293d5b3b0aSRuslan Bukin 	struct mbuf *m;
4303d5b3b0aSRuslan Bukin 	uint32_t nsegs;
4313d5b3b0aSRuslan Bukin 
4323d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
4333d5b3b0aSRuslan Bukin 
4343d5b3b0aSRuslan Bukin 	m = xr->m;
4353d5b3b0aSRuslan Bukin 
4363d5b3b0aSRuslan Bukin 	nsegs = 1;
4373d5b3b0aSRuslan Bukin 
4383d5b3b0aSRuslan Bukin 	switch (xr->req_type) {
4393d5b3b0aSRuslan Bukin 	case XR_TYPE_MBUF:
4403d5b3b0aSRuslan Bukin 		seg[0].ds_addr = mtod(m, bus_addr_t);
4413d5b3b0aSRuslan Bukin 		seg[0].ds_len = m->m_pkthdr.len;
4423d5b3b0aSRuslan Bukin 		break;
4433d5b3b0aSRuslan Bukin 	case XR_TYPE_BIO:
4443d5b3b0aSRuslan Bukin 	case XR_TYPE_VIRT:
4453d5b3b0aSRuslan Bukin 	default:
4463d5b3b0aSRuslan Bukin 		panic("implement me\n");
4473d5b3b0aSRuslan Bukin 	}
4483d5b3b0aSRuslan Bukin 
4493d5b3b0aSRuslan Bukin 	return (nsegs);
4503d5b3b0aSRuslan Bukin }
4513d5b3b0aSRuslan Bukin 
4523d5b3b0aSRuslan Bukin static int
4533d5b3b0aSRuslan Bukin xdma_load_data(xdma_channel_t *xchan,
4543d5b3b0aSRuslan Bukin     struct xdma_request *xr, struct bus_dma_segment *seg)
4553d5b3b0aSRuslan Bukin {
4563d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
4573d5b3b0aSRuslan Bukin 	int error;
4583d5b3b0aSRuslan Bukin 	int nsegs;
4593d5b3b0aSRuslan Bukin 
4603d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
4613d5b3b0aSRuslan Bukin 
4623d5b3b0aSRuslan Bukin 	error = 0;
4633d5b3b0aSRuslan Bukin 	nsegs = 0;
4643d5b3b0aSRuslan Bukin 
4653d5b3b0aSRuslan Bukin 	if (xchan->caps & XCHAN_CAP_BUSDMA)
4663d5b3b0aSRuslan Bukin 		nsegs = _xdma_load_data_busdma(xchan, xr, seg);
4673d5b3b0aSRuslan Bukin 	else
4683d5b3b0aSRuslan Bukin 		nsegs = _xdma_load_data(xchan, xr, seg);
4693d5b3b0aSRuslan Bukin 	if (nsegs == 0)
4703d5b3b0aSRuslan Bukin 		return (0); /* Try again later. */
4713d5b3b0aSRuslan Bukin 
4723d5b3b0aSRuslan Bukin 	xr->buf.nsegs = nsegs;
4733d5b3b0aSRuslan Bukin 	xr->buf.nsegs_left = nsegs;
4743d5b3b0aSRuslan Bukin 
4753d5b3b0aSRuslan Bukin 	return (nsegs);
4763d5b3b0aSRuslan Bukin }
4773d5b3b0aSRuslan Bukin 
4783d5b3b0aSRuslan Bukin static int
4793d5b3b0aSRuslan Bukin xdma_process(xdma_channel_t *xchan,
4803d5b3b0aSRuslan Bukin     struct xdma_sglist *sg)
4813d5b3b0aSRuslan Bukin {
4823d5b3b0aSRuslan Bukin 	struct bus_dma_segment seg[XDMA_MAX_SEG];
4833d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
4843d5b3b0aSRuslan Bukin 	struct xdma_request *xr_tmp;
4853d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
4863d5b3b0aSRuslan Bukin 	uint32_t capacity;
4873d5b3b0aSRuslan Bukin 	uint32_t n;
4883d5b3b0aSRuslan Bukin 	uint32_t c;
4893d5b3b0aSRuslan Bukin 	int nsegs;
4903d5b3b0aSRuslan Bukin 	int ret;
4913d5b3b0aSRuslan Bukin 
4923d5b3b0aSRuslan Bukin 	XCHAN_ASSERT_LOCKED(xchan);
4933d5b3b0aSRuslan Bukin 
4943d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
4953d5b3b0aSRuslan Bukin 
4963d5b3b0aSRuslan Bukin 	n = 0;
4973d5b3b0aSRuslan Bukin 
4983d5b3b0aSRuslan Bukin 	ret = XDMA_CHANNEL_CAPACITY(xdma->dma_dev, xchan, &capacity);
4993d5b3b0aSRuslan Bukin 	if (ret != 0) {
5003d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
5013d5b3b0aSRuslan Bukin 		    "%s: Can't get DMA controller capacity.\n", __func__);
5023d5b3b0aSRuslan Bukin 		return (-1);
5033d5b3b0aSRuslan Bukin 	}
5043d5b3b0aSRuslan Bukin 
5053d5b3b0aSRuslan Bukin 	TAILQ_FOREACH_SAFE(xr, &xchan->queue_in, xr_next, xr_tmp) {
5063d5b3b0aSRuslan Bukin 		switch (xr->req_type) {
5073d5b3b0aSRuslan Bukin 		case XR_TYPE_MBUF:
508*5a51e5e4SRuslan Bukin 			if ((xchan->caps & XCHAN_CAP_NOSEG) ||
509*5a51e5e4SRuslan Bukin 			    (c > xchan->maxnsegs))
5103d5b3b0aSRuslan Bukin 				c = xdma_mbuf_defrag(xchan, xr);
5113d5b3b0aSRuslan Bukin 			break;
5123d5b3b0aSRuslan Bukin 		case XR_TYPE_BIO:
5133d5b3b0aSRuslan Bukin 		case XR_TYPE_VIRT:
5143d5b3b0aSRuslan Bukin 		default:
5153d5b3b0aSRuslan Bukin 			c = 1;
5163d5b3b0aSRuslan Bukin 		}
5173d5b3b0aSRuslan Bukin 
5183d5b3b0aSRuslan Bukin 		if (capacity <= (c + n)) {
5193d5b3b0aSRuslan Bukin 			/*
5203d5b3b0aSRuslan Bukin 			 * No space yet available for the entire
5213d5b3b0aSRuslan Bukin 			 * request in the DMA engine.
5223d5b3b0aSRuslan Bukin 			 */
5233d5b3b0aSRuslan Bukin 			break;
5243d5b3b0aSRuslan Bukin 		}
5253d5b3b0aSRuslan Bukin 
5263d5b3b0aSRuslan Bukin 		if ((c + n + xchan->maxnsegs) >= XDMA_SGLIST_MAXLEN) {
5273d5b3b0aSRuslan Bukin 			/* Sglist is full. */
5283d5b3b0aSRuslan Bukin 			break;
5293d5b3b0aSRuslan Bukin 		}
5303d5b3b0aSRuslan Bukin 
5313d5b3b0aSRuslan Bukin 		nsegs = xdma_load_data(xchan, xr, seg);
5323d5b3b0aSRuslan Bukin 		if (nsegs == 0)
5333d5b3b0aSRuslan Bukin 			break;
5343d5b3b0aSRuslan Bukin 
5353d5b3b0aSRuslan Bukin 		xdma_sglist_add(&sg[n], seg, nsegs, xr);
5363d5b3b0aSRuslan Bukin 		n += nsegs;
5373d5b3b0aSRuslan Bukin 
5383d5b3b0aSRuslan Bukin 		QUEUE_IN_LOCK(xchan);
5393d5b3b0aSRuslan Bukin 		TAILQ_REMOVE(&xchan->queue_in, xr, xr_next);
5403d5b3b0aSRuslan Bukin 		QUEUE_IN_UNLOCK(xchan);
5413d5b3b0aSRuslan Bukin 
5423d5b3b0aSRuslan Bukin 		QUEUE_PROC_LOCK(xchan);
5433d5b3b0aSRuslan Bukin 		TAILQ_INSERT_TAIL(&xchan->processing, xr, xr_next);
5443d5b3b0aSRuslan Bukin 		QUEUE_PROC_UNLOCK(xchan);
5453d5b3b0aSRuslan Bukin 	}
5463d5b3b0aSRuslan Bukin 
5473d5b3b0aSRuslan Bukin 	return (n);
5483d5b3b0aSRuslan Bukin }
5493d5b3b0aSRuslan Bukin 
5503d5b3b0aSRuslan Bukin int
5513d5b3b0aSRuslan Bukin xdma_queue_submit_sg(xdma_channel_t *xchan)
5523d5b3b0aSRuslan Bukin {
5533d5b3b0aSRuslan Bukin 	struct xdma_sglist *sg;
5543d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
5553d5b3b0aSRuslan Bukin 	uint32_t sg_n;
5563d5b3b0aSRuslan Bukin 	int ret;
5573d5b3b0aSRuslan Bukin 
5583d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
5593d5b3b0aSRuslan Bukin 	KASSERT(xdma != NULL, ("xdma is NULL"));
5603d5b3b0aSRuslan Bukin 
5613d5b3b0aSRuslan Bukin 	XCHAN_ASSERT_LOCKED(xchan);
5623d5b3b0aSRuslan Bukin 
5633d5b3b0aSRuslan Bukin 	sg = xchan->sg;
5643d5b3b0aSRuslan Bukin 
565*5a51e5e4SRuslan Bukin 	if ((xchan->caps & XCHAN_CAP_NOBUFS) == 0 &&
566*5a51e5e4SRuslan Bukin 	   (xchan->flags & XCHAN_BUFS_ALLOCATED) == 0) {
5673d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
5683d5b3b0aSRuslan Bukin 		    "%s: Can't submit a transfer: no bufs\n",
5693d5b3b0aSRuslan Bukin 		    __func__);
5703d5b3b0aSRuslan Bukin 		return (-1);
5713d5b3b0aSRuslan Bukin 	}
5723d5b3b0aSRuslan Bukin 
5733d5b3b0aSRuslan Bukin 	sg_n = xdma_process(xchan, sg);
5743d5b3b0aSRuslan Bukin 	if (sg_n == 0)
5753d5b3b0aSRuslan Bukin 		return (0); /* Nothing to submit */
5763d5b3b0aSRuslan Bukin 
5773d5b3b0aSRuslan Bukin 	/* Now submit sglist to DMA engine driver. */
5783d5b3b0aSRuslan Bukin 	ret = XDMA_CHANNEL_SUBMIT_SG(xdma->dma_dev, xchan, sg, sg_n);
5793d5b3b0aSRuslan Bukin 	if (ret != 0) {
5803d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
5813d5b3b0aSRuslan Bukin 		    "%s: Can't submit an sglist.\n", __func__);
5823d5b3b0aSRuslan Bukin 		return (-1);
5833d5b3b0aSRuslan Bukin 	}
5843d5b3b0aSRuslan Bukin 
5853d5b3b0aSRuslan Bukin 	return (0);
5863d5b3b0aSRuslan Bukin }
587