Author: br
Date: Tue Dec 20 18:02:07 2016
New Revision: 310330
URL: https://svnweb.freebsd.org/changeset/base/310330

Log:
  Add xDMA -- the DMA abstraction layer, initial verison.
  
  xDMA is a DMA framework designed to abstract the interaction
  between device drivers and DMA engines.
  
  Project wiki: https://wiki.freebsd.org/xdma
  
  Sponsored by: DARPA, AFRL
  Differential Revision:        https://reviews.freebsd.org/D8807

Added:
  head/share/man/man4/xdma.4   (contents, props changed)
  head/sys/dev/xdma/
  head/sys/dev/xdma/xdma.c   (contents, props changed)
  head/sys/dev/xdma/xdma.h   (contents, props changed)
  head/sys/dev/xdma/xdma_fdt_test.c   (contents, props changed)
  head/sys/dev/xdma/xdma_if.m   (contents, props changed)
Modified:
  head/sys/conf/files

Added: head/share/man/man4/xdma.4
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/share/man/man4/xdma.4  Tue Dec 20 18:02:07 2016        (r310330)
@@ -0,0 +1,77 @@
+.\" Copyright (c) 2016 Ruslan Bukin <b...@bsdpad.com>
+.\" All rights reserved.
+.\"
+.\" This software was developed by SRI International and the University of
+.\" Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+.\" ("CTSRD"), as part of the DARPA CRASH research programme.
+.\"
+.\" Redistribution and use in source and binary forms, with or without
+.\" modification, are permitted provided that the following conditions
+.\" are met:
+.\" 1. Redistributions of source code must retain the above copyright
+.\"    notice, this list of conditions and the following disclaimer.
+.\" 2. Redistributions in binary form must reproduce the above copyright
+.\"    notice, this list of conditions and the following disclaimer in the
+.\"    documentation and/or other materials provided with the distribution.
+.\"
+.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+.\" ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+.\" SUCH DAMAGE.
+.\"
+.\" $FreeBSD$
+.\"
+.Dd December 12, 2016
+.Dt XDMA 4
+.Os
+.Sh NAME
+.Nm xdma
+.Nd DMA abstraction layer
+.Sh SYNOPSIS
+To compile xDMA device support into the kernel, place the following line
+in your kernel configuration file:
+.Bd -ragged -offset indent
+.Cd "device xdma"
+.Ed
+.Pp
+To compile xDMA FDT-based test driver, place the following line as well:
+.Bd -literal -offset indent
+.Cd "device xdma_test"
+.Ed
+.Sh DESCRIPTION
+xDMA is a DMA framework designed to abstract the interaction between device
+drivers and DMA engines.
+.Pp
+xDMA defines an interface for efficient interaction between the device driver
+and DMA controller.
+The
+.Nm
+device provides a virtual DMA controller and virtual channels called xchans.
+The controller provides virtual channels management (allocation, deallocation,
+configuration) and interrupt notification esteblishment needed to receive
+events from the hardware DMA controller.
+.Nm
+supports the following transfer types:
+.Bl -hang -offset indent -width xxxxxxxx
+.It Nm Cyclic
+A non-stop periodic transfer designed for applications like audio.
+.It Nm Memcpy
+A memory-to-memory transfer.
+.El
+.Sh SEE ALSO
+.Xr bus_dma 9
+.Sh HISTORY
+Support for xDMA first appeared in
+.Fx 12.0 .
+.Sh AUTHORS
+.An -nosplit
+.Fx
+xDMA framework was first added by
+.An Ruslan Bukin Aq Mt b...@freebsd.org .

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files Tue Dec 20 17:12:17 2016        (r310329)
+++ head/sys/conf/files Tue Dec 20 18:02:07 2016        (r310330)
@@ -3262,6 +3262,9 @@ wpi.fw                    optional wpifw                  
                \
        compile-with    "${NORMAL_FW}"                                  \
        no-obj no-implicit-rule                                         \
        clean           "wpi.fw"
