svn commit: r332472 - in head/sys: conf dev/altera/atse dev/altera/softdma

Ruslan Bukin br at FreeBSD.org
Fri Apr 13 14:18:05 UTC 2018


Author: br
Date: Fri Apr 13 14:18:04 2018
New Revision: 332472
URL: https://svnweb.freebsd.org/changeset/base/332472

Log:
  Add driver for Altera SoftDMA® device.
  
  SoftDMA is a software implementation of DMA engine built using Altera
  FIFO component.
  
  Sponsored by:	DARPA, AFRL
  Differential Revision:	https://reviews.freebsd.org/D9620

Added:
  head/sys/dev/altera/softdma/
  head/sys/dev/altera/softdma/a_api.h
     - copied unchanged from r332471, head/sys/dev/altera/atse/a_api.h
  head/sys/dev/altera/softdma/softdma.c   (contents, props changed)
Deleted:
  head/sys/dev/altera/atse/a_api.h
Modified:
  head/sys/conf/files
  head/sys/dev/altera/atse/if_atse.c

Modified: head/sys/conf/files
==============================================================================
--- head/sys/conf/files	Fri Apr 13 13:31:20 2018	(r332471)
+++ head/sys/conf/files	Fri Apr 13 14:18:04 2018	(r332472)
@@ -837,6 +837,7 @@ dev/altera/sdcard/altera_sdcard_disk.c	optional altera
 dev/altera/sdcard/altera_sdcard_io.c	optional altera_sdcard
 dev/altera/sdcard/altera_sdcard_fdt.c	optional altera_sdcard fdt
 dev/altera/sdcard/altera_sdcard_nexus.c	optional altera_sdcard
+dev/altera/softdma/softdma.c	optional altera_softdma xdma fdt
 dev/altera/pio/pio.c		optional altera_pio
 dev/altera/pio/pio_if.m		optional altera_pio
 dev/amdpm/amdpm.c		optional amdpm pci | nfpm pci

Modified: head/sys/dev/altera/atse/if_atse.c
==============================================================================
--- head/sys/dev/altera/atse/if_atse.c	Fri Apr 13 13:31:20 2018	(r332471)
+++ head/sys/dev/altera/atse/if_atse.c	Fri Apr 13 14:18:04 2018	(r332472)
@@ -88,7 +88,7 @@ __FBSDID("$FreeBSD$");
 #include <dev/mii/miivar.h>
 
 #include <dev/altera/atse/if_atsereg.h>
-#include <dev/altera/atse/a_api.h>
+#include <dev/altera/softdma/a_api.h>
 
 MODULE_DEPEND(atse, ether, 1, 1, 1);
 MODULE_DEPEND(atse, miibus, 1, 1, 1);

