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