+dev/xdma/xdma.c                        optional xdma
+dev/xdma/xdma_if.m             optional xdma
+dev/xdma/xdma_fdt_test.c       optional xdma xdma_test fdt
 dev/xe/if_xe.c                 optional xe
 dev/xe/if_xe_pccard.c          optional xe pccard
 dev/xen/balloon/balloon.c      optional xenhvm

Added: head/sys/dev/xdma/xdma.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/xdma/xdma.c    Tue Dec 20 18:02:07 2016        (r310330)
@@ -0,0 +1,673 @@
+/*-
+ * Copyright (c) 2016 Ruslan Bukin <b...@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/queue.h>
+#include <sys/kobj.h>
+#include <sys/malloc.h>
+#include <sys/mutex.h>
+#include <sys/limits.h>
+#include <sys/lock.h>
+#include <sys/sysctl.h>
+#include <sys/systm.h>
+#include <sys/sx.h>
+#include <sys/bus_dma.h>
+
+#include <machine/bus.h>
+
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include <dev/xdma/xdma.h>
+
+#include <xdma_if.h>
+
+MALLOC_DEFINE(M_XDMA, "xdma", "xDMA framework");
+
+/*
+ * Multiple xDMA controllers may work with single DMA device,
+ * so we have global lock for physical channel management.
+ */
+static struct mtx xdma_mtx;
+#define        XDMA_LOCK()             mtx_lock(&xdma_mtx)
+#define        XDMA_UNLOCK()           mtx_unlock(&xdma_mtx)
+#define        XDMA_ASSERT_LOCKED()    mtx_assert(&xdma_mtx, MA_OWNED)
+
+/*
+ * Per channel locks.
+ */
+#define        XCHAN_LOCK(xchan)               mtx_lock(&xchan->mtx_lock)
+#define        XCHAN_UNLOCK(xchan)             mtx_unlock(&xchan->mtx_lock)
+#define        XCHAN_ASSERT_LOCKED(xchan)      mtx_assert(&xchan->mtx_lock, 
MA_OWNED)
+
+/*
+ * Allocate virtual xDMA channel.
+ */
+xdma_channel_t *
+xdma_channel_alloc(xdma_controller_t *xdma)
+{
+       xdma_channel_t *xchan;
+       int ret;
+
+       xchan = malloc(sizeof(xdma_channel_t), M_XDMA, M_WAITOK | M_ZERO);
+       if (xchan == NULL) {
+               device_printf(xdma->dev,
+                   "%s: Can't allocate memory for channel.\n", __func__);
+               return (NULL);
+       }
+       xchan->xdma = xdma;
+
+       XDMA_LOCK();
+
+       /* Request a real channel from hardware driver. */
+       ret = XDMA_CHANNEL_ALLOC(xdma->dma_dev, xchan);
+       if (ret != 0) {
+               device_printf(xdma->dev,
+                   "%s: Can't request hardware channel.\n", __func__);
+               XDMA_UNLOCK();
+               free(xchan, M_XDMA);
+
+               return (NULL);
+       }
+
+       TAILQ_INIT(&xchan->ie_handlers);
+       mtx_init(&xchan->mtx_lock, "xDMA", NULL, MTX_DEF);
+
+       TAILQ_INSERT_TAIL(&xdma->channels, xchan, xchan_next);
+
+       XDMA_UNLOCK();
+
+       return (xchan);
+}
+
+int
+xdma_channel_free(xdma_channel_t *xchan)
+{
+       xdma_controller_t *xdma;
+       int err;
+
+       xdma = xchan->xdma;
+
+       XDMA_LOCK();
+
+       /* Free the real DMA channel. */
+       err = XDMA_CHANNEL_FREE(xdma->dma_dev, xchan);
+       if (err != 0) {
+               device_printf(xdma->dev,
+                   "%s: Can't free real hw channel.\n", __func__);
+               XDMA_UNLOCK();
+               return (-1);
+       }
+
+       xdma_teardown_all_intr(xchan);
+
+       /* Deallocate descriptors, if any. */
+       xdma_desc_free(xchan);
+
+       mtx_destroy(&xchan->mtx_lock);
+
+       TAILQ_REMOVE(&xdma->channels, xchan, xchan_next);
+
+       free(xchan, M_XDMA);
+
+       XDMA_UNLOCK();
+
+       return (0);
+}
+
+int
+xdma_setup_intr(xdma_channel_t *xchan, int (*cb)(void *), void *arg,
+    void **ihandler)
+{
+       struct xdma_intr_handler *ih;
+       xdma_controller_t *xdma;
+
+       xdma = xchan->xdma;
+       KASSERT(xdma != NULL, ("xdma is NULL"));
+
+       /* Sanity check. */
+       if (cb == NULL) {
+               device_printf(xdma->dev,
+                   "%s: Can't setup interrupt handler.\n",
+                   __func__);
+
+               return (-1);
+       }
+
+       ih = malloc(sizeof(struct xdma_intr_handler),
+           M_XDMA, M_WAITOK | M_ZERO);
+       if (ih == NULL) {
+               device_printf(xdma->dev,
+                   "%s: Can't allocate memory for interrupt handler.\n",
+                   __func__);
+
+               return (-1);
+       }
+
+       ih->cb = cb;
+       ih->cb_user = arg;
+
+       TAILQ_INSERT_TAIL(&xchan->ie_handlers, ih, ih_next);
+
+       if (ihandler != NULL) {
+               *ihandler = ih;
+       }
+
+       return (0);
+}
+
+int
+xdma_teardown_intr(xdma_channel_t *xchan, struct xdma_intr_handler *ih)
+{
+       xdma_controller_t *xdma;
+
+       xdma = xchan->xdma;
+       KASSERT(xdma != NULL, ("xdma is NULL"));
+
+       /* Sanity check. */
+       if (ih == NULL) {
+               device_printf(xdma->dev,
+                   "%s: Can't teardown interrupt.\n", __func__);
+               return (-1);
+       }
+
+       TAILQ_REMOVE(&xchan->ie_handlers, ih, ih_next);
+       free(ih, M_XDMA);
+
+       return (0);
+}
+
+int
+xdma_teardown_all_intr(xdma_channel_t *xchan)
+{
+       struct xdma_intr_handler *ih_tmp;
+       struct xdma_intr_handler *ih;
+       xdma_controller_t *xdma;
+
+       xdma = xchan->xdma;
+       KASSERT(xdma != NULL, ("xdma is NULL"));
+
+       TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp) {
+               TAILQ_REMOVE(&xchan->ie_handlers, ih, ih_next);
+               free(ih, M_XDMA);
+       }
+
+       return (0);
+}
+
+static void
+xdma_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+{
+       xdma_channel_t *xchan;
+       int i;
+
+       xchan = (xdma_channel_t *)arg;
+       KASSERT(xchan != NULL, ("xchan is NULL"));
+
+       if (err) {
+               xchan->map_err = 1;
+               return;
+       }
+
+       for (i = 0; i < nseg; i++) {
+               xchan->descs_phys[i].ds_addr = segs[i].ds_addr;
+               xchan->descs_phys[i].ds_len = segs[i].ds_len;
+       }
+}
+
+static int
+xdma_desc_alloc_bus_dma(xdma_channel_t *xchan, uint32_t desc_size,
+    uint32_t align)
+{
+       xdma_controller_t *xdma;
+       bus_size_t all_desc_sz;
+       xdma_config_t *conf;
+       int nsegments;
+       int err;
+
+       xdma = xchan->xdma;
+       conf = &xchan->conf;
+
+       nsegments = conf->block_num;
+       all_desc_sz = (nsegments * desc_size);
+
+       err = bus_dma_tag_create(
+           bus_get_dma_tag(xdma->dev),
+           align, desc_size,           /* alignment, boundary */
+           BUS_SPACE_MAXADDR_32BIT,    /* lowaddr */
+           BUS_SPACE_MAXADDR,          /* highaddr */
+           NULL, NULL,                 /* filter, filterarg */
+           all_desc_sz, nsegments,     /* maxsize, nsegments*/
+           desc_size, 0,               /* maxsegsize, flags */
+           NULL, NULL,                 /* lockfunc, lockarg */
+           &xchan->dma_tag);
+       if (err) {
+               device_printf(xdma->dev,
+                   "%s: Can't create bus_dma tag.\n", __func__);
+               return (-1);
+       }
+
+       err = bus_dmamem_alloc(xchan->dma_tag, (void **)&xchan->descs,
+           BUS_DMA_WAITOK | BUS_DMA_COHERENT, &xchan->dma_map);
+       if (err) {
+               device_printf(xdma->dev,
+                   "%s: Can't allocate memory for descriptors.\n", __func__);
+               return (-1);
+       }
+
+       xchan->descs_phys = malloc(nsegments * sizeof(xdma_descriptor_t), 
M_XDMA,
+           (M_WAITOK | M_ZERO));
+
+       xchan->map_err = 0;
+       err = bus_dmamap_load(xchan->dma_tag, xchan->dma_map, xchan->descs,
+           all_desc_sz, xdma_dmamap_cb, xchan, BUS_DMA_WAITOK);
+       if (err) {
+               device_printf(xdma->dev,
+                   "%s: Can't load DMA map.\n", __func__);
+               return (-1);
+       }
+
+       if (xchan->map_err != 0) {
+               device_printf(xdma->dev,
+                   "%s: Can't load DMA map.\n", __func__);
+               return (-1);
+       }
+
+       return (0);
+}
+
+/*
+ * This function called by DMA controller driver.
+ */
+int
+xdma_desc_alloc(xdma_channel_t *xchan, uint32_t desc_size, uint32_t align)
+{
+       xdma_controller_t *xdma;
+       xdma_config_t *conf;
+       int ret;
+
+       XCHAN_ASSERT_LOCKED(xchan);
+
+       xdma = xchan->xdma;
+       if (xdma == NULL) {
+               device_printf(xdma->dev,
+                   "%s: Channel was not allocated properly.\n", __func__);
+               return (-1);
+       }
+
+       if (xchan->flags & XCHAN_DESC_ALLOCATED) {
+               device_printf(xdma->dev,
+                   "%s: Descriptors already allocated.\n", __func__);
+               return (-1);
+       }
+
+       if ((xchan->flags & XCHAN_CONFIGURED) == 0) {
+               device_printf(xdma->dev,
+                   "%s: Channel has no configuration.\n", __func__);
+               return (-1);
+       }
+
+       conf = &xchan->conf;
+
+       XCHAN_UNLOCK(xchan);
+       ret = xdma_desc_alloc_bus_dma(xchan, desc_size, align);
+       XCHAN_LOCK(xchan);
+       if (ret != 0) {
+               device_printf(xdma->dev,
+                   "%s: Can't allocate memory for descriptors.\n",
+                   __func__);
+               return (-1);
+       }
+
+       xchan->flags |= XCHAN_DESC_ALLOCATED;
+
+       /* We are going to write to descriptors. */
+       bus_dmamap_sync(xchan->dma_tag, xchan->dma_map, BUS_DMASYNC_PREWRITE);
+
+       return (0);
+}
+
+int
+xdma_desc_free(xdma_channel_t *xchan)
+{
+
+       if ((xchan->flags & XCHAN_DESC_ALLOCATED) == 0) {
+               /* No descriptors allocated. */
+               return (-1);
+       }
+
+       bus_dmamap_unload(xchan->dma_tag, xchan->dma_map);
+       bus_dmamem_free(xchan->dma_tag, xchan->descs, xchan->dma_map);
+       bus_dma_tag_destroy(xchan->dma_tag);
+       free(xchan->descs_phys, M_XDMA);
+
+       xchan->flags &= ~(XCHAN_DESC_ALLOCATED);
+
+       return (0);
+}
+
+int
+xdma_prep_memcpy(xdma_channel_t *xchan, uintptr_t src_addr,
+    uintptr_t dst_addr, size_t len)
+{
+       xdma_controller_t *xdma;
+       xdma_config_t *conf;
+       int ret;
+
+       xdma = xchan->xdma;
+       KASSERT(xdma != NULL, ("xdma is NULL"));
+
+       conf = &xchan->conf;
+       conf->direction = XDMA_MEM_TO_MEM;
+       conf->src_addr = src_addr;
+       conf->dst_addr = dst_addr;
+       conf->block_len = len;
+       conf->block_num = 1;
+
+       xchan->flags |= (XCHAN_CONFIGURED | XCHAN_TYPE_MEMCPY);
+
+       XCHAN_LOCK(xchan);
+
+       /* Deallocate old descriptors, if any. */
+       xdma_desc_free(xchan);
+
+       ret = XDMA_CHANNEL_PREP_MEMCPY(xdma->dma_dev, xchan);
+       if (ret != 0) {
+               device_printf(xdma->dev,
+                   "%s: Can't prepare memcpy transfer.\n", __func__);
+               XDMA_UNLOCK();
+
+               return (-1);
+       }
+
+       if (xchan->flags & XCHAN_DESC_ALLOCATED) {
+               /* Driver created xDMA descriptors. */
+               bus_dmamap_sync(xchan->dma_tag, xchan->dma_map,
+                   BUS_DMASYNC_POSTWRITE);
+       }
+
+       XCHAN_UNLOCK(xchan);
+
+       return (0);
+}
+
+int
+xdma_prep_cyclic(xdma_channel_t *xchan, enum xdma_direction dir,
+    uintptr_t src_addr, uintptr_t dst_addr, int block_len,
+    int block_num, int src_width, int dst_width)
+{
+       xdma_controller_t *xdma;
+       xdma_config_t *conf;
+       int ret;
+
+       xdma = xchan->xdma;
+       KASSERT(xdma != NULL, ("xdma is NULL"));
+
+       conf = &xchan->conf;
+       conf->direction = dir;
+       conf->src_addr = src_addr;
+       conf->dst_addr = dst_addr;
+       conf->block_len = block_len;
+       conf->block_num = block_num;
+       conf->src_width = src_width;
+       conf->dst_width = dst_width;
+
+       xchan->flags |= (XCHAN_CONFIGURED | XCHAN_TYPE_CYCLIC);
+
+       XCHAN_LOCK(xchan);
+
+       /* Deallocate old descriptors, if any. */
+       xdma_desc_free(xchan);
+
+       ret = XDMA_CHANNEL_PREP_CYCLIC(xdma->dma_dev, xchan);
+       if (ret != 0) {
+               device_printf(xdma->dev,
+                   "%s: Can't prepare cyclic transfer.\n", __func__);
+               XDMA_UNLOCK();
+               return (-1);
+       }
+
+       if (xchan->flags & XCHAN_DESC_ALLOCATED) {
+               /* Driver has created xDMA descriptors. */
+               bus_dmamap_sync(xchan->dma_tag, xchan->dma_map,
+                   BUS_DMASYNC_POSTWRITE);
+       }
+
+       XCHAN_UNLOCK(xchan);
+
+       return (0);
+}
+
+int
+xdma_begin(xdma_channel_t *xchan)
+{
+       xdma_controller_t *xdma;
+       int ret;
+
+       xdma = xchan->xdma;
+
+       ret = XDMA_CHANNEL_CONTROL(xdma->dma_dev, xchan, XDMA_CMD_BEGIN);
+       if (ret != 0) {
+               device_printf(xdma->dev,
+                   "%s: Can't begin the channel operation.\n", __func__);
+               return (-1);
+       }
+
+       return (0);
+}
+
+int
+xdma_terminate(xdma_channel_t *xchan)
+{
+       xdma_controller_t *xdma;
+       int ret;
+
+       xdma = xchan->xdma;
+
+       ret = XDMA_CHANNEL_CONTROL(xdma->dma_dev, xchan, XDMA_CMD_TERMINATE);
+       if (ret != 0) {
+               device_printf(xdma->dev,
+                   "%s: Can't terminate the channel operation.\n", __func__);
+               return (-1);
+       }
+
+       return (0);
+}
+
+int
+xdma_pause(xdma_channel_t *xchan)
+{
+       xdma_controller_t *xdma;
+       int ret;
+
+       xdma = xchan->xdma;
+
+       ret = XDMA_CHANNEL_CONTROL(xdma->dma_dev, xchan, XDMA_CMD_PAUSE);
+       if (ret != 0) {
+               device_printf(xdma->dev,
+                   "%s: Can't pause the channel operation.\n", __func__);
+               return (-1);
+       }
+
+       return (ret);
+}
+
+int
+xdma_callback(xdma_channel_t *xchan)
+{
+       struct xdma_intr_handler *ih_tmp;
+       struct xdma_intr_handler *ih;
+
+       TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp) {
+               if (ih->cb != NULL) {
+                       ih->cb(ih->cb_user);
+               }
+       }
+
+       return (0);
+}
+
+void
+xdma_assert_locked(void)
+{
+
+       XDMA_ASSERT_LOCKED();
+}
+
+#ifdef FDT
+/*
+ * Notify the DMA driver we have machine-dependent data in FDT.
+ */
+static int
+xdma_ofw_md_data(xdma_controller_t *xdma, pcell_t *cells, int ncells)
+{
+       uint32_t ret;
+
+       ret = XDMA_OFW_MD_DATA(xdma->dma_dev, cells, ncells, (void 
**)&xdma->data);
+
+       return (ret);
+}
+
+/*
+ * Allocate xdma controller.
+ */
+xdma_controller_t *
+xdma_ofw_get(device_t dev, const char *prop)
+{
+       phandle_t node, parent;
+       xdma_controller_t *xdma;
+       device_t dma_dev;
+       pcell_t *cells;
+       int ncells;
+       int error;
+       int ndmas;
+       int idx;
+
+       node = ofw_bus_get_node(dev);
+       if (node <= 0) {
+               device_printf(dev,
+                   "%s called on not ofw based device.\n", __func__);
+       }
+
+       error = ofw_bus_parse_xref_list_get_length(node,
+           "dmas", "#dma-cells", &ndmas);
+       if (error) {
+               device_printf(dev,
+                   "%s can't get dmas list.\n", __func__);
+               return (NULL);
+       }
+
+       if (ndmas == 0) {
+               device_printf(dev,
+                   "%s dmas list is empty.\n", __func__);
+               return (NULL);
+       }
+
+       error = ofw_bus_find_string_index(node, "dma-names", prop, &idx);
+       if (error != 0) {
+               device_printf(dev,
+                   "%s can't find string index.\n", __func__);
+               return (NULL);
+       }
+
+       error = ofw_bus_parse_xref_list_alloc(node, "dmas", "#dma-cells",
+           idx, &parent, &ncells, &cells);
+       if (error != 0) {
+               device_printf(dev,
+                   "%s can't get dma device xref.\n", __func__);
+               return (NULL);
+       }
+
+       dma_dev = OF_device_from_xref(parent);
+       if (dma_dev == NULL) {
+               device_printf(dev,
+                   "%s can't get dma device.\n", __func__);
+               return (NULL);
+       }
+
+       xdma = malloc(sizeof(struct xdma_controller), M_XDMA, M_WAITOK | 
M_ZERO);
+       if (xdma == NULL) {
+               device_printf(dev,
+                   "%s can't allocate memory for xdma.\n", __func__);
+               return (NULL);
+       }
+       xdma->dev = dev;
+       xdma->dma_dev = dma_dev;
+
+       TAILQ_INIT(&xdma->channels);
+
+       xdma_ofw_md_data(xdma, cells, ncells);
+       free(cells, M_OFWPROP);
+
+       return (xdma);
+}
+#endif
+
+/*
+ * Free xDMA controller object.
+ */
+int
+xdma_put(xdma_controller_t *xdma)
+{
+
+       XDMA_LOCK();
+
+       /* Ensure no channels allocated. */
+       if (!TAILQ_EMPTY(&xdma->channels)) {
+               device_printf(xdma->dev, "%s: Can't free xDMA\n", __func__);
+               return (-1);
+       }
+
+       free(xdma->data, M_DEVBUF);
+       free(xdma, M_XDMA);
+
+       XDMA_UNLOCK();
+
+       return (0);
+}
+
+static void
+xdma_init(void)
+{
+
+       mtx_init(&xdma_mtx, "xDMA", NULL, MTX_DEF);
+}
+
+SYSINIT(xdma, SI_SUB_DRIVERS, SI_ORDER_FIRST, xdma_init, NULL);