Copied: head/sys/dev/altera/softdma/a_api.h (from r332471, head/sys/dev/altera/atse/a_api.h)
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/altera/softdma/a_api.h	Fri Apr 13 14:18:04 2018	(r332472, copy of r332471, head/sys/dev/altera/atse/a_api.h)
@@ -0,0 +1,100 @@
+/*-
+ * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
+ *
+ * Copyright (c) 2012 Bjoern A. Zeeb
+ * All rights reserved.
+ *
+ * This software was developed by SRI International and the University of
+ * Cambridge Computer Laboratory under DARPA/AFRL contract (FA8750-11-C-0249)
+ * ("MRC2"), as part of the DARPA MRC 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$
+ */
+/*
+ * Altera, Embedded Peripherals IP, User Guide, v. 11.0, June 2011.
+ * UG-01085-11.0.
+ */
+
+#ifndef _A_API_H
+#define _A_API_H
+
+/* Table 16-1. Memory Map. */
+#define	A_ONCHIP_FIFO_MEM_CORE_DATA		0x00
+#define	A_ONCHIP_FIFO_MEM_CORE_METADATA		0x04
+
+#define	A_ONCHIP_FIFO_MEM_CORE_SOP		(1<<0)
+#define	A_ONCHIP_FIFO_MEM_CORE_EOP		(1<<1)
+#define	A_ONCHIP_FIFO_MEM_CORE_EMPTY_MASK	0x000000f7
+#define	A_ONCHIP_FIFO_MEM_CORE_EMPTY_SHIFT	2
+	/* Reserved				(1<<7)	   */
+#define	A_ONCHIP_FIFO_MEM_CORE_CHANNEL_MASK	0x0000ff00
+#define	A_ONCHIP_FIFO_MEM_CORE_CHANNEL_SHIFT	8
+#define	A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK	0x00ff0000
+#define	A_ONCHIP_FIFO_MEM_CORE_ERROR_SHIFT	16
+	/* Reserved				0xff000000 */
+
+/* Table 16-3. FIFO Status Register Memory Map. */
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_FILL_LEVEL	0x00
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_I_STATUS	0x04
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT		0x08
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_INT_ENABLE	0x0c
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_ALMOSTFULL	0x10
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_ALMOSTEMPTY	0x14
+
+/* Table 16-5. Status Bit Field Descriptions. */
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_FULL		(1<<0)
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_EMPTY		(1<<1)
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_ALMOSTFULL	(1<<2)
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_ALMOSTEMPTY	(1<<3)
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_OVERFLOW		(1<<4)
+#define	A_ONCHIP_FIFO_MEM_CORE_STATUS_UNDERFLOW		(1<<5)
+
+/* Table 16-6. Event Bit Field Descriptions. */
+/* XXX Datasheet has incorrect bit fields. Validate. */
+#define	A_ONCHIP_FIFO_MEM_CORE_EVENT_FULL		(1<<0)
+#define	A_ONCHIP_FIFO_MEM_CORE_EVENT_EMPTY		(1<<1)
+#define	A_ONCHIP_FIFO_MEM_CORE_EVENT_ALMOSTFULL		(1<<2)
+#define	A_ONCHIP_FIFO_MEM_CORE_EVENT_ALMOSTEMPTY	(1<<3)
+#define	A_ONCHIP_FIFO_MEM_CORE_EVENT_OVERFLOW		(1<<4)
+#define	A_ONCHIP_FIFO_MEM_CORE_EVENT_UNDERFLOW		(1<<5)
+
+/* Table 16-7. InterruptEnable Bit Field Descriptions. */
+/* XXX Datasheet has incorrect bit fields. Validate. */
+#define	A_ONCHIP_FIFO_MEM_CORE_INTR_FULL		(1<<0)
+#define	A_ONCHIP_FIFO_MEM_CORE_INTR_EMPTY		(1<<1)
+#define	A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTFULL		(1<<2)
+#define	A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTEMPTY		(1<<3)
+#define	A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW		(1<<4)
+#define	A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW		(1<<5)
+#define	A_ONCHIP_FIFO_MEM_CORE_INTR_ALL			\
+	    (A_ONCHIP_FIFO_MEM_CORE_INTR_EMPTY|		\
+	    A_ONCHIP_FIFO_MEM_CORE_INTR_FULL|		\
+	    A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTEMPTY|	\
+	    A_ONCHIP_FIFO_MEM_CORE_INTR_ALMOSTFULL|	\
+	    A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW|	\
+	    A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW)
+
+#endif /* _A_API_H */
+
+/* end */

