xref: /freebsd/sys/dev/xdma/xdma_sg.c (revision 685dc743dc3b5645e34836464128e1c0558b404b)
13d5b3b0aSRuslan Bukin /*-
2101869a8SRuslan Bukin  * SPDX-License-Identifier: BSD-2-Clause
3101869a8SRuslan Bukin  *
4101869a8SRuslan Bukin  * Copyright (c) 2018-2019 Ruslan Bukin <br@bsdpad.com>
53d5b3b0aSRuslan Bukin  *
63d5b3b0aSRuslan Bukin  * This software was developed by SRI International and the University of
73d5b3b0aSRuslan Bukin  * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
83d5b3b0aSRuslan Bukin  * ("CTSRD"), as part of the DARPA CRASH research programme.
93d5b3b0aSRuslan Bukin  *
103d5b3b0aSRuslan Bukin  * Redistribution and use in source and binary forms, with or without
113d5b3b0aSRuslan Bukin  * modification, are permitted provided that the following conditions
123d5b3b0aSRuslan Bukin  * are met:
133d5b3b0aSRuslan Bukin  * 1. Redistributions of source code must retain the above copyright
143d5b3b0aSRuslan Bukin  *    notice, this list of conditions and the following disclaimer.
153d5b3b0aSRuslan Bukin  * 2. Redistributions in binary form must reproduce the above copyright
163d5b3b0aSRuslan Bukin  *    notice, this list of conditions and the following disclaimer in the
173d5b3b0aSRuslan Bukin  *    documentation and/or other materials provided with the distribution.
183d5b3b0aSRuslan Bukin  *
193d5b3b0aSRuslan Bukin  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
203d5b3b0aSRuslan Bukin  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
213d5b3b0aSRuslan Bukin  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
223d5b3b0aSRuslan Bukin  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
233d5b3b0aSRuslan Bukin  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
243d5b3b0aSRuslan Bukin  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
253d5b3b0aSRuslan Bukin  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
263d5b3b0aSRuslan Bukin  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
273d5b3b0aSRuslan Bukin  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
283d5b3b0aSRuslan Bukin  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
293d5b3b0aSRuslan Bukin  * SUCH DAMAGE.
303d5b3b0aSRuslan Bukin  */
313d5b3b0aSRuslan Bukin 
323d5b3b0aSRuslan Bukin #include <sys/cdefs.h>
333d5b3b0aSRuslan Bukin #include "opt_platform.h"
343d5b3b0aSRuslan Bukin #include <sys/param.h>
353d5b3b0aSRuslan Bukin #include <sys/conf.h>
363d5b3b0aSRuslan Bukin #include <sys/bus.h>
373d5b3b0aSRuslan Bukin #include <sys/kernel.h>
38e2e050c8SConrad Meyer #include <sys/lock.h>
393d5b3b0aSRuslan Bukin #include <sys/malloc.h>
403d5b3b0aSRuslan Bukin #include <sys/mbuf.h>
41e2e050c8SConrad Meyer #include <sys/mutex.h>
42101869a8SRuslan Bukin #include <sys/rwlock.h>
433d5b3b0aSRuslan Bukin 
443d5b3b0aSRuslan Bukin #include <machine/bus.h>
453d5b3b0aSRuslan Bukin 
46101869a8SRuslan Bukin #include <vm/vm.h>
47e2e050c8SConrad Meyer #include <vm/pmap.h>
48101869a8SRuslan Bukin #include <vm/vm_extern.h>
49101869a8SRuslan Bukin #include <vm/vm_page.h>
50101869a8SRuslan Bukin 
513d5b3b0aSRuslan Bukin #ifdef FDT
523d5b3b0aSRuslan Bukin #include <dev/fdt/fdt_common.h>
533d5b3b0aSRuslan Bukin #include <dev/ofw/ofw_bus.h>
543d5b3b0aSRuslan Bukin #include <dev/ofw/ofw_bus_subr.h>
553d5b3b0aSRuslan Bukin #endif
563d5b3b0aSRuslan Bukin 
573d5b3b0aSRuslan Bukin #include <dev/xdma/xdma.h>
583d5b3b0aSRuslan Bukin 
593d5b3b0aSRuslan Bukin #include <xdma_if.h>
603d5b3b0aSRuslan Bukin 
613d5b3b0aSRuslan Bukin struct seg_load_request {
623d5b3b0aSRuslan Bukin 	struct bus_dma_segment *seg;
633d5b3b0aSRuslan Bukin 	uint32_t nsegs;
643d5b3b0aSRuslan Bukin 	uint32_t error;
653d5b3b0aSRuslan Bukin };
663d5b3b0aSRuslan Bukin 
67101869a8SRuslan Bukin static void
xchan_bufs_free_reserved(xdma_channel_t * xchan)68101869a8SRuslan Bukin xchan_bufs_free_reserved(xdma_channel_t *xchan)
69101869a8SRuslan Bukin {
70101869a8SRuslan Bukin 	struct xdma_request *xr;
71101869a8SRuslan Bukin 	vm_size_t size;
72101869a8SRuslan Bukin 	int i;
73101869a8SRuslan Bukin 
74101869a8SRuslan Bukin 	for (i = 0; i < xchan->xr_num; i++) {
75101869a8SRuslan Bukin 		xr = &xchan->xr_mem[i];
76101869a8SRuslan Bukin 		size = xr->buf.size;
77101869a8SRuslan Bukin 		if (xr->buf.vaddr) {
78101869a8SRuslan Bukin 			pmap_kremove_device(xr->buf.vaddr, size);
79101869a8SRuslan Bukin 			kva_free(xr->buf.vaddr, size);
80101869a8SRuslan Bukin 			xr->buf.vaddr = 0;
81101869a8SRuslan Bukin 		}
82101869a8SRuslan Bukin 		if (xr->buf.paddr) {
83101869a8SRuslan Bukin 			vmem_free(xchan->vmem, xr->buf.paddr, size);
84101869a8SRuslan Bukin 			xr->buf.paddr = 0;
85101869a8SRuslan Bukin 		}
86101869a8SRuslan Bukin 		xr->buf.size = 0;
87101869a8SRuslan Bukin 	}
88101869a8SRuslan Bukin }
89101869a8SRuslan Bukin 
903d5b3b0aSRuslan Bukin static int
xchan_bufs_alloc_reserved(xdma_channel_t * xchan)91101869a8SRuslan Bukin xchan_bufs_alloc_reserved(xdma_channel_t *xchan)
923d5b3b0aSRuslan Bukin {
933d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
943d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
95101869a8SRuslan Bukin 	vmem_addr_t addr;
96101869a8SRuslan Bukin 	vm_size_t size;
973d5b3b0aSRuslan Bukin 	int i;
983d5b3b0aSRuslan Bukin 
993d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
1003d5b3b0aSRuslan Bukin 
101101869a8SRuslan Bukin 	if (xchan->vmem == NULL)
102101869a8SRuslan Bukin 		return (ENOBUFS);
103101869a8SRuslan Bukin 
1043d5b3b0aSRuslan Bukin 	for (i = 0; i < xchan->xr_num; i++) {
1053d5b3b0aSRuslan Bukin 		xr = &xchan->xr_mem[i];
106101869a8SRuslan Bukin 		size = round_page(xchan->maxsegsize);
107101869a8SRuslan Bukin 		if (vmem_alloc(xchan->vmem, size,
108101869a8SRuslan Bukin 		    M_BESTFIT | M_NOWAIT, &addr)) {
109101869a8SRuslan Bukin 			device_printf(xdma->dev,
110101869a8SRuslan Bukin 			    "%s: Can't allocate memory\n", __func__);
111101869a8SRuslan Bukin 			xchan_bufs_free_reserved(xchan);
112101869a8SRuslan Bukin 			return (ENOMEM);
113101869a8SRuslan Bukin 		}
114101869a8SRuslan Bukin 
115101869a8SRuslan Bukin 		xr->buf.size = size;
116101869a8SRuslan Bukin 		xr->buf.paddr = addr;
117101869a8SRuslan Bukin 		xr->buf.vaddr = kva_alloc(size);
118101869a8SRuslan Bukin 		if (xr->buf.vaddr == 0) {
119101869a8SRuslan Bukin 			device_printf(xdma->dev,
120101869a8SRuslan Bukin 			    "%s: Can't allocate KVA\n", __func__);
121101869a8SRuslan Bukin 			xchan_bufs_free_reserved(xchan);
122101869a8SRuslan Bukin 			return (ENOMEM);
123101869a8SRuslan Bukin 		}
124101869a8SRuslan Bukin 		pmap_kenter_device(xr->buf.vaddr, size, addr);
1253d5b3b0aSRuslan Bukin 	}
1263d5b3b0aSRuslan Bukin 
1273d5b3b0aSRuslan Bukin 	return (0);
1283d5b3b0aSRuslan Bukin }
1293d5b3b0aSRuslan Bukin 
1303d5b3b0aSRuslan Bukin static int
xchan_bufs_alloc_busdma(xdma_channel_t * xchan)131101869a8SRuslan Bukin xchan_bufs_alloc_busdma(xdma_channel_t *xchan)
1323d5b3b0aSRuslan Bukin {
1333d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
1343d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
1353d5b3b0aSRuslan Bukin 	int err;
1363d5b3b0aSRuslan Bukin 	int i;
1373d5b3b0aSRuslan Bukin 
1383d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
1393d5b3b0aSRuslan Bukin 
1403d5b3b0aSRuslan Bukin 	/* Create bus_dma tag */
1413d5b3b0aSRuslan Bukin 	err = bus_dma_tag_create(
1423d5b3b0aSRuslan Bukin 	    bus_get_dma_tag(xdma->dev),	/* Parent tag. */
1433d5b3b0aSRuslan Bukin 	    xchan->alignment,		/* alignment */
1443d5b3b0aSRuslan Bukin 	    xchan->boundary,		/* boundary */
1453d5b3b0aSRuslan Bukin 	    xchan->lowaddr,		/* lowaddr */
1463d5b3b0aSRuslan Bukin 	    xchan->highaddr,		/* highaddr */
1473d5b3b0aSRuslan Bukin 	    NULL, NULL,			/* filter, filterarg */
1483d5b3b0aSRuslan Bukin 	    xchan->maxsegsize * xchan->maxnsegs, /* maxsize */
1493d5b3b0aSRuslan Bukin 	    xchan->maxnsegs,		/* nsegments */
1503d5b3b0aSRuslan Bukin 	    xchan->maxsegsize,		/* maxsegsize */
1513d5b3b0aSRuslan Bukin 	    0,				/* flags */
1523d5b3b0aSRuslan Bukin 	    NULL, NULL,			/* lockfunc, lockarg */
1533d5b3b0aSRuslan Bukin 	    &xchan->dma_tag_bufs);
1543d5b3b0aSRuslan Bukin 	if (err != 0) {
1553d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
1563d5b3b0aSRuslan Bukin 		    "%s: Can't create bus_dma tag.\n", __func__);
1573d5b3b0aSRuslan Bukin 		return (-1);
1583d5b3b0aSRuslan Bukin 	}
1593d5b3b0aSRuslan Bukin 
1603d5b3b0aSRuslan Bukin 	for (i = 0; i < xchan->xr_num; i++) {
1613d5b3b0aSRuslan Bukin 		xr = &xchan->xr_mem[i];
1623d5b3b0aSRuslan Bukin 		err = bus_dmamap_create(xchan->dma_tag_bufs, 0,
1633d5b3b0aSRuslan Bukin 		    &xr->buf.map);
1643d5b3b0aSRuslan Bukin 		if (err != 0) {
1653d5b3b0aSRuslan Bukin 			device_printf(xdma->dev,
1663d5b3b0aSRuslan Bukin 			    "%s: Can't create buf DMA map.\n", __func__);
1673d5b3b0aSRuslan Bukin 
1683d5b3b0aSRuslan Bukin 			/* Cleanup. */
1693d5b3b0aSRuslan Bukin 			bus_dma_tag_destroy(xchan->dma_tag_bufs);
1703d5b3b0aSRuslan Bukin 
1713d5b3b0aSRuslan Bukin 			return (-1);
1723d5b3b0aSRuslan Bukin 		}
1733d5b3b0aSRuslan Bukin 	}
1743d5b3b0aSRuslan Bukin 
1753d5b3b0aSRuslan Bukin 	return (0);
1763d5b3b0aSRuslan Bukin }
1773d5b3b0aSRuslan Bukin 
1783d5b3b0aSRuslan Bukin static int
xchan_bufs_alloc(xdma_channel_t * xchan)1793d5b3b0aSRuslan Bukin xchan_bufs_alloc(xdma_channel_t *xchan)
1803d5b3b0aSRuslan Bukin {
1813d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
1823d5b3b0aSRuslan Bukin 	int ret;
1833d5b3b0aSRuslan Bukin 
1843d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
1853d5b3b0aSRuslan Bukin 
1863d5b3b0aSRuslan Bukin 	if (xdma == NULL) {
187d676fedfSEd Maste 		printf("%s: Channel was not allocated properly.\n", __func__);
1883d5b3b0aSRuslan Bukin 		return (-1);
1893d5b3b0aSRuslan Bukin 	}
1903d5b3b0aSRuslan Bukin 
1913d5b3b0aSRuslan Bukin 	if (xchan->caps & XCHAN_CAP_BUSDMA)
192101869a8SRuslan Bukin 		ret = xchan_bufs_alloc_busdma(xchan);
193101869a8SRuslan Bukin 	else {
194101869a8SRuslan Bukin 		ret = xchan_bufs_alloc_reserved(xchan);
195101869a8SRuslan Bukin 	}
1963d5b3b0aSRuslan Bukin 	if (ret != 0) {
1973d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
1983d5b3b0aSRuslan Bukin 		    "%s: Can't allocate bufs.\n", __func__);
1993d5b3b0aSRuslan Bukin 		return (-1);
2003d5b3b0aSRuslan Bukin 	}
2013d5b3b0aSRuslan Bukin 
2023d5b3b0aSRuslan Bukin 	xchan->flags |= XCHAN_BUFS_ALLOCATED;
2033d5b3b0aSRuslan Bukin 
2043d5b3b0aSRuslan Bukin 	return (0);
2053d5b3b0aSRuslan Bukin }
2063d5b3b0aSRuslan Bukin 
2073d5b3b0aSRuslan Bukin static int
xchan_bufs_free(xdma_channel_t * xchan)2083d5b3b0aSRuslan Bukin xchan_bufs_free(xdma_channel_t *xchan)
2093d5b3b0aSRuslan Bukin {
2103d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
2113d5b3b0aSRuslan Bukin 	struct xchan_buf *b;
2123d5b3b0aSRuslan Bukin 	int i;
2133d5b3b0aSRuslan Bukin 
2143d5b3b0aSRuslan Bukin 	if ((xchan->flags & XCHAN_BUFS_ALLOCATED) == 0)
2153d5b3b0aSRuslan Bukin 		return (-1);
2163d5b3b0aSRuslan Bukin 
2173d5b3b0aSRuslan Bukin 	if (xchan->caps & XCHAN_CAP_BUSDMA) {
2183d5b3b0aSRuslan Bukin 		for (i = 0; i < xchan->xr_num; i++) {
2193d5b3b0aSRuslan Bukin 			xr = &xchan->xr_mem[i];
2203d5b3b0aSRuslan Bukin 			b = &xr->buf;
2213d5b3b0aSRuslan Bukin 			bus_dmamap_destroy(xchan->dma_tag_bufs, b->map);
2223d5b3b0aSRuslan Bukin 		}
2233d5b3b0aSRuslan Bukin 		bus_dma_tag_destroy(xchan->dma_tag_bufs);
224101869a8SRuslan Bukin 	} else
225101869a8SRuslan Bukin 		xchan_bufs_free_reserved(xchan);
2263d5b3b0aSRuslan Bukin 
2273d5b3b0aSRuslan Bukin 	xchan->flags &= ~XCHAN_BUFS_ALLOCATED;
2283d5b3b0aSRuslan Bukin 
2293d5b3b0aSRuslan Bukin 	return (0);
2303d5b3b0aSRuslan Bukin }
2313d5b3b0aSRuslan Bukin 
2323d5b3b0aSRuslan Bukin void
xdma_channel_free_sg(xdma_channel_t * xchan)2333d5b3b0aSRuslan Bukin xdma_channel_free_sg(xdma_channel_t *xchan)
2343d5b3b0aSRuslan Bukin {
2353d5b3b0aSRuslan Bukin 
2363d5b3b0aSRuslan Bukin 	xchan_bufs_free(xchan);
2373d5b3b0aSRuslan Bukin 	xchan_sglist_free(xchan);
2383d5b3b0aSRuslan Bukin 	xchan_bank_free(xchan);
2393d5b3b0aSRuslan Bukin }
2403d5b3b0aSRuslan Bukin 
2413d5b3b0aSRuslan Bukin /*
2423d5b3b0aSRuslan Bukin  * Prepare xchan for a scatter-gather transfer.
2433d5b3b0aSRuslan Bukin  * xr_num - xdma requests queue size,
2443d5b3b0aSRuslan Bukin  * maxsegsize - maximum allowed scatter-gather list element size in bytes
2453d5b3b0aSRuslan Bukin  */
2463d5b3b0aSRuslan Bukin int
xdma_prep_sg(xdma_channel_t * xchan,uint32_t xr_num,bus_size_t maxsegsize,bus_size_t maxnsegs,bus_size_t alignment,bus_addr_t boundary,bus_addr_t lowaddr,bus_addr_t highaddr)2473d5b3b0aSRuslan Bukin xdma_prep_sg(xdma_channel_t *xchan, uint32_t xr_num,
2483d5b3b0aSRuslan Bukin     bus_size_t maxsegsize, bus_size_t maxnsegs,
2493d5b3b0aSRuslan Bukin     bus_size_t alignment, bus_addr_t boundary,
2503d5b3b0aSRuslan Bukin     bus_addr_t lowaddr, bus_addr_t highaddr)
2513d5b3b0aSRuslan Bukin {
2523d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
2533d5b3b0aSRuslan Bukin 	int ret;
2543d5b3b0aSRuslan Bukin 
2553d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
2563d5b3b0aSRuslan Bukin 
2573d5b3b0aSRuslan Bukin 	KASSERT(xdma != NULL, ("xdma is NULL"));
2583d5b3b0aSRuslan Bukin 
2593d5b3b0aSRuslan Bukin 	if (xchan->flags & XCHAN_CONFIGURED) {
2603d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
2613d5b3b0aSRuslan Bukin 		    "%s: Channel is already configured.\n", __func__);
2623d5b3b0aSRuslan Bukin 		return (-1);
2633d5b3b0aSRuslan Bukin 	}
2643d5b3b0aSRuslan Bukin 
2653d5b3b0aSRuslan Bukin 	xchan->xr_num = xr_num;
2663d5b3b0aSRuslan Bukin 	xchan->maxsegsize = maxsegsize;
2673d5b3b0aSRuslan Bukin 	xchan->maxnsegs = maxnsegs;
2683d5b3b0aSRuslan Bukin 	xchan->alignment = alignment;
2693d5b3b0aSRuslan Bukin 	xchan->boundary = boundary;
2703d5b3b0aSRuslan Bukin 	xchan->lowaddr = lowaddr;
2713d5b3b0aSRuslan Bukin 	xchan->highaddr = highaddr;
2723d5b3b0aSRuslan Bukin 
2733d5b3b0aSRuslan Bukin 	if (xchan->maxnsegs > XDMA_MAX_SEG) {
2743d5b3b0aSRuslan Bukin 		device_printf(xdma->dev, "%s: maxnsegs is too big\n",
2753d5b3b0aSRuslan Bukin 		    __func__);
2763d5b3b0aSRuslan Bukin 		return (-1);
2773d5b3b0aSRuslan Bukin 	}
2783d5b3b0aSRuslan Bukin 
2793d5b3b0aSRuslan Bukin 	xchan_bank_init(xchan);
2803d5b3b0aSRuslan Bukin 
2813d5b3b0aSRuslan Bukin 	/* Allocate sglist. */
2823d5b3b0aSRuslan Bukin 	ret = xchan_sglist_alloc(xchan);
2833d5b3b0aSRuslan Bukin 	if (ret != 0) {
2843d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
2853d5b3b0aSRuslan Bukin 		    "%s: Can't allocate sglist.\n", __func__);
2863d5b3b0aSRuslan Bukin 		return (-1);
2873d5b3b0aSRuslan Bukin 	}
2883d5b3b0aSRuslan Bukin 
2895a51e5e4SRuslan Bukin 	/* Allocate buffers if required. */
2900c340d7eSRuslan Bukin 	if (xchan->caps & (XCHAN_CAP_BUSDMA | XCHAN_CAP_BOUNCE)) {
2913d5b3b0aSRuslan Bukin 		ret = xchan_bufs_alloc(xchan);
2923d5b3b0aSRuslan Bukin 		if (ret != 0) {
2933d5b3b0aSRuslan Bukin 			device_printf(xdma->dev,
2943d5b3b0aSRuslan Bukin 			    "%s: Can't allocate bufs.\n", __func__);
2953d5b3b0aSRuslan Bukin 
2963d5b3b0aSRuslan Bukin 			/* Cleanup */
2973d5b3b0aSRuslan Bukin 			xchan_sglist_free(xchan);
2983d5b3b0aSRuslan Bukin 			xchan_bank_free(xchan);
2993d5b3b0aSRuslan Bukin 
3003d5b3b0aSRuslan Bukin 			return (-1);
3013d5b3b0aSRuslan Bukin 		}
3025a51e5e4SRuslan Bukin 	}
3033d5b3b0aSRuslan Bukin 
3043d5b3b0aSRuslan Bukin 	xchan->flags |= (XCHAN_CONFIGURED | XCHAN_TYPE_SG);
3053d5b3b0aSRuslan Bukin 
3063d5b3b0aSRuslan Bukin 	XCHAN_LOCK(xchan);
3073d5b3b0aSRuslan Bukin 	ret = XDMA_CHANNEL_PREP_SG(xdma->dma_dev, xchan);
3083d5b3b0aSRuslan Bukin 	if (ret != 0) {
3093d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
3103d5b3b0aSRuslan Bukin 		    "%s: Can't prepare SG transfer.\n", __func__);
3113d5b3b0aSRuslan Bukin 		XCHAN_UNLOCK(xchan);
3123d5b3b0aSRuslan Bukin 
3133d5b3b0aSRuslan Bukin 		return (-1);
3143d5b3b0aSRuslan Bukin 	}
3153d5b3b0aSRuslan Bukin 	XCHAN_UNLOCK(xchan);
3163d5b3b0aSRuslan Bukin 
3173d5b3b0aSRuslan Bukin 	return (0);
3183d5b3b0aSRuslan Bukin }
3193d5b3b0aSRuslan Bukin 
3203d5b3b0aSRuslan Bukin void
xchan_seg_done(xdma_channel_t * xchan,struct xdma_transfer_status * st)3213d5b3b0aSRuslan Bukin xchan_seg_done(xdma_channel_t *xchan,
3223d5b3b0aSRuslan Bukin     struct xdma_transfer_status *st)
3233d5b3b0aSRuslan Bukin {
3243d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
3253d5b3b0aSRuslan Bukin 	struct xchan_buf *b;
326951e0584SRuslan Bukin 	bus_addr_t addr;
3273d5b3b0aSRuslan Bukin 
3283d5b3b0aSRuslan Bukin 	xr = TAILQ_FIRST(&xchan->processing);
3293d5b3b0aSRuslan Bukin 	if (xr == NULL)
3303d5b3b0aSRuslan Bukin 		panic("request not found\n");
3313d5b3b0aSRuslan Bukin 
3323d5b3b0aSRuslan Bukin 	b = &xr->buf;
3333d5b3b0aSRuslan Bukin 
3343d5b3b0aSRuslan Bukin 	atomic_subtract_int(&b->nsegs_left, 1);
3353d5b3b0aSRuslan Bukin 
3363d5b3b0aSRuslan Bukin 	if (b->nsegs_left == 0) {
3373d5b3b0aSRuslan Bukin 		if (xchan->caps & XCHAN_CAP_BUSDMA) {
3383d5b3b0aSRuslan Bukin 			if (xr->direction == XDMA_MEM_TO_DEV)
3393d5b3b0aSRuslan Bukin 				bus_dmamap_sync(xchan->dma_tag_bufs, b->map,
3403d5b3b0aSRuslan Bukin 				    BUS_DMASYNC_POSTWRITE);
3413d5b3b0aSRuslan Bukin 			else
3423d5b3b0aSRuslan Bukin 				bus_dmamap_sync(xchan->dma_tag_bufs, b->map,
3433d5b3b0aSRuslan Bukin 				    BUS_DMASYNC_POSTREAD);
3443d5b3b0aSRuslan Bukin 			bus_dmamap_unload(xchan->dma_tag_bufs, b->map);
3450c340d7eSRuslan Bukin 		} else if (xchan->caps & XCHAN_CAP_BOUNCE) {
3460c340d7eSRuslan Bukin 			if (xr->req_type == XR_TYPE_MBUF &&
347101869a8SRuslan Bukin 			    xr->direction == XDMA_DEV_TO_MEM)
348101869a8SRuslan Bukin 				m_copyback(xr->m, 0, st->transferred,
349101869a8SRuslan Bukin 				    (void *)xr->buf.vaddr);
350951e0584SRuslan Bukin 		} else if (xchan->caps & XCHAN_CAP_IOMMU) {
351951e0584SRuslan Bukin 			if (xr->direction == XDMA_MEM_TO_DEV)
352951e0584SRuslan Bukin 				addr = xr->src_addr;
353951e0584SRuslan Bukin 			else
354951e0584SRuslan Bukin 				addr = xr->dst_addr;
355951e0584SRuslan Bukin 			xdma_iommu_remove_entry(xchan, addr);
3563d5b3b0aSRuslan Bukin 		}
3573d5b3b0aSRuslan Bukin 		xr->status.error = st->error;
3583d5b3b0aSRuslan Bukin 		xr->status.transferred = st->transferred;
3593d5b3b0aSRuslan Bukin 
3603d5b3b0aSRuslan Bukin 		QUEUE_PROC_LOCK(xchan);
3613d5b3b0aSRuslan Bukin 		TAILQ_REMOVE(&xchan->processing, xr, xr_next);
3623d5b3b0aSRuslan Bukin 		QUEUE_PROC_UNLOCK(xchan);
3633d5b3b0aSRuslan Bukin 
3643d5b3b0aSRuslan Bukin 		QUEUE_OUT_LOCK(xchan);
3653d5b3b0aSRuslan Bukin 		TAILQ_INSERT_TAIL(&xchan->queue_out, xr, xr_next);
3663d5b3b0aSRuslan Bukin 		QUEUE_OUT_UNLOCK(xchan);
3673d5b3b0aSRuslan Bukin 	}
3683d5b3b0aSRuslan Bukin }
3693d5b3b0aSRuslan Bukin 
3703d5b3b0aSRuslan Bukin static void
xdma_dmamap_cb(void * arg,bus_dma_segment_t * segs,int nsegs,int error)3713d5b3b0aSRuslan Bukin xdma_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
3723d5b3b0aSRuslan Bukin {
3733d5b3b0aSRuslan Bukin 	struct seg_load_request *slr;
3743d5b3b0aSRuslan Bukin 	struct bus_dma_segment *seg;
3753d5b3b0aSRuslan Bukin 	int i;
3763d5b3b0aSRuslan Bukin 
3773d5b3b0aSRuslan Bukin 	slr = arg;
3783d5b3b0aSRuslan Bukin 	seg = slr->seg;
3793d5b3b0aSRuslan Bukin 
3803d5b3b0aSRuslan Bukin 	if (error != 0) {
3813d5b3b0aSRuslan Bukin 		slr->error = error;
3823d5b3b0aSRuslan Bukin 		return;
3833d5b3b0aSRuslan Bukin 	}
3843d5b3b0aSRuslan Bukin 
3853d5b3b0aSRuslan Bukin 	slr->nsegs = nsegs;
3863d5b3b0aSRuslan Bukin 
3873d5b3b0aSRuslan Bukin 	for (i = 0; i < nsegs; i++) {
3883d5b3b0aSRuslan Bukin 		seg[i].ds_addr = segs[i].ds_addr;
3893d5b3b0aSRuslan Bukin 		seg[i].ds_len = segs[i].ds_len;
3903d5b3b0aSRuslan Bukin 	}
3913d5b3b0aSRuslan Bukin }
3923d5b3b0aSRuslan Bukin 
3933d5b3b0aSRuslan Bukin static int
_xdma_load_data_busdma(xdma_channel_t * xchan,struct xdma_request * xr,struct bus_dma_segment * seg)3943d5b3b0aSRuslan Bukin _xdma_load_data_busdma(xdma_channel_t *xchan, struct xdma_request *xr,
3953d5b3b0aSRuslan Bukin     struct bus_dma_segment *seg)
3963d5b3b0aSRuslan Bukin {
3973d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
3983d5b3b0aSRuslan Bukin 	struct seg_load_request slr;
3993d5b3b0aSRuslan Bukin 	uint32_t nsegs;
4003d5b3b0aSRuslan Bukin 	void *addr;
4013d5b3b0aSRuslan Bukin 	int error;
4023d5b3b0aSRuslan Bukin 
4033d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
4043d5b3b0aSRuslan Bukin 
4053d5b3b0aSRuslan Bukin 	error = 0;
4063d5b3b0aSRuslan Bukin 	nsegs = 0;
4073d5b3b0aSRuslan Bukin 
4083d5b3b0aSRuslan Bukin 	switch (xr->req_type) {
4093d5b3b0aSRuslan Bukin 	case XR_TYPE_MBUF:
4103d5b3b0aSRuslan Bukin 		error = bus_dmamap_load_mbuf_sg(xchan->dma_tag_bufs,
4113d5b3b0aSRuslan Bukin 		    xr->buf.map, xr->m, seg, &nsegs, BUS_DMA_NOWAIT);
4123d5b3b0aSRuslan Bukin 		break;
4133d5b3b0aSRuslan Bukin 	case XR_TYPE_BIO:
4143d5b3b0aSRuslan Bukin 		slr.nsegs = 0;
4153d5b3b0aSRuslan Bukin 		slr.error = 0;
4163d5b3b0aSRuslan Bukin 		slr.seg = seg;
4173d5b3b0aSRuslan Bukin 		error = bus_dmamap_load_bio(xchan->dma_tag_bufs,
4183d5b3b0aSRuslan Bukin 		    xr->buf.map, xr->bp, xdma_dmamap_cb, &slr, BUS_DMA_NOWAIT);
4193d5b3b0aSRuslan Bukin 		if (slr.error != 0) {
4203d5b3b0aSRuslan Bukin 			device_printf(xdma->dma_dev,
4213d5b3b0aSRuslan Bukin 			    "%s: bus_dmamap_load failed, err %d\n",
4223d5b3b0aSRuslan Bukin 			    __func__, slr.error);
4233d5b3b0aSRuslan Bukin 			return (0);
4243d5b3b0aSRuslan Bukin 		}
4253d5b3b0aSRuslan Bukin 		nsegs = slr.nsegs;
4263d5b3b0aSRuslan Bukin 		break;
4273d5b3b0aSRuslan Bukin 	case XR_TYPE_VIRT:
4283d5b3b0aSRuslan Bukin 		switch (xr->direction) {
4293d5b3b0aSRuslan Bukin 		case XDMA_MEM_TO_DEV:
4303d5b3b0aSRuslan Bukin 			addr = (void *)xr->src_addr;
4313d5b3b0aSRuslan Bukin 			break;
4323d5b3b0aSRuslan Bukin 		case XDMA_DEV_TO_MEM:
4333d5b3b0aSRuslan Bukin 			addr = (void *)xr->dst_addr;
4343d5b3b0aSRuslan Bukin 			break;
4353d5b3b0aSRuslan Bukin 		default:
4363d5b3b0aSRuslan Bukin 			device_printf(xdma->dma_dev,
4373d5b3b0aSRuslan Bukin 			    "%s: Direction is not supported\n", __func__);
4383d5b3b0aSRuslan Bukin 			return (0);
4393d5b3b0aSRuslan Bukin 		}
4403d5b3b0aSRuslan Bukin 		slr.nsegs = 0;
4413d5b3b0aSRuslan Bukin 		slr.error = 0;
4423d5b3b0aSRuslan Bukin 		slr.seg = seg;
4433d5b3b0aSRuslan Bukin 		error = bus_dmamap_load(xchan->dma_tag_bufs, xr->buf.map,
4443d5b3b0aSRuslan Bukin 		    addr, (xr->block_len * xr->block_num),
4453d5b3b0aSRuslan Bukin 		    xdma_dmamap_cb, &slr, BUS_DMA_NOWAIT);
4463d5b3b0aSRuslan Bukin 		if (slr.error != 0) {
4473d5b3b0aSRuslan Bukin 			device_printf(xdma->dma_dev,
4483d5b3b0aSRuslan Bukin 			    "%s: bus_dmamap_load failed, err %d\n",
4493d5b3b0aSRuslan Bukin 			    __func__, slr.error);
4503d5b3b0aSRuslan Bukin 			return (0);
4513d5b3b0aSRuslan Bukin 		}
4523d5b3b0aSRuslan Bukin 		nsegs = slr.nsegs;
4533d5b3b0aSRuslan Bukin 		break;
4543d5b3b0aSRuslan Bukin 	default:
4553d5b3b0aSRuslan Bukin 		break;
4563d5b3b0aSRuslan Bukin 	}
4573d5b3b0aSRuslan Bukin 
4583d5b3b0aSRuslan Bukin 	if (error != 0) {
4593d5b3b0aSRuslan Bukin 		if (error == ENOMEM) {
4603d5b3b0aSRuslan Bukin 			/*
4613d5b3b0aSRuslan Bukin 			 * Out of memory. Try again later.
4623d5b3b0aSRuslan Bukin 			 * TODO: count errors.
4633d5b3b0aSRuslan Bukin 			 */
4643d5b3b0aSRuslan Bukin 		} else
4653d5b3b0aSRuslan Bukin 			device_printf(xdma->dma_dev,
4663d5b3b0aSRuslan Bukin 			    "%s: bus_dmamap_load failed with err %d\n",
4673d5b3b0aSRuslan Bukin 			    __func__, error);
4683d5b3b0aSRuslan Bukin 		return (0);
4693d5b3b0aSRuslan Bukin 	}
4703d5b3b0aSRuslan Bukin 
4713d5b3b0aSRuslan Bukin 	if (xr->direction == XDMA_MEM_TO_DEV)
4723d5b3b0aSRuslan Bukin 		bus_dmamap_sync(xchan->dma_tag_bufs, xr->buf.map,
4733d5b3b0aSRuslan Bukin 		    BUS_DMASYNC_PREWRITE);
4743d5b3b0aSRuslan Bukin 	else
4753d5b3b0aSRuslan Bukin 		bus_dmamap_sync(xchan->dma_tag_bufs, xr->buf.map,
4763d5b3b0aSRuslan Bukin 		    BUS_DMASYNC_PREREAD);
4773d5b3b0aSRuslan Bukin 
4783d5b3b0aSRuslan Bukin 	return (nsegs);
4793d5b3b0aSRuslan Bukin }
4803d5b3b0aSRuslan Bukin 
4813d5b3b0aSRuslan Bukin static int
_xdma_load_data(xdma_channel_t * xchan,struct xdma_request * xr,struct bus_dma_segment * seg)4823d5b3b0aSRuslan Bukin _xdma_load_data(xdma_channel_t *xchan, struct xdma_request *xr,
4833d5b3b0aSRuslan Bukin     struct bus_dma_segment *seg)
4843d5b3b0aSRuslan Bukin {
4853d5b3b0aSRuslan Bukin 	struct mbuf *m;
4863d5b3b0aSRuslan Bukin 	uint32_t nsegs;
487951e0584SRuslan Bukin 	vm_offset_t va, addr;
488951e0584SRuslan Bukin 	bus_addr_t pa;
489951e0584SRuslan Bukin 	vm_prot_t prot;
4903d5b3b0aSRuslan Bukin 
4913d5b3b0aSRuslan Bukin 	m = xr->m;
4923d5b3b0aSRuslan Bukin 
493*a00eed96SRuslan Bukin 	KASSERT(xchan->caps & (XCHAN_CAP_NOSEG | XCHAN_CAP_BOUNCE),
494951e0584SRuslan Bukin 	    ("Handling segmented data is not implemented here."));
495951e0584SRuslan Bukin 
4963d5b3b0aSRuslan Bukin 	nsegs = 1;
4973d5b3b0aSRuslan Bukin 
4983d5b3b0aSRuslan Bukin 	switch (xr->req_type) {
4993d5b3b0aSRuslan Bukin 	case XR_TYPE_MBUF:
50017f1623dSRuslan Bukin 		if (xchan->caps & XCHAN_CAP_BOUNCE) {
501101869a8SRuslan Bukin 			if (xr->direction == XDMA_MEM_TO_DEV)
502101869a8SRuslan Bukin 				m_copydata(m, 0, m->m_pkthdr.len,
503101869a8SRuslan Bukin 				    (void *)xr->buf.vaddr);
504101869a8SRuslan Bukin 			seg[0].ds_addr = (bus_addr_t)xr->buf.paddr;
505951e0584SRuslan Bukin 		} else if (xchan->caps & XCHAN_CAP_IOMMU) {
506951e0584SRuslan Bukin 			addr = mtod(m, bus_addr_t);
507951e0584SRuslan Bukin 			pa = vtophys(addr);
508951e0584SRuslan Bukin 
509951e0584SRuslan Bukin 			if (xr->direction == XDMA_MEM_TO_DEV)
510951e0584SRuslan Bukin 				prot = VM_PROT_READ;
511951e0584SRuslan Bukin 			else
512951e0584SRuslan Bukin 				prot = VM_PROT_WRITE;
513951e0584SRuslan Bukin 
514951e0584SRuslan Bukin 			xdma_iommu_add_entry(xchan, &va,
515951e0584SRuslan Bukin 			    pa, m->m_pkthdr.len, prot);
516951e0584SRuslan Bukin 
517951e0584SRuslan Bukin 			/*
518951e0584SRuslan Bukin 			 * Save VA so we can unload data later
519951e0584SRuslan Bukin 			 * after completion of this transfer.
520951e0584SRuslan Bukin 			 */
521951e0584SRuslan Bukin 			if (xr->direction == XDMA_MEM_TO_DEV)
522951e0584SRuslan Bukin 				xr->src_addr = va;
523951e0584SRuslan Bukin 			else
524951e0584SRuslan Bukin 				xr->dst_addr = va;
525951e0584SRuslan Bukin 			seg[0].ds_addr = va;
52617f1623dSRuslan Bukin 		} else
52717f1623dSRuslan Bukin 			seg[0].ds_addr = mtod(m, bus_addr_t);
5283d5b3b0aSRuslan Bukin 		seg[0].ds_len = m->m_pkthdr.len;
5293d5b3b0aSRuslan Bukin 		break;
5303d5b3b0aSRuslan Bukin 	case XR_TYPE_BIO:
5313d5b3b0aSRuslan Bukin 	case XR_TYPE_VIRT:
5323d5b3b0aSRuslan Bukin 	default:
5333d5b3b0aSRuslan Bukin 		panic("implement me\n");
5343d5b3b0aSRuslan Bukin 	}
5353d5b3b0aSRuslan Bukin 
5363d5b3b0aSRuslan Bukin 	return (nsegs);
5373d5b3b0aSRuslan Bukin }
5383d5b3b0aSRuslan Bukin 
5393d5b3b0aSRuslan Bukin static int
xdma_load_data(xdma_channel_t * xchan,struct xdma_request * xr,struct bus_dma_segment * seg)5403d5b3b0aSRuslan Bukin xdma_load_data(xdma_channel_t *xchan,
5413d5b3b0aSRuslan Bukin     struct xdma_request *xr, struct bus_dma_segment *seg)
5423d5b3b0aSRuslan Bukin {
5433d5b3b0aSRuslan Bukin 	int nsegs;
5443d5b3b0aSRuslan Bukin 
5453d5b3b0aSRuslan Bukin 	nsegs = 0;
5463d5b3b0aSRuslan Bukin 
5473d5b3b0aSRuslan Bukin 	if (xchan->caps & XCHAN_CAP_BUSDMA)
5483d5b3b0aSRuslan Bukin 		nsegs = _xdma_load_data_busdma(xchan, xr, seg);
5493d5b3b0aSRuslan Bukin 	else
5503d5b3b0aSRuslan Bukin 		nsegs = _xdma_load_data(xchan, xr, seg);
5513d5b3b0aSRuslan Bukin 	if (nsegs == 0)
5523d5b3b0aSRuslan Bukin 		return (0); /* Try again later. */
5533d5b3b0aSRuslan Bukin 
5543d5b3b0aSRuslan Bukin 	xr->buf.nsegs = nsegs;
5553d5b3b0aSRuslan Bukin 	xr->buf.nsegs_left = nsegs;
5563d5b3b0aSRuslan Bukin 
5573d5b3b0aSRuslan Bukin 	return (nsegs);
5583d5b3b0aSRuslan Bukin }
5593d5b3b0aSRuslan Bukin 
5603d5b3b0aSRuslan Bukin static int
xdma_process(xdma_channel_t * xchan,struct xdma_sglist * sg)5613d5b3b0aSRuslan Bukin xdma_process(xdma_channel_t *xchan,
5623d5b3b0aSRuslan Bukin     struct xdma_sglist *sg)
5633d5b3b0aSRuslan Bukin {
5643d5b3b0aSRuslan Bukin 	struct bus_dma_segment seg[XDMA_MAX_SEG];
5653d5b3b0aSRuslan Bukin 	struct xdma_request *xr;
5663d5b3b0aSRuslan Bukin 	struct xdma_request *xr_tmp;
5673d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
5683d5b3b0aSRuslan Bukin 	uint32_t capacity;
5693d5b3b0aSRuslan Bukin 	uint32_t n;
5703d5b3b0aSRuslan Bukin 	uint32_t c;
5713d5b3b0aSRuslan Bukin 	int nsegs;
5723d5b3b0aSRuslan Bukin 	int ret;
5733d5b3b0aSRuslan Bukin 
5743d5b3b0aSRuslan Bukin 	XCHAN_ASSERT_LOCKED(xchan);
5753d5b3b0aSRuslan Bukin 
5763d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
5773d5b3b0aSRuslan Bukin 
5783d5b3b0aSRuslan Bukin 	n = 0;
579101869a8SRuslan Bukin 	c = 0;
5803d5b3b0aSRuslan Bukin 
5813d5b3b0aSRuslan Bukin 	ret = XDMA_CHANNEL_CAPACITY(xdma->dma_dev, xchan, &capacity);
5823d5b3b0aSRuslan Bukin 	if (ret != 0) {
5833d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
5843d5b3b0aSRuslan Bukin 		    "%s: Can't get DMA controller capacity.\n", __func__);
5853d5b3b0aSRuslan Bukin 		return (-1);
5863d5b3b0aSRuslan Bukin 	}
5873d5b3b0aSRuslan Bukin 
5883d5b3b0aSRuslan Bukin 	TAILQ_FOREACH_SAFE(xr, &xchan->queue_in, xr_next, xr_tmp) {
5893d5b3b0aSRuslan Bukin 		switch (xr->req_type) {
5903d5b3b0aSRuslan Bukin 		case XR_TYPE_MBUF:
5915a51e5e4SRuslan Bukin 			if ((xchan->caps & XCHAN_CAP_NOSEG) ||
5925a51e5e4SRuslan Bukin 			    (c > xchan->maxnsegs))
5933d5b3b0aSRuslan Bukin 				c = xdma_mbuf_defrag(xchan, xr);
5943d5b3b0aSRuslan Bukin 			break;
5953d5b3b0aSRuslan Bukin 		case XR_TYPE_BIO:
5963d5b3b0aSRuslan Bukin 		case XR_TYPE_VIRT:
5973d5b3b0aSRuslan Bukin 		default:
5983d5b3b0aSRuslan Bukin 			c = 1;
5993d5b3b0aSRuslan Bukin 		}
6003d5b3b0aSRuslan Bukin 
6013d5b3b0aSRuslan Bukin 		if (capacity <= (c + n)) {
6023d5b3b0aSRuslan Bukin 			/*
6033d5b3b0aSRuslan Bukin 			 * No space yet available for the entire
6043d5b3b0aSRuslan Bukin 			 * request in the DMA engine.
6053d5b3b0aSRuslan Bukin 			 */
6063d5b3b0aSRuslan Bukin 			break;
6073d5b3b0aSRuslan Bukin 		}
6083d5b3b0aSRuslan Bukin 
6093d5b3b0aSRuslan Bukin 		if ((c + n + xchan->maxnsegs) >= XDMA_SGLIST_MAXLEN) {
6103d5b3b0aSRuslan Bukin 			/* Sglist is full. */
6113d5b3b0aSRuslan Bukin 			break;
6123d5b3b0aSRuslan Bukin 		}
6133d5b3b0aSRuslan Bukin 
6143d5b3b0aSRuslan Bukin 		nsegs = xdma_load_data(xchan, xr, seg);
6153d5b3b0aSRuslan Bukin 		if (nsegs == 0)
6163d5b3b0aSRuslan Bukin 			break;
6173d5b3b0aSRuslan Bukin 
6183d5b3b0aSRuslan Bukin 		xdma_sglist_add(&sg[n], seg, nsegs, xr);
6193d5b3b0aSRuslan Bukin 		n += nsegs;
6203d5b3b0aSRuslan Bukin 
6213d5b3b0aSRuslan Bukin 		QUEUE_IN_LOCK(xchan);
6223d5b3b0aSRuslan Bukin 		TAILQ_REMOVE(&xchan->queue_in, xr, xr_next);
6233d5b3b0aSRuslan Bukin 		QUEUE_IN_UNLOCK(xchan);
6243d5b3b0aSRuslan Bukin 
6253d5b3b0aSRuslan Bukin 		QUEUE_PROC_LOCK(xchan);
6263d5b3b0aSRuslan Bukin 		TAILQ_INSERT_TAIL(&xchan->processing, xr, xr_next);
6273d5b3b0aSRuslan Bukin 		QUEUE_PROC_UNLOCK(xchan);
6283d5b3b0aSRuslan Bukin 	}
6293d5b3b0aSRuslan Bukin 
6303d5b3b0aSRuslan Bukin 	return (n);
6313d5b3b0aSRuslan Bukin }
6323d5b3b0aSRuslan Bukin 
6333d5b3b0aSRuslan Bukin int
xdma_queue_submit_sg(xdma_channel_t * xchan)6343d5b3b0aSRuslan Bukin xdma_queue_submit_sg(xdma_channel_t *xchan)
6353d5b3b0aSRuslan Bukin {
6363d5b3b0aSRuslan Bukin 	struct xdma_sglist *sg;
6373d5b3b0aSRuslan Bukin 	xdma_controller_t *xdma;
6383d5b3b0aSRuslan Bukin 	uint32_t sg_n;
6393d5b3b0aSRuslan Bukin 	int ret;
6403d5b3b0aSRuslan Bukin 
6413d5b3b0aSRuslan Bukin 	xdma = xchan->xdma;
6423d5b3b0aSRuslan Bukin 	KASSERT(xdma != NULL, ("xdma is NULL"));
6433d5b3b0aSRuslan Bukin 
6443d5b3b0aSRuslan Bukin 	XCHAN_ASSERT_LOCKED(xchan);
6453d5b3b0aSRuslan Bukin 
6463d5b3b0aSRuslan Bukin 	sg = xchan->sg;
6473d5b3b0aSRuslan Bukin 
6480c340d7eSRuslan Bukin 	if ((xchan->caps & (XCHAN_CAP_BOUNCE | XCHAN_CAP_BUSDMA)) &&
6495a51e5e4SRuslan Bukin 	   (xchan->flags & XCHAN_BUFS_ALLOCATED) == 0) {
6503d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
6513d5b3b0aSRuslan Bukin 		    "%s: Can't submit a transfer: no bufs\n",
6523d5b3b0aSRuslan Bukin 		    __func__);
6533d5b3b0aSRuslan Bukin 		return (-1);
6543d5b3b0aSRuslan Bukin 	}
6553d5b3b0aSRuslan Bukin 
6563d5b3b0aSRuslan Bukin 	sg_n = xdma_process(xchan, sg);
6573d5b3b0aSRuslan Bukin 	if (sg_n == 0)
6583d5b3b0aSRuslan Bukin 		return (0); /* Nothing to submit */
6593d5b3b0aSRuslan Bukin 
6603d5b3b0aSRuslan Bukin 	/* Now submit sglist to DMA engine driver. */
6613d5b3b0aSRuslan Bukin 	ret = XDMA_CHANNEL_SUBMIT_SG(xdma->dma_dev, xchan, sg, sg_n);
6623d5b3b0aSRuslan Bukin 	if (ret != 0) {
6633d5b3b0aSRuslan Bukin 		device_printf(xdma->dev,
6643d5b3b0aSRuslan Bukin 		    "%s: Can't submit an sglist.\n", __func__);
6653d5b3b0aSRuslan Bukin 		return (-1);
6663d5b3b0aSRuslan Bukin 	}
6673d5b3b0aSRuslan Bukin 
6683d5b3b0aSRuslan Bukin 	return (0);
6693d5b3b0aSRuslan Bukin }
670