Added: head/sys/dev/xdma/xdma.h
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/xdma/xdma.h    Tue Dec 20 18:02:07 2016        (r310330)
@@ -0,0 +1,148 @@
+/*-
+ * Copyright (c) 2016 Ruslan Bukin <b...@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * $FreeBSD$
+ */
+
+#ifndef _DEV_EXTRES_XDMA_H_
+#define _DEV_EXTRES_XDMA_H_
+
+enum xdma_direction {
+       XDMA_MEM_TO_MEM,
+       XDMA_MEM_TO_DEV,
+       XDMA_DEV_TO_MEM,
+       XDMA_DEV_TO_DEV,
+};
+
+enum xdma_operation_type {
+       XDMA_MEMCPY,
+       XDMA_SG,
+       XDMA_CYCLIC,
+};
+
+enum xdma_command {
+       XDMA_CMD_BEGIN,
+       XDMA_CMD_PAUSE,
+       XDMA_CMD_TERMINATE,
+       XDMA_CMD_TERMINATE_ALL,
+};
+
+struct xdma_controller {
+       device_t dev;           /* DMA consumer device_t. */
+       device_t dma_dev;       /* A real DMA device_t. */
+       void *data;             /* OFW MD part. */
+
+       /* List of virtual channels allocated. */
+       TAILQ_HEAD(xdma_channel_list, xdma_channel)     channels;
+};
+
+typedef struct xdma_controller xdma_controller_t;
+
+struct xdma_channel_config {
+       enum xdma_direction     direction;
+       uintptr_t               src_addr;       /* Physical address. */
+       uintptr_t               dst_addr;       /* Physical address. */
+       int                     block_len;      /* In bytes. */
+       int                     block_num;      /* Count of blocks. */
+       int                     src_width;      /* In bytes. */
+       int                     dst_width;      /* In bytes. */
+};
+
+typedef struct xdma_channel_config xdma_config_t;
+
+struct xdma_descriptor {
+       uintptr_t       ds_addr;
+       uint32_t        ds_len;
+};
+
+typedef struct xdma_descriptor xdma_descriptor_t;
+
+struct xdma_channel {
+       xdma_controller_t               *xdma;
+       xdma_config_t                   conf;
+
+       uint8_t                         flags;
+#define        XCHAN_DESC_ALLOCATED            (1 << 0)
+#define        XCHAN_CONFIGURED                (1 << 1)
+#define        XCHAN_TYPE_CYCLIC               (1 << 2)
+#define        XCHAN_TYPE_MEMCPY               (1 << 3)
+
+       /* A real hardware driver channel. */
+       void                            *chan;
+
+       /* Interrupt handlers. */
+       TAILQ_HEAD(, xdma_intr_handler) ie_handlers;
+
+       /* Descriptors. */
+       bus_dma_tag_t                   dma_tag;
+       bus_dmamap_t                    dma_map;
+       void                            *descs;
+       xdma_descriptor_t               *descs_phys;
+       uint8_t                         map_err;
+
+       struct mtx                      mtx_lock;
+
+       TAILQ_ENTRY(xdma_channel)       xchan_next;
+};
+
+typedef struct xdma_channel xdma_channel_t;
+
+/* xDMA controller alloc/free */
+xdma_controller_t *xdma_ofw_get(device_t dev, const char *prop);
+int xdma_put(xdma_controller_t *xdma);
+
+xdma_channel_t * xdma_channel_alloc(xdma_controller_t *);
+int xdma_channel_free(xdma_channel_t *);
+
+int xdma_prep_cyclic(xdma_channel_t *, enum xdma_direction,
+    uintptr_t, uintptr_t, int, int, int, int);
+int xdma_prep_memcpy(xdma_channel_t *, uintptr_t, uintptr_t, size_t len);
+int xdma_desc_alloc(xdma_channel_t *, uint32_t, uint32_t);
+int xdma_desc_free(xdma_channel_t *xchan);
+
+/* Channel Control */
+int xdma_begin(xdma_channel_t *xchan);
+int xdma_pause(xdma_channel_t *xchan);
+int xdma_terminate(xdma_channel_t *xchan);
+
+/* Interrupt callback */
+int xdma_setup_intr(xdma_channel_t *xchan, int (*cb)(void *), void *arg, void 
**);
+int xdma_teardown_intr(xdma_channel_t *xchan, struct xdma_intr_handler *ih);
+int xdma_teardown_all_intr(xdma_channel_t *xchan);
+int xdma_callback(struct xdma_channel *xchan);
+void xdma_assert_locked(void);
+
+struct xdma_intr_handler {
+       int                             (*cb)(void *);
+       void                            *cb_user;
+       struct mtx                      ih_lock;
+       TAILQ_ENTRY(xdma_intr_handler)  ih_next;
+};
+
+#endif /* !_DEV_EXTRES_XDMA_H_ */