Added: head/sys/dev/altera/softdma/softdma.c
==============================================================================
--- /dev/null	00:00:00 1970	(empty, because file is newly added)
+++ head/sys/dev/altera/softdma/softdma.c	Fri Apr 13 14:18:04 2018	(r332472)
@@ -0,0 +1,864 @@
+/*-
+ * Copyright (c) 2017-2018 Ruslan Bukin <br at 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.
+ */
+
+/* This is driver for SoftDMA device built using Altera FIFO component. */
+
+#include <sys/cdefs.h>
+__FBSDID("$FreeBSD$");
+
+#include "opt_platform.h"
+#include <sys/param.h>
+#include <sys/systm.h>
+#include <sys/conf.h>
+#include <sys/bus.h>
+#include <sys/endian.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>
+
+#ifdef FDT
+#include <dev/fdt/fdt_common.h>
+#include <dev/ofw/ofw_bus.h>
+#include <dev/ofw/ofw_bus_subr.h>
+#endif
+
+#include <dev/altera/softdma/a_api.h>
+
+#include <dev/xdma/xdma.h>
+#include "xdma_if.h"
+
+#define SOFTDMA_DEBUG
+#undef SOFTDMA_DEBUG
+
+#ifdef SOFTDMA_DEBUG
+#define dprintf(fmt, ...)  printf(fmt, ##__VA_ARGS__)
+#else
+#define dprintf(fmt, ...)
+#endif
+
+#define	AVALON_FIFO_TX_BASIC_OPTS_DEPTH		16
+#define	SOFTDMA_NCHANNELS			1
+#define	CONTROL_GEN_SOP				(1 << 0)
+#define	CONTROL_GEN_EOP				(1 << 1)
+#define	CONTROL_OWN				(1 << 31)
+
+#define	SOFTDMA_RX_EVENTS	\
+	(A_ONCHIP_FIFO_MEM_CORE_INTR_FULL	| \
+	 A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW	| \
+	 A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW)
+#define	SOFTDMA_TX_EVENTS	\
+	(A_ONCHIP_FIFO_MEM_CORE_INTR_EMPTY	| \
+ 	A_ONCHIP_FIFO_MEM_CORE_INTR_OVERFLOW	| \
+ 	A_ONCHIP_FIFO_MEM_CORE_INTR_UNDERFLOW)
+
+struct softdma_channel {
+	struct softdma_softc	*sc;
+	struct mtx		mtx;
+	xdma_channel_t		*xchan;
+	struct proc		*p;
+	int			used;
+	int			index;
+	int			run;
+	uint32_t		idx_tail;
+	uint32_t		idx_head;
+	struct softdma_desc	*descs;
+
+	uint32_t		descs_num;
+	uint32_t		descs_used_count;
+};
+
+struct softdma_desc {
+	uint64_t		src_addr;
+	uint64_t		dst_addr;
+	uint32_t		len;
+	uint32_t		access_width;
+	uint32_t		count;
+	uint16_t		src_incr;
+	uint16_t		dst_incr;
+	uint32_t		direction;
+	struct softdma_desc	*next;
+	uint32_t		transfered;
+	uint32_t		status;
+	uint32_t		reserved;
+	uint32_t		control;
+};
+
+struct softdma_softc {
+	device_t		dev;
+	struct resource		*res[3];
+	bus_space_tag_t		bst;
+	bus_space_handle_t	bsh;
+	bus_space_tag_t		bst_c;
+	bus_space_handle_t	bsh_c;
+	void			*ih;
+	struct softdma_channel	channels[SOFTDMA_NCHANNELS];
+};
+
+static struct resource_spec softdma_spec[] = {
+	{ SYS_RES_MEMORY,	0,	RF_ACTIVE },	/* fifo */
+	{ SYS_RES_MEMORY,	1,	RF_ACTIVE },	/* core */
+	{ SYS_RES_IRQ,		0,	RF_ACTIVE },
+	{ -1, 0 }
+};
+
+static int softdma_probe(device_t dev);
+static int softdma_attach(device_t dev);
+static int softdma_detach(device_t dev);
+
+static inline uint32_t
+softdma_next_desc(struct softdma_channel *chan, uint32_t curidx)
+{
+
+	return ((curidx + 1) % chan->descs_num);
+}
+
+static void
+softdma_mem_write(struct softdma_softc *sc, uint32_t reg, uint32_t val)
+{
+
+	bus_write_4(sc->res[0], reg, htole32(val));
+}
+
+static uint32_t
+softdma_mem_read(struct softdma_softc *sc, uint32_t reg)
+{
+	uint32_t val;
+
+	val = bus_read_4(sc->res[0], reg);
+
+	return (le32toh(val));
+}
+
+static void
+softdma_memc_write(struct softdma_softc *sc, uint32_t reg, uint32_t val)
+{
+
+	bus_write_4(sc->res[1], reg, htole32(val));
+}
+
+static uint32_t
+softdma_memc_read(struct softdma_softc *sc, uint32_t reg)
+{
+	uint32_t val;
+
+	val = bus_read_4(sc->res[1], reg);
+
+	return (le32toh(val));
+}
+
+static uint32_t
+softdma_fill_level(struct softdma_softc *sc)
+{
+	uint32_t val;
+
+	val = softdma_memc_read(sc,
+	    A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_FILL_LEVEL);
+
+	return (val);
+}
+
+static void
+softdma_intr(void *arg)
+{
+	struct softdma_channel *chan;
+	struct softdma_softc *sc;
+	int reg;
+	int err;
+
+	sc = arg;
+
+	chan = &sc->channels[0];
+
+	reg = softdma_memc_read(sc, A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT);
+
+	if (reg & (A_ONCHIP_FIFO_MEM_CORE_EVENT_OVERFLOW | 
+	    A_ONCHIP_FIFO_MEM_CORE_EVENT_UNDERFLOW)) {
+		/* Errors */
+		err = (((reg & A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK) >> \
+		    A_ONCHIP_FIFO_MEM_CORE_ERROR_SHIFT) & 0xff);
+	}
+
+	if (reg != 0) {
+		softdma_memc_write(sc,
+		    A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_EVENT, reg);
+		chan->run = 1;
+		wakeup(chan);
+	}
+}
+
+static int
+softdma_probe(device_t dev)
+{
+
+	if (!ofw_bus_status_okay(dev))
+		return (ENXIO);
+
+	if (!ofw_bus_is_compatible(dev, "altr,softdma"))
+		return (ENXIO);
+
+	device_set_desc(dev, "SoftDMA");
+
+	return (BUS_PROBE_DEFAULT);
+}
+
+static int
+softdma_attach(device_t dev)
+{
+	struct softdma_softc *sc;
+	phandle_t xref, node;
+	int err;
+
+	sc = device_get_softc(dev);
+	sc->dev = dev;
+
+	if (bus_alloc_resources(dev, softdma_spec, sc->res)) {
+		device_printf(dev,
+		    "could not allocate resources for device\n");
+		return (ENXIO);
+	}
+
+	/* FIFO memory interface */
+	sc->bst = rman_get_bustag(sc->res[0]);
+	sc->bsh = rman_get_bushandle(sc->res[0]);
+
+	/* FIFO control memory interface */
+	sc->bst_c = rman_get_bustag(sc->res[1]);
+	sc->bsh_c = rman_get_bushandle(sc->res[1]);
+
+	/* Setup interrupt handler */
+	err = bus_setup_intr(dev, sc->res[2], INTR_TYPE_MISC | INTR_MPSAFE,
+	    NULL, softdma_intr, sc, &sc->ih);
+	if (err) {
+		device_printf(dev, "Unable to alloc interrupt resource.\n");
+		return (ENXIO);
+	}
+
+	node = ofw_bus_get_node(dev);
+	xref = OF_xref_from_node(node);
+	OF_device_register_xref(xref, dev);
+
+	return (0);
+}
+
+static int
+softdma_detach(device_t dev)
+{
+	struct softdma_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	return (0);
+}
+
+static int
+softdma_process_tx(struct softdma_channel *chan, struct softdma_desc *desc)
+{
+	struct softdma_softc *sc;
+	uint32_t src_offs, dst_offs;
+	uint32_t reg;
+	uint32_t fill_level;
+	uint32_t leftm;
+	uint32_t tmp;
+	uint32_t val;
+	uint32_t c;
+
+	sc = chan->sc;
+
+	fill_level = softdma_fill_level(sc);
+	while (fill_level == AVALON_FIFO_TX_BASIC_OPTS_DEPTH)
+		fill_level = softdma_fill_level(sc);
+
+	/* Set start of packet. */
+	if (desc->control & CONTROL_GEN_SOP) {
+		reg = 0;
+		reg |= A_ONCHIP_FIFO_MEM_CORE_SOP;
+		softdma_mem_write(sc, A_ONCHIP_FIFO_MEM_CORE_METADATA, reg);
+	}
+
+	src_offs = dst_offs = 0;
+	c = 0;
+	while ((desc->len - c) >= 4) {
+		val = *(uint32_t *)(desc->src_addr + src_offs);
+		bus_write_4(sc->res[0], A_ONCHIP_FIFO_MEM_CORE_DATA, val);
+		if (desc->src_incr)
+			src_offs += 4;
+		if (desc->dst_incr)
+			dst_offs += 4;
+		fill_level += 1;
+
+		while (fill_level == AVALON_FIFO_TX_BASIC_OPTS_DEPTH) {
+			fill_level = softdma_fill_level(sc);
+		}
+		c += 4;
+	}
+
+	val = 0;
+	leftm = (desc->len - c);
+
+	switch (leftm) {
+	case 1:
+		val = *(uint8_t *)(desc->src_addr + src_offs);
+		val <<= 24;
+		src_offs += 1;
+		break;
+	case 2:
+	case 3:
+		val = *(uint16_t *)(desc->src_addr + src_offs);
+		val <<= 16;
+		src_offs += 2;
+
+		if (leftm == 3) {
+			tmp = *(uint8_t *)(desc->src_addr + src_offs);
+			val |= (tmp << 8);
+			src_offs += 1;
+		}
+		break;
+	case 0:
+	default:
+		break;
+	}
+
+	/* Set end of packet. */
+	reg = 0;
+	if (desc->control & CONTROL_GEN_EOP)
+		reg |= A_ONCHIP_FIFO_MEM_CORE_EOP;
+	reg |= ((4 - leftm) << A_ONCHIP_FIFO_MEM_CORE_EMPTY_SHIFT);
+	softdma_mem_write(sc, A_ONCHIP_FIFO_MEM_CORE_METADATA, reg);
+
+	/* Ensure there is a FIFO entry available. */
+	fill_level = softdma_fill_level(sc);
+	while (fill_level == AVALON_FIFO_TX_BASIC_OPTS_DEPTH)
+		fill_level = softdma_fill_level(sc);
+
+	/* Final write */
+	bus_write_4(sc->res[0], A_ONCHIP_FIFO_MEM_CORE_DATA, val);
+
+	return (dst_offs);
+}
+
+static int
+softdma_process_rx(struct softdma_channel *chan, struct softdma_desc *desc)
+{
+	uint32_t src_offs, dst_offs;
+	struct softdma_softc *sc;
+	uint32_t fill_level;
+	uint32_t empty;
+	uint32_t meta;
+	uint32_t data;
+	int sop_rcvd;
+	int timeout;
+	size_t len;
+	int error;
+
+	sc = chan->sc;
+	empty = 0;
+	src_offs = dst_offs = 0;
+	error = 0;
+
+	fill_level = softdma_fill_level(sc);
+	if (fill_level == 0) {
+		/* Nothing to receive. */
+		return (0);
+	}
+
+	len = desc->len;
+
+	sop_rcvd = 0;
+	while (fill_level) {
+		empty = 0;
+		data = bus_read_4(sc->res[0], A_ONCHIP_FIFO_MEM_CORE_DATA);
+		meta = softdma_mem_read(sc, A_ONCHIP_FIFO_MEM_CORE_METADATA);
+
+		if (meta & A_ONCHIP_FIFO_MEM_CORE_ERROR_MASK) {
+			error = 1;
+			break;
+		}
+
+		if ((meta & A_ONCHIP_FIFO_MEM_CORE_CHANNEL_MASK) != 0) {
+			error = 1;
+			break;
+		}
+
+		if (meta & A_ONCHIP_FIFO_MEM_CORE_SOP) {
+			sop_rcvd = 1;
+		}
+
+		if (meta & A_ONCHIP_FIFO_MEM_CORE_EOP) {
+			empty = (meta & A_ONCHIP_FIFO_MEM_CORE_EMPTY_MASK) >>
+			    A_ONCHIP_FIFO_MEM_CORE_EMPTY_SHIFT;
+		}
+
+		if (sop_rcvd == 0) {
+			error = 1;
+			break;
+		}
+
+		if (empty == 0) {
+			*(uint32_t *)(desc->dst_addr + dst_offs) = data;
+			dst_offs += 4;
+		} else if (empty == 1) {
+			*(uint16_t *)(desc->dst_addr + dst_offs) =
+			    ((data >> 16) & 0xffff);
+			dst_offs += 2;
+
+			*(uint8_t *)(desc->dst_addr + dst_offs) =
+			    ((data >> 8) & 0xff);
+			dst_offs += 1;
+		} else {
+			panic("empty %d\n", empty);
+		}
+
+		if (meta & A_ONCHIP_FIFO_MEM_CORE_EOP)
+			break;
+
+		fill_level = softdma_fill_level(sc);
+		timeout = 100;
+		while (fill_level == 0 && timeout--)
+			fill_level = softdma_fill_level(sc);
+		if (timeout == 0) {
+			/* No EOP received. Broken packet. */
+			error = 1;
+			break;
+		}
+	}
+
+	if (error) {
+		return (-1);
+	}
+
+	return (dst_offs);
+}
+
+static uint32_t
+softdma_process_descriptors(struct softdma_channel *chan,
+    xdma_transfer_status_t *status)
+{
+	struct xdma_channel *xchan;
+	struct softdma_desc *desc;
+	struct softdma_softc *sc;
+	xdma_transfer_status_t st;
+	int ret;
+
+	sc = chan->sc;
+
+	xchan = chan->xchan;
+
+	desc = &chan->descs[chan->idx_tail];
+
+	while (desc != NULL) {
+
+		if ((desc->control & CONTROL_OWN) == 0) {
+			break;
+		}
+
+		if (desc->direction == XDMA_MEM_TO_DEV) {
+			ret = softdma_process_tx(chan, desc);
+		} else {
+			ret = softdma_process_rx(chan, desc);
+			if (ret == 0) {
+				/* No new data available. */
+				break;
+			}
+		}
+
+		/* Descriptor processed. */
+		desc->control = 0;
+
+		if (ret >= 0) {
+			st.error = 0;
+			st.transferred = ret;
+		} else {
+			st.error = ret;
+			st.transferred = 0;
+		}
+
+		xchan_seg_done(xchan, &st);
+		atomic_subtract_int(&chan->descs_used_count, 1);
+
+		if (ret >= 0) {
+			status->transferred += ret;
+		} else {
+			status->error = 1;
+			break;
+		}
+
+		chan->idx_tail = softdma_next_desc(chan, chan->idx_tail);
+
+		/* Process next descriptor, if any. */
+		desc = desc->next;
+	}
+
+	return (0);
+}
+
+static void
+softdma_worker(void *arg)
+{
+	xdma_transfer_status_t status;
+	struct softdma_channel *chan;
+	struct softdma_softc *sc;
+
+	chan = arg;
+
+	sc = chan->sc;
+
+	while (1) {
+		mtx_lock(&chan->mtx);
+
+		do {
+			mtx_sleep(chan, &chan->mtx, 0, "softdma_wait", hz / 2);
+		} while (chan->run == 0);
+
+		status.error = 0;
+		status.transferred = 0;
+
+		softdma_process_descriptors(chan, &status);
+
+		/* Finish operation */
+		chan->run = 0;
+		xdma_callback(chan->xchan, &status);
+
+		mtx_unlock(&chan->mtx);
+	}
+
+}
+
+static int
+softdma_proc_create(struct softdma_channel *chan)
+{
+	struct softdma_softc *sc;
+
+	sc = chan->sc;
+
+	if (chan->p != NULL) {
+		/* Already created */
+		return (0);
+	}
+
+	mtx_init(&chan->mtx, "SoftDMA", NULL, MTX_DEF);
+
+	if (kproc_create(softdma_worker, (void *)chan, &chan->p, 0, 0,
+	    "softdma_worker") != 0) {
+		device_printf(sc->dev,
+		    "%s: Failed to create worker thread.\n", __func__);
+		return (-1);
+	}
+
+	return (0);
+}
+
+static int
+softdma_channel_alloc(device_t dev, struct xdma_channel *xchan)
+{
+	struct softdma_channel *chan;
+	struct softdma_softc *sc;
+	int i;
+
+	sc = device_get_softc(dev);
+
+	for (i = 0; i < SOFTDMA_NCHANNELS; i++) {
+		chan = &sc->channels[i];
+		if (chan->used == 0) {
+			chan->xchan = xchan;
+			xchan->chan = (void *)chan;
+			chan->index = i;
+			chan->idx_head = 0;
+			chan->idx_tail = 0;
+			chan->descs_used_count = 0;
+			chan->descs_num = 1024;
+			chan->sc = sc;
+
+			if (softdma_proc_create(chan) != 0) {
+				return (-1);
+			}
+
+			chan->used = 1;
+
+			return (0);
+		}
+	}
+
+	return (-1);
+}
+
+static int
+softdma_channel_free(device_t dev, struct xdma_channel *xchan)
+{
+	struct softdma_channel *chan;
+	struct softdma_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	chan = (struct softdma_channel *)xchan->chan;
+
+	if (chan->descs != NULL) {
+		free(chan->descs, M_DEVBUF);
+	}
+
+	chan->used = 0;
+
+	return (0);
+}
+
+static int
+softdma_desc_alloc(struct xdma_channel *xchan)
+{
+	struct softdma_channel *chan;
+	uint32_t nsegments;
+
+	chan = (struct softdma_channel *)xchan->chan;
+
+	nsegments = chan->descs_num;
+
+	chan->descs = malloc(nsegments * sizeof(struct softdma_desc),
+	    M_DEVBUF, (M_WAITOK | M_ZERO));
+
+	return (0);
+}
+
+static int
+softdma_channel_prep_sg(device_t dev, struct xdma_channel *xchan)
+{
+	struct softdma_channel *chan;
+	struct softdma_desc *desc;
+	struct softdma_softc *sc;
+	int ret;
+	int i;
+
+	sc = device_get_softc(dev);
+
+	chan = (struct softdma_channel *)xchan->chan;
+
+	ret = softdma_desc_alloc(xchan);
+	if (ret != 0) {
+		device_printf(sc->dev,
+		    "%s: Can't allocate descriptors.\n", __func__);
+		return (-1);
+	}
+
+	for (i = 0; i < chan->descs_num; i++) {
+		desc = &chan->descs[i];
+
+		if (i == (chan->descs_num - 1)) {
+			desc->next = &chan->descs[0];
+		} else {
+			desc->next = &chan->descs[i+1];
+		}
+	}
+
+	return (0);
+}
+
+static int
+softdma_channel_capacity(device_t dev, xdma_channel_t *xchan,
+    uint32_t *capacity)
+{
+	struct softdma_channel *chan;
+	uint32_t c;
+
+	chan = (struct softdma_channel *)xchan->chan;
+
+	/* At least one descriptor must be left empty. */
+	c = (chan->descs_num - chan->descs_used_count - 1);
+
+	*capacity = c;
+
+	return (0);
+}
+
+static int
+softdma_channel_submit_sg(device_t dev, struct xdma_channel *xchan,
+    struct xdma_sglist *sg, uint32_t sg_n)
+{
+	struct softdma_channel *chan;
+	struct softdma_desc *desc;
+	struct softdma_softc *sc;
+	uint32_t enqueued;
+	uint32_t saved_dir;
+	uint32_t tmp;
+	uint32_t len;
+	int i;
+
+	sc = device_get_softc(dev);
+
+	chan = (struct softdma_channel *)xchan->chan;
+
+	enqueued = 0;
+
+	for (i = 0; i < sg_n; i++) {
+		len = (uint32_t)sg[i].len;
+
+		desc = &chan->descs[chan->idx_head];
+		desc->src_addr = sg[i].src_addr;
+		desc->dst_addr = sg[i].dst_addr;
+		if (sg[i].direction == XDMA_MEM_TO_DEV) {
+			desc->src_incr = 1;
+			desc->dst_incr = 0;
+		} else {
+			desc->src_incr = 0;
+			desc->dst_incr = 1;
+		}
+		desc->direction = sg[i].direction;
+		saved_dir = sg[i].direction;
+		desc->len = len;
+		desc->transfered = 0;
+		desc->status = 0;
+		desc->reserved = 0;
+		desc->control = 0;
+
+		if (sg[i].first == 1)
+			desc->control |= CONTROL_GEN_SOP;
+		if (sg[i].last == 1)
+			desc->control |= CONTROL_GEN_EOP;
+
+		tmp = chan->idx_head;
+		chan->idx_head = softdma_next_desc(chan, chan->idx_head);
+		atomic_add_int(&chan->descs_used_count, 1);
+		desc->control |= CONTROL_OWN;
+		enqueued += 1;
+	}
+
+	if (enqueued == 0)
+		return (0);
+
+	if (saved_dir == XDMA_MEM_TO_DEV) {
+		chan->run = 1;
+		wakeup(chan);
+	} else
+		softdma_memc_write(sc,
+		    A_ONCHIP_FIFO_MEM_CORE_STATUS_REG_INT_ENABLE,
+		    SOFTDMA_RX_EVENTS);
+
+	return (0);
+}
+
+static int
+softdma_channel_request(device_t dev, struct xdma_channel *xchan,
+    struct xdma_request *req)
+{
+	struct softdma_channel *chan;
+	struct softdma_desc *desc;
+	struct softdma_softc *sc;
+	int ret;
+
+	sc = device_get_softc(dev);
+
+	chan = (struct softdma_channel *)xchan->chan;
+
+	ret = softdma_desc_alloc(xchan);
+	if (ret != 0) {
+		device_printf(sc->dev,
+		    "%s: Can't allocate descriptors.\n", __func__);
+		return (-1);
+	}
+
+	desc = &chan->descs[0];
+
+	desc->src_addr = req->src_addr;
+	desc->dst_addr = req->dst_addr;
+	desc->len = req->block_len;
+	desc->src_incr = 1;
+	desc->dst_incr = 1;
+	desc->next = NULL;
+
+	return (0);
+}
+
+static int
+softdma_channel_control(device_t dev, xdma_channel_t *xchan, int cmd)
+{
+	struct softdma_channel *chan;
+	struct softdma_softc *sc;
+
+	sc = device_get_softc(dev);
+
+	chan = (struct softdma_channel *)xchan->chan;
+
+	switch (cmd) {
+	case XDMA_CMD_BEGIN:
+	case XDMA_CMD_TERMINATE:
+	case XDMA_CMD_PAUSE:
+		/* TODO: implement me */
+		return (-1);
+	}
+
+	return (0);
+}
+
+#ifdef FDT
+static int
+softdma_ofw_md_data(device_t dev, pcell_t *cells,
+    int ncells, void **ptr)
+{
+
+	return (0);
+}
+#endif
+
+static device_method_t softdma_methods[] = {
+	/* Device interface */
+	DEVMETHOD(device_probe,			softdma_probe),
+	DEVMETHOD(device_attach,		softdma_attach),
+	DEVMETHOD(device_detach,		softdma_detach),
+
+	/* xDMA Interface */
+	DEVMETHOD(xdma_channel_alloc,		softdma_channel_alloc),
+	DEVMETHOD(xdma_channel_free,		softdma_channel_free),
+	DEVMETHOD(xdma_channel_request,		softdma_channel_request),
+	DEVMETHOD(xdma_channel_control,		softdma_channel_control),
+
+	/* xDMA SG Interface */
+	DEVMETHOD(xdma_channel_prep_sg,		softdma_channel_prep_sg),
+	DEVMETHOD(xdma_channel_submit_sg,	softdma_channel_submit_sg),
+	DEVMETHOD(xdma_channel_capacity,	softdma_channel_capacity),
+
+#ifdef FDT
+	DEVMETHOD(xdma_ofw_md_data,		softdma_ofw_md_data),
+#endif
+
+	DEVMETHOD_END
+};
+
+static driver_t softdma_driver = {
+	"softdma",
+	softdma_methods,
+	sizeof(struct softdma_softc),
+};
+
+static devclass_t softdma_devclass;
+
+EARLY_DRIVER_MODULE(softdma, simplebus, softdma_driver, softdma_devclass, 0, 0,
+    BUS_PASS_INTERRUPT + BUS_PASS_ORDER_LATE);


More information about the svn-src-all mailing list