Added: head/sys/dev/xdma/xdma_fdt_test.c
==============================================================================
--- /dev/null   00:00:00 1970   (empty, because file is newly added)
+++ head/sys/dev/xdma/xdma_fdt_test.c   Tue Dec 20 18:02:07 2016        
(r310330)
@@ -0,0 +1,417 @@
+/*-
+ * Copyright (c) 2016 Ruslan Bukin <b...@bsdpad.com>
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract FA8750-10-C-0237
+ * ("CTSRD"), as part of the DARPA CRASH research programme.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ */
+
+/* xDMA memcpy test driver. */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/kernel.h>
+#include <sys/kthread.h>
+#include <sys/module.h>
+#include <sys/lock.h>
+#include <sys/mutex.h>
+#include <sys/resource.h>
+#include <sys/rman.h>
+
+#include <machine/bus.h>
+
+#include <dev/xdma/xdma.h>
+
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+
+/*
+ * To use this test add a compatible node to your dts, e.g.
+ *
+ *     xdma_test {
+ *             compatible = "freebsd,xdma-test";
+ *
+ *             dmas = <&dma 0 0 0xffffffff>;
+ *             dma-names = "test";
+ *     };
+ */
+
+struct xdmatest_softc {
+       device_t                dev;
+       xdma_controller_t       *xdma;
+       xdma_channel_t          *xchan;
+       void                    *ih;
+       struct intr_config_hook config_intrhook;
+       char                    *src;
+       char                    *dst;
+       uint32_t                len;
+       uintptr_t               src_phys;
+       uintptr_t               dst_phys;
+       bus_dma_tag_t           src_dma_tag;

*** DIFF OUTPUT TRUNCATED AT 1000 LINES ***
_______________________________________________
svn-src-all@freebsd.org mailing list
https://lists.freebsd.org/mailman/listinfo/svn-src-all
To unsubscribe, send any mail to "svn-src-all-unsubscr...@freebsd.org"

Reply via email to