Page Menu
Home
FreeBSD
Search
Configure Global Search
Log In
Files
F107377127
D9617.diff
No One
Temporary
Actions
View File
Edit File
Delete File
View Transforms
Subscribe
Mute Notifications
Flag For Later
Award Token
Size
44 KB
Referenced Files
None
Subscribers
None
D9617.diff
View Options
Index: sys/dev/xdma/xdma.h
===================================================================
--- sys/dev/xdma/xdma.h
+++ sys/dev/xdma/xdma.h
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2016 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2016-2017 Ruslan Bukin <br@bsdpad.com>
* All rights reserved.
*
* This software was developed by SRI International and the University of
@@ -30,8 +30,10 @@
* $FreeBSD$
*/
-#ifndef _DEV_EXTRES_XDMA_H_
-#define _DEV_EXTRES_XDMA_H_
+#ifndef _DEV_XDMA_H_
+#define _DEV_XDMA_H_
+
+#include <sys/proc.h>
enum xdma_direction {
XDMA_MEM_TO_MEM,
@@ -46,6 +48,12 @@
XDMA_CYCLIC,
};
+enum xdma_request_type {
+ XR_TYPE_ADDR,
+ XR_TYPE_MBUF,
+ XR_TYPE_BIO,
+};
+
enum xdma_command {
XDMA_CMD_BEGIN,
XDMA_CMD_PAUSE,
@@ -53,10 +61,19 @@
XDMA_CMD_TERMINATE_ALL,
};
+struct xdma_transfer_status {
+ uint32_t transferred;
+ int error;
+};
+
+typedef struct xdma_transfer_status xdma_transfer_status_t;
+
struct xdma_controller {
device_t dev; /* DMA consumer device_t. */
device_t dma_dev; /* A real DMA device_t. */
void *data; /* OFW MD part. */
+ struct proc *xdma_proc;
+ struct mtx proc_mtx;
/* List of virtual channels allocated. */
TAILQ_HEAD(xdma_channel_list, xdma_channel) channels;
@@ -64,6 +81,35 @@
typedef struct xdma_controller xdma_controller_t;
+struct xchan_buf {
+ bus_dmamap_t map;
+ uint32_t nsegs;
+ uint32_t nsegs_left;
+ void *cbuf;
+};
+
+/* SG type of transfer. */
+struct xdma_request {
+ struct mbuf *m;
+ struct bio *bp;
+ enum xdma_request_type type;
+ enum xdma_direction direction;
+ bus_addr_t src_addr; /* Physical address. */
+ bus_addr_t dst_addr; /* Physical address. */
+ uint8_t src_width;
+ uint8_t dst_width;
+ bus_size_t len;
+ xdma_transfer_status_t status;
+ void *user;
+ TAILQ_ENTRY(xdma_request) xr_next;
+ struct xchan_buf buf;
+};
+
+/*
+ * Cyclic/memcpy type of transfer.
+ * Legacy configuration struct
+ * TODO: replace with xdma_request.
+ */
struct xdma_channel_config {
enum xdma_direction direction;
uintptr_t src_addr; /* Physical address. */
@@ -76,55 +122,98 @@
typedef struct xdma_channel_config xdma_config_t;
-struct xdma_descriptor {
- bus_addr_t ds_addr;
- bus_size_t ds_len;
+struct xdma_sglist {
+ bus_addr_t src_addr;
+ bus_addr_t dst_addr;
+ size_t len;
+ uint8_t src_width;
+ uint8_t dst_width;
+ enum xdma_direction direction;
+ bool first;
+ bool last;
};
-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)
+ uint32_t flags;
+#define XCHAN_BUFS_ALLOCATED (1 << 0)
+#define XCHAN_SGLIST_ALLOCATED (1 << 1)
+#define XCHAN_CONFIGURED (1 << 2)
+#define XCHAN_TYPE_CYCLIC (1 << 3)
+#define XCHAN_TYPE_MEMCPY (1 << 4)
+#define XCHAN_TYPE_FIFO (1 << 5)
+#define XCHAN_TYPE_SG (1 << 6)
+
+ uint32_t caps;
+#define XCHAN_CAP_BUSDMA (1 << 0)
+#define XCHAN_CAP_BUSDMA_NOSEG (1 << 1)
/* 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;
+ TAILQ_ENTRY(xdma_channel) xchan_next;
struct mtx mtx_lock;
+ struct mtx mtx_qin_lock;
+ struct mtx mtx_qout_lock;
+ struct mtx mtx_bank_lock;
+ struct mtx mtx_proc_lock;
- TAILQ_ENTRY(xdma_channel) xchan_next;
+ /* Request queue. */
+ bus_dma_tag_t dma_tag_bufs;
+ struct xdma_request *xr_mem;
+ uint32_t xr_num;
+
+ /* Bus dma tag options. */
+ uint32_t maxsegsize;
+ uint32_t maxnsegs;
+ uint32_t alignment;
+
+ struct xdma_sglist *sg;
+
+ TAILQ_HEAD(, xdma_request) bank;
+ TAILQ_HEAD(, xdma_request) queue_in;
+ TAILQ_HEAD(, xdma_request) queue_out;
+ TAILQ_HEAD(, xdma_request) processing;
+ struct mtx mtx_queue;
};
typedef struct xdma_channel xdma_channel_t;
-/* xDMA controller alloc/free */
+/* xDMA controller ops */
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 *);
+/* xDMA channel ops */
+xdma_channel_t * xdma_channel_alloc(xdma_controller_t *, uint32_t caps);
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);
+int xdma_prep_sg(xdma_channel_t *xchan, uint32_t, uint32_t, uint32_t, uint32_t);
+
+int xchan_seg_done(xdma_channel_t *xchan, xdma_transfer_status_t *);
+
+/* xchan queues operations */
+int xdma_dequeue_mbuf(xdma_channel_t *xchan, struct mbuf **m, xdma_transfer_status_t *);
+int xdma_enqueue_mbuf(xdma_channel_t *xchan, struct mbuf **m, uintptr_t addr,
+ uint8_t, uint8_t, enum xdma_direction dir);
+int xdma_dequeue_bio(xdma_channel_t *xchan, struct bio **bp, xdma_transfer_status_t *status);
+int xdma_enqueue_bio(xdma_channel_t *xchan, struct bio **bp, bus_addr_t addr,
+ uint8_t, uint8_t, enum xdma_direction dir);
+int xdma_dequeue(xdma_channel_t *xchan, void **user, xdma_transfer_status_t *status);
+int xdma_enqueue(xdma_channel_t *xchan, uintptr_t src, uintptr_t dst,
+ uint8_t, uint8_t, bus_size_t, enum xdma_direction dir, void *);
+
+int xdma_queue_submit(xdma_channel_t *xchan);
+
+uint32_t xdma_mbuf_defrag(xdma_channel_t *xchan, struct xdma_request *xr);
+uint32_t xdma_mbuf_chain_count(struct mbuf *m0);
/* Channel Control */
int xdma_begin(xdma_channel_t *xchan);
@@ -132,17 +221,45 @@
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_setup_intr(xdma_channel_t *xchan, int (*cb)(void *, xdma_transfer_status_t *), 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);
+int xdma_callback(struct xdma_channel *xchan, xdma_transfer_status_t *status);
+
+int xchan_sglist_init(xdma_channel_t *xchan);
+int xchan_sglist_free(xdma_channel_t *xchan);
+int xdma_sglist_add(struct xdma_sglist *sg, struct bus_dma_segment *seg,
+ uint32_t nsegs, struct xdma_request *xr);
struct xdma_intr_handler {
- int (*cb)(void *);
+ int (*cb)(void *cb_user, xdma_transfer_status_t *status);
void *cb_user;
struct mtx ih_lock;
TAILQ_ENTRY(xdma_intr_handler) ih_next;
};
-#endif /* !_DEV_EXTRES_XDMA_H_ */
+static MALLOC_DEFINE(M_XDMA, "xdma", "xDMA framework");
+
+struct xdma_request * xchan_bank_get(xdma_channel_t *xchan);
+int xchan_bank_put(xdma_channel_t *xchan, struct xdma_request *xr);
+
+#define QUEUE_IN_LOCK(xchan) mtx_lock(&(xchan)->mtx_qin_lock)
+#define QUEUE_IN_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_qin_lock)
+#define QUEUE_IN_ASSERT_LOCKED(xchan) mtx_assert(&(xchan)->mtx_qin_lock, MA_OWNED)
+
+#define QUEUE_OUT_LOCK(xchan) mtx_lock(&(xchan)->mtx_qout_lock)
+#define QUEUE_OUT_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_qout_lock)
+#define QUEUE_OUT_ASSERT_LOCKED(xchan) mtx_assert(&(xchan)->mtx_qout_lock, MA_OWNED)
+
+#define QUEUE_BANK_LOCK(xchan) mtx_lock(&(xchan)->mtx_bank_lock)
+#define QUEUE_BANK_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_bank_lock)
+#define QUEUE_BANK_ASSERT_LOCKED(xchan) mtx_assert(&(xchan)->mtx_bank_lock, MA_OWNED)
+
+#define QUEUE_PROC_LOCK(xchan) mtx_lock(&(xchan)->mtx_proc_lock)
+#define QUEUE_PROC_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_proc_lock)
+#define QUEUE_PROC_ASSERT_LOCKED(xchan) mtx_assert(&(xchan)->mtx_proc_lock, MA_OWNED)
+
+#define XDMA_SGLIST_MAXLEN 2048
+#define XDMA_MAX_SEG 128
+
+#endif /* !_DEV_XDMA_H_ */
Index: sys/dev/xdma/xdma.c
===================================================================
--- sys/dev/xdma/xdma.c
+++ sys/dev/xdma/xdma.c
@@ -1,5 +1,5 @@
/*-
- * Copyright (c) 2016 Ruslan Bukin <br@bsdpad.com>
+ * Copyright (c) 2016-2017 Ruslan Bukin <br@bsdpad.com>
* All rights reserved.
*
* This software was developed by SRI International and the University of
@@ -36,10 +36,12 @@
#include <sys/conf.h>
#include <sys/bus.h>
#include <sys/kernel.h>
+#include <sys/kthread.h>
#include <sys/queue.h>
#include <sys/kobj.h>
#include <sys/malloc.h>
#include <sys/mutex.h>
+#include <sys/mbuf.h>
#include <sys/limits.h>
#include <sys/lock.h>
#include <sys/sysctl.h>
@@ -59,13 +61,12 @@
#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)
@@ -77,11 +78,108 @@
#define XCHAN_UNLOCK(xchan) mtx_unlock(&(xchan)->mtx_lock)
#define XCHAN_ASSERT_LOCKED(xchan) mtx_assert(&(xchan)->mtx_lock, MA_OWNED)
+static int xchan_bufs_alloc(xdma_channel_t *xchan);
+static int xchan_bufs_free(xdma_channel_t *xchan);
+
+struct seg_load_request {
+ struct bus_dma_segment *seg;
+ uint32_t nsegs;
+ uint32_t error;
+};
+
+static void
+xdma_task(void *arg)
+{
+ xdma_controller_t *xdma;
+ xdma_channel_t *xchan_tmp;
+ xdma_channel_t *xchan;
+
+ xdma = arg;
+
+ for (;;) {
+ mtx_lock(&xdma->proc_mtx);
+ msleep(xdma, &xdma->proc_mtx, PRIBIO, "jobqueue", hz);
+ mtx_unlock(&xdma->proc_mtx);
+
+ if (TAILQ_EMPTY(&xdma->channels)) {
+ continue;
+ }
+
+ TAILQ_FOREACH_SAFE(xchan, &xdma->channels, xchan_next, xchan_tmp) {
+ if (xchan->flags & XCHAN_TYPE_SG) {
+ xdma_queue_submit(xchan);
+ }
+ }
+ }
+}
+
+static int
+xchan_bank_init(xdma_channel_t *xchan)
+{
+ struct xdma_request *xr;
+ xdma_controller_t *xdma;
+ int i;
+
+ xdma = xchan->xdma;
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ xchan->xr_mem = malloc(sizeof(struct xdma_request) * xchan->xr_num,
+ M_XDMA, M_WAITOK | M_ZERO);
+ if (xchan->xr_mem == NULL) {
+ device_printf(xdma->dev,
+ "%s: Can't allocate memory.\n", __func__);
+ return (-1);
+ }
+
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ TAILQ_INSERT_TAIL(&xchan->bank, xr, xr_next);
+ }
+
+ return (0);
+}
+
+static int
+xchan_bank_free(xdma_channel_t *xchan)
+{
+
+ free(xchan->xr_mem, M_XDMA);
+
+ return (0);
+}
+
+struct xdma_request *
+xchan_bank_get(xdma_channel_t *xchan)
+{
+ struct xdma_request *xr;
+ struct xdma_request *xr_tmp;
+
+ QUEUE_BANK_LOCK(xchan);
+ TAILQ_FOREACH_SAFE(xr, &xchan->bank, xr_next, xr_tmp) {
+ TAILQ_REMOVE(&xchan->bank, xr, xr_next);
+ break;
+ }
+ QUEUE_BANK_UNLOCK(xchan);
+
+ return (xr);
+}
+
+int
+xchan_bank_put(xdma_channel_t *xchan, struct xdma_request *xr)
+{
+
+ QUEUE_BANK_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->bank, xr, xr_next);
+ QUEUE_BANK_UNLOCK(xchan);
+
+ return (0);
+}
+
/*
* Allocate virtual xDMA channel.
*/
xdma_channel_t *
-xdma_channel_alloc(xdma_controller_t *xdma)
+xdma_channel_alloc(xdma_controller_t *xdma, uint32_t caps)
{
xdma_channel_t *xchan;
int ret;
@@ -93,6 +191,7 @@
return (NULL);
}
xchan->xdma = xdma;
+ xchan->caps = caps;
XDMA_LOCK();
@@ -108,7 +207,12 @@
}
TAILQ_INIT(&xchan->ie_handlers);
+
mtx_init(&xchan->mtx_lock, "xDMA", NULL, MTX_DEF);
+ mtx_init(&xchan->mtx_qin_lock, "xDMA", NULL, MTX_DEF);
+ mtx_init(&xchan->mtx_qout_lock, "xDMA", NULL, MTX_DEF);
+ mtx_init(&xchan->mtx_bank_lock, "xDMA", NULL, MTX_DEF);
+ mtx_init(&xchan->mtx_proc_lock, "xDMA", NULL, MTX_DEF);
TAILQ_INSERT_TAIL(&xdma->channels, xchan, xchan_next);
@@ -138,10 +242,18 @@
xdma_teardown_all_intr(xchan);
- /* Deallocate descriptors, if any. */
- xdma_desc_free(xchan);
+ /* Deallocate bufs, if any. */
+ xchan_bufs_free(xchan);
+ if (xchan->flags & XCHAN_TYPE_SG) {
+ xchan_sglist_free(xchan);
+ xchan_bank_free(xchan);
+ }
mtx_destroy(&xchan->mtx_lock);
+ mtx_destroy(&xchan->mtx_qin_lock);
+ mtx_destroy(&xchan->mtx_qout_lock);
+ mtx_destroy(&xchan->mtx_bank_lock);
+ mtx_destroy(&xchan->mtx_proc_lock);
TAILQ_REMOVE(&xdma->channels, xchan, xchan_next);
@@ -153,8 +265,9 @@
}
int
-xdma_setup_intr(xdma_channel_t *xchan, int (*cb)(void *), void *arg,
- void **ihandler)
+xdma_setup_intr(xdma_channel_t *xchan,
+ int (*cb)(void *, xdma_transfer_status_t *),
+ void *arg, void **ihandler)
{
struct xdma_intr_handler *ih;
xdma_controller_t *xdma;
@@ -232,197 +345,609 @@
return (0);
}
-static void
-xdma_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nseg, int err)
+static int
+xchan_bufs_alloc_no_busdma(xdma_channel_t *xchan)
{
- xdma_channel_t *xchan;
+ xdma_controller_t *xdma;
+ struct xdma_request *xr;
int i;
- xchan = (xdma_channel_t *)arg;
- KASSERT(xchan != NULL, ("xchan is NULL"));
+ xdma = xchan->xdma;
- if (err) {
- xchan->map_err = 1;
- return;
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ xr->buf.cbuf = contigmalloc(xchan->maxsegsize,
+ M_XDMA, 0, 0, ~0, PAGE_SIZE, 0);
+ if (xr->buf.cbuf == NULL) {
+ device_printf(xdma->dev,
+ "%s: Can't allocate contiguous kernel"
+ " physical memory\n", __func__);
+ return (-1);
+ }
}
- 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;
- }
+ return (0);
}
static int
-xdma_desc_alloc_bus_dma(xdma_channel_t *xchan, uint32_t desc_size,
- uint32_t align)
+xchan_bufs_alloc_busdma(xdma_channel_t *xchan)
{
xdma_controller_t *xdma;
- bus_size_t all_desc_sz;
- xdma_config_t *conf;
- int nsegments;
+ struct xdma_request *xr;
int err;
+ int i;
xdma = xchan->xdma;
- conf = &xchan->conf;
-
- nsegments = conf->block_num;
- all_desc_sz = (nsegments * desc_size);
+ /* Create bus_dma tag */
err = bus_dma_tag_create(
- bus_get_dma_tag(xdma->dev),
- align, desc_size, /* alignment, boundary */
+ bus_get_dma_tag(xdma->dev), /* Parent tag. */
+ xchan->alignment, 0, /* 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 */
+ xchan->maxsegsize * xchan->maxnsegs, /* maxsize */
+ xchan->maxnsegs, /* nsegments */
+ xchan->maxsegsize, /* maxsegsize */
+ 0, /* flags */
NULL, NULL, /* lockfunc, lockarg */
- &xchan->dma_tag);
- if (err) {
+ &xchan->dma_tag_bufs);
+ if (err != 0) {
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) {
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ err = bus_dmamap_create(xchan->dma_tag_bufs, 0,
+ &xr->buf.map);
+ if (err != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't create buf DMA map.\n", __func__);
+
+ /* Cleanup. */
+ bus_dma_tag_destroy(xchan->dma_tag_bufs);
+
+ return (-1);
+ }
+ }
+
+ return (0);
+}
+
+static int
+xchan_bufs_alloc(xdma_channel_t *xchan)
+{
+ xdma_controller_t *xdma;
+ int ret;
+
+ xdma = xchan->xdma;
+
+ if (xdma == NULL) {
+ device_printf(xdma->dev,
+ "%s: Channel was not allocated properly.\n", __func__);
+ return (-1);
+ }
+
+ if (xchan->caps & XCHAN_CAP_BUSDMA) {
+ ret = xchan_bufs_alloc_busdma(xchan);
+ } else {
+ ret = xchan_bufs_alloc_no_busdma(xchan);
+ }
+ if (ret != 0) {
device_printf(xdma->dev,
- "%s: Can't allocate memory for descriptors.\n", __func__);
+ "%s: Can't allocate bufs.\n", __func__);
return (-1);
}
- xchan->descs_phys = malloc(nsegments * sizeof(xdma_descriptor_t), M_XDMA,
- (M_WAITOK | M_ZERO));
+ xchan->flags |= XCHAN_BUFS_ALLOCATED;
- 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 (0);
+}
+
+static int
+xchan_bufs_free(xdma_channel_t *xchan)
+{
+ struct xdma_request *xr;
+ struct xchan_buf *b;
+ int i;
+
+ if ((xchan->flags & XCHAN_BUFS_ALLOCATED) == 0) {
+ /* No bufs allocated. */
return (-1);
}
- if (xchan->map_err != 0) {
+ if (xchan->caps & XCHAN_CAP_BUSDMA) {
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ b = &xr->buf;
+ bus_dmamap_destroy(xchan->dma_tag_bufs, b->map);
+ }
+ bus_dma_tag_destroy(xchan->dma_tag_bufs);
+ } else {
+ for (i = 0; i < xchan->xr_num; i++) {
+ xr = &xchan->xr_mem[i];
+ contigfree(xr->buf.cbuf, xchan->maxsegsize, M_XDMA);
+ }
+ }
+
+ xchan->flags &= ~XCHAN_BUFS_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);
+
+ ret = XDMA_CHANNEL_PREP_MEMCPY(xdma->dma_dev, xchan);
+ if (ret != 0) {
device_printf(xdma->dev,
- "%s: Can't load DMA map.\n", __func__);
+ "%s: Can't prepare memcpy transfer.\n", __func__);
+ XCHAN_UNLOCK(xchan);
+
return (-1);
}
+ XCHAN_UNLOCK(xchan);
+
return (0);
}
/*
- * This function called by DMA controller driver.
+ * xr_num - xdma requests queue size,
+ * maxsegsize - maximum allowed scatter-gather list element size in bytes
*/
int
-xdma_desc_alloc(xdma_channel_t *xchan, uint32_t desc_size, uint32_t align)
+xdma_prep_sg(xdma_channel_t *xchan, uint32_t xr_num, uint32_t maxsegsize,
+ uint32_t maxnsegs, uint32_t alignment)
{
xdma_controller_t *xdma;
- xdma_config_t *conf;
int ret;
-
- XCHAN_ASSERT_LOCKED(xchan);
+ int err;
xdma = xchan->xdma;
- if (xdma == NULL) {
+
+ KASSERT(xdma != NULL, ("xdma is NULL"));
+
+ if (xchan->flags & XCHAN_CONFIGURED) {
device_printf(xdma->dev,
- "%s: Channel was not allocated properly.\n", __func__);
+ "%s: Channel is already configured.\n", __func__);
return (-1);
}
- if (xchan->flags & XCHAN_DESC_ALLOCATED) {
+ xchan->maxsegsize = maxsegsize;
+ xchan->maxnsegs = maxnsegs;
+ xchan->alignment = alignment;
+ xchan->xr_num = xr_num;
+
+ if (xchan->maxnsegs > XDMA_MAX_SEG) {
device_printf(xdma->dev,
- "%s: Descriptors already allocated.\n", __func__);
+ "%s: maxnsegs %d is too big\n",
+ __func__, xchan->maxnsegs);
return (-1);
}
- if ((xchan->flags & XCHAN_CONFIGURED) == 0) {
+ /* Allocate sglist. */
+ ret = xchan_sglist_init(xchan);
+ if (ret != 0) {
device_printf(xdma->dev,
- "%s: Channel has no configuration.\n", __func__);
+ "%s: Can't allocate sglist.\n", __func__);
return (-1);
}
- conf = &xchan->conf;
+ TAILQ_INIT(&xchan->bank);
+ TAILQ_INIT(&xchan->queue_in);
+ TAILQ_INIT(&xchan->queue_out);
+ TAILQ_INIT(&xchan->processing);
- XCHAN_UNLOCK(xchan);
- ret = xdma_desc_alloc_bus_dma(xchan, desc_size, align);
- XCHAN_LOCK(xchan);
+ /* Allocate memory for requests. */
+ err = xchan_bank_init(xchan);
+ if (err != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't init bank.\n", __func__);
+
+ /* Cleanup */
+ xchan_sglist_free(xchan);
+
+ return (-1);
+ }
+
+ /* Allocate bufs. */
+ ret = xchan_bufs_alloc(xchan);
if (ret != 0) {
device_printf(xdma->dev,
- "%s: Can't allocate memory for descriptors.\n",
- __func__);
+ "%s: Can't allocate bufs.\n", __func__);
+
+ /* Cleanup */
+ xchan_sglist_free(xchan);
+ xchan_bank_free(xchan);
+
return (-1);
}
- xchan->flags |= XCHAN_DESC_ALLOCATED;
+ xchan->flags |= (XCHAN_CONFIGURED | XCHAN_TYPE_SG);
- /* We are going to write to descriptors. */
- bus_dmamap_sync(xchan->dma_tag, xchan->dma_map, BUS_DMASYNC_PREWRITE);
+ XCHAN_LOCK(xchan);
+ ret = XDMA_CHANNEL_PREP_SG(xdma->dma_dev, xchan);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't prepare SG transfer.\n", __func__);
+ XCHAN_UNLOCK(xchan);
+
+ return (-1);
+ }
+ XCHAN_UNLOCK(xchan);
return (0);
}
int
-xdma_desc_free(xdma_channel_t *xchan)
+xdma_dequeue(xdma_channel_t *xchan, void **user,
+ xdma_transfer_status_t *status)
{
+ struct xdma_request *xr_tmp;
+ struct xdma_request *xr;
+
+ QUEUE_OUT_LOCK(xchan);
+ TAILQ_FOREACH_SAFE(xr, &xchan->queue_out, xr_next, xr_tmp) {
+ TAILQ_REMOVE(&xchan->queue_out, xr, xr_next);
+ break;
+ }
+ QUEUE_OUT_UNLOCK(xchan);
- if ((xchan->flags & XCHAN_DESC_ALLOCATED) == 0) {
- /* No descriptors allocated. */
+ if (xr == NULL) {
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);
+ *user = xr->user;
+ status->error = xr->status.error;
+ status->transferred = xr->status.transferred;
- xchan->flags &= ~(XCHAN_DESC_ALLOCATED);
+ xchan_bank_put(xchan, xr);
return (0);
}
int
-xdma_prep_memcpy(xdma_channel_t *xchan, uintptr_t src_addr,
- uintptr_t dst_addr, size_t len)
+xdma_enqueue(xdma_channel_t *xchan, uintptr_t src, uintptr_t dst,
+ uint8_t src_width, uint8_t dst_width, bus_size_t len,
+ enum xdma_direction dir, void *user)
{
+ struct xdma_request *xr;
xdma_controller_t *xdma;
- xdma_config_t *conf;
+
+ xdma = xchan->xdma;
+
+ xr = xchan_bank_get(xchan);
+ if (xr == NULL) {
+ /* No space is available yet. */
+ return (-1);
+ }
+
+ xr->user = user;
+ xr->direction = dir;
+ xr->m = NULL;
+ xr->bp = NULL;
+ xr->len = len;
+ xr->type = 0;
+ xr->src_addr = src;
+ xr->dst_addr = dst;
+ xr->src_width = src_width;
+ xr->dst_width = dst_width;
+
+ QUEUE_IN_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->queue_in, xr, xr_next);
+ QUEUE_IN_UNLOCK(xchan);
+
+ return (0);
+}
+
+static void
+xdma_dmamap_cb(void *arg, bus_dma_segment_t *segs, int nsegs, int error)
+{
+ struct seg_load_request *slr;
+ struct bus_dma_segment *seg;
+ int i;
+
+ slr = arg;
+ seg = slr->seg;
+
+ if (error != 0) {
+ slr->error = error;
+ return;
+ }
+
+ slr->nsegs = nsegs;
+
+ for (i = 0; i < nsegs; i++) {
+ seg[i].ds_addr = segs[i].ds_addr;
+ seg[i].ds_len = segs[i].ds_len;
+ }
+}
+
+static int
+xdma_load_busdma(xdma_channel_t *xchan, struct xdma_request *xr,
+ struct bus_dma_segment *seg)
+{
+ xdma_controller_t *xdma;
+ struct seg_load_request slr;
+ uint32_t nsegs;
+ void *addr;
+ int error;
+
+ xdma = xchan->xdma;
+
+ error = 0;
+ nsegs = 0;
+
+ switch (xr->type) {
+ case XR_TYPE_MBUF:
+ error = bus_dmamap_load_mbuf_sg(xchan->dma_tag_bufs,
+ xr->buf.map, xr->m, seg, &nsegs, BUS_DMA_NOWAIT);
+ break;
+ case XR_TYPE_BIO:
+ slr.nsegs = 0;
+ slr.error = 0;
+ slr.seg = seg;
+ error = bus_dmamap_load_bio(xchan->dma_tag_bufs,
+ xr->buf.map, xr->bp, xdma_dmamap_cb, &slr, BUS_DMA_NOWAIT);
+ if (slr.error != 0) {
+ device_printf(xdma->dma_dev,
+ "%s: bus_dmamap_load failed, err %d\n",
+ __func__, slr.error);
+ return (0);
+ }
+ nsegs = slr.nsegs;
+ break;
+ case XR_TYPE_ADDR:
+ switch (xr->direction) {
+ case XDMA_MEM_TO_DEV:
+ addr = (void *)xr->src_addr;
+ break;
+ case XDMA_DEV_TO_MEM:
+ addr = (void *)xr->dst_addr;
+ break;
+ default:
+ device_printf(xdma->dma_dev,
+ "%s: Direction is not supported\n", __func__);
+ return (0);
+ }
+ slr.nsegs = 0;
+ slr.error = 0;
+ slr.seg = seg;
+ error = bus_dmamap_load(xchan->dma_tag_bufs, xr->buf.map,
+ addr, xr->len, xdma_dmamap_cb, &slr, BUS_DMA_NOWAIT);
+ if (slr.error != 0) {
+ device_printf(xdma->dma_dev,
+ "%s: bus_dmamap_load failed, err %d\n",
+ __func__, slr.error);
+ return (0);
+ }
+ nsegs = slr.nsegs;
+ break;
+ default:
+ break;
+ }
+
+ if (error != 0) {
+ if (error == ENOMEM) {
+ /*
+ * Out of memory. Try again later.
+ * TODO: count errors.
+ */
+ } else {
+ device_printf(xdma->dma_dev,
+ "%s: bus_dmamap_load failed with err %d\n",
+ __func__, error);
+ }
+ return (0);
+ }
+
+ if (xr->direction == XDMA_MEM_TO_DEV) {
+ bus_dmamap_sync(xchan->dma_tag_bufs, xr->buf.map,
+ BUS_DMASYNC_PREWRITE);
+ } else {
+ bus_dmamap_sync(xchan->dma_tag_bufs, xr->buf.map,
+ BUS_DMASYNC_PREREAD);
+ }
+
+ return (nsegs);
+}
+
+static int
+xdma_load_no_busdma(xdma_channel_t *xchan, struct xdma_request *xr,
+ struct bus_dma_segment *seg)
+{
+ xdma_controller_t *xdma;
+ struct mbuf *m;
+ uint32_t nsegs;
+
+ xdma = xchan->xdma;
+
+ m = xr->m;
+
+ nsegs = 1;
+
+ switch (xr->type) {
+ case XR_TYPE_MBUF:
+ if (xr->direction == XDMA_MEM_TO_DEV) {
+ m_copydata(m, 0, m->m_pkthdr.len, xr->buf.cbuf);
+ seg[0].ds_addr = (bus_addr_t)xr->buf.cbuf;
+ seg[0].ds_len = m->m_pkthdr.len;
+ } else {
+ seg[0].ds_addr = mtod(m, bus_addr_t);
+ seg[0].ds_len = m->m_pkthdr.len;
+ }
+ break;
+ case XR_TYPE_BIO:
+ case XR_TYPE_ADDR:
+ default:
+ panic("implement me\n");
+ }
+
+ return (nsegs);
+}
+
+static int
+xdma_sglist_prepare_one(xdma_channel_t *xchan,
+ struct xdma_request *xr, struct bus_dma_segment *seg)
+{
+ xdma_controller_t *xdma;
+ int error;
+ int nsegs;
+
+ xdma = xchan->xdma;
+
+ error = 0;
+ nsegs = 0;
+
+ if (xchan->caps & XCHAN_CAP_BUSDMA) {
+ nsegs = xdma_load_busdma(xchan, xr, seg);
+ } else {
+ nsegs = xdma_load_no_busdma(xchan, xr, seg);
+ }
+ if (nsegs == 0) {
+ /* Try again later. */
+ return (0);
+ }
+
+ xr->buf.nsegs = nsegs;
+ xr->buf.nsegs_left = nsegs;
+
+ return (nsegs);
+}
+
+static int
+xdma_sglist_prepare(xdma_channel_t *xchan,
+ struct xdma_sglist *sg)
+{
+ struct bus_dma_segment seg[XDMA_MAX_SEG];
+ struct xdma_request *xr;
+ struct xdma_request *xr_tmp;
+ xdma_controller_t *xdma;
+ uint32_t capacity;
+ uint32_t n;
+ uint32_t c;
+ int nsegs;
+ int ret;
+
+ xdma = xchan->xdma;
+
+ n = 0;
+
+ ret = XDMA_CHANNEL_CAPACITY(xdma->dma_dev, xchan, &capacity);
+ if (ret != 0) {
+ device_printf(xdma->dev,
+ "%s: Can't get DMA controller capacity.\n", __func__);
+ return (-1);
+ }
+
+ TAILQ_FOREACH_SAFE(xr, &xchan->queue_in, xr_next, xr_tmp) {
+ switch (xr->type) {
+ case XR_TYPE_MBUF:
+ c = xdma_mbuf_defrag(xchan, xr);
+ break;
+ case XR_TYPE_BIO:
+ /* TODO: unmapped bio ? */
+ case XR_TYPE_ADDR:
+ default:
+ c = 1;
+ }
+
+ if (capacity <= (c + n)) {
+ /*
+ * No space yet available for the entire
+ * request in the DMA engine.
+ */
+ break;
+ }
+
+ if ((c + n + xchan->maxnsegs) >= XDMA_SGLIST_MAXLEN) {
+ /* Sglist is full. */
+ break;
+ }
+
+ nsegs = xdma_sglist_prepare_one(xchan, xr, seg);
+ if (nsegs == 0) {
+ break;
+ }
+
+ xdma_sglist_add(&sg[n], seg, nsegs, xr);
+ n += nsegs;
+
+ QUEUE_IN_LOCK(xchan);
+ TAILQ_REMOVE(&xchan->queue_in, xr, xr_next);
+ QUEUE_IN_UNLOCK(xchan);
+
+ QUEUE_PROC_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->processing, xr, xr_next);
+ QUEUE_PROC_UNLOCK(xchan);
+ }
+
+ return (n);
+}
+
+int
+xdma_queue_submit(xdma_channel_t *xchan)
+{
+ struct xdma_sglist *sg;
+ xdma_controller_t *xdma;
+ uint32_t sg_n;
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;
+ sg = xchan->sg;
- xchan->flags |= (XCHAN_CONFIGURED | XCHAN_TYPE_MEMCPY);
+ if ((xchan->flags & XCHAN_BUFS_ALLOCATED) == 0) {
+ device_printf(xdma->dev,
+ "%s: Can't submit SG transfer: no bufs\n",
+ __func__);
+ return (-1);
+ }
XCHAN_LOCK(xchan);
- /* Deallocate old descriptors, if any. */
- xdma_desc_free(xchan);
+ sg_n = xdma_sglist_prepare(xchan, sg);
+ if (sg_n == 0) {
+ /* Nothing to submit */
+ XCHAN_UNLOCK(xchan);
+ return (0);
+ }
- ret = XDMA_CHANNEL_PREP_MEMCPY(xdma->dma_dev, xchan);
+ /* Now submit xdma_sglist to DMA engine driver. */
+
+ ret = XDMA_CHANNEL_SUBMIT_SG(xdma->dma_dev, xchan, sg, sg_n);
if (ret != 0) {
device_printf(xdma->dev,
- "%s: Can't prepare memcpy transfer.\n", __func__);
+ "%s: Can't submit SG transfer.\n", __func__);
+
XCHAN_UNLOCK(xchan);
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);
@@ -453,8 +978,8 @@
XCHAN_LOCK(xchan);
- /* Deallocate old descriptors, if any. */
- xdma_desc_free(xchan);
+ /* Deallocate bufs, if any. */
+ xchan_bufs_free(xchan);
ret = XDMA_CHANNEL_PREP_CYCLIC(xdma->dma_dev, xchan);
if (ret != 0) {
@@ -465,12 +990,6 @@
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);
@@ -484,6 +1003,11 @@
xdma = xchan->xdma;
+ if (xchan->flags & XCHAN_TYPE_SG) {
+ /* Not valid. */
+ return (0);
+ };
+
ret = XDMA_CHANNEL_CONTROL(xdma->dma_dev, xchan, XDMA_CMD_BEGIN);
if (ret != 0) {
device_printf(xdma->dev,
@@ -531,25 +1055,68 @@
}
int
-xdma_callback(xdma_channel_t *xchan)
+xchan_seg_done(xdma_channel_t *xchan,
+ struct xdma_transfer_status *st)
{
- struct xdma_intr_handler *ih_tmp;
- struct xdma_intr_handler *ih;
+ struct xdma_request *xr;
+ xdma_controller_t *xdma;
+ struct xchan_buf *b;
- TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp) {
- if (ih->cb != NULL) {
- ih->cb(ih->cb_user);
+ xdma = xchan->xdma;
+
+ xr = TAILQ_FIRST(&xchan->processing);
+ if (xr == NULL) {
+ panic("request not found\n");
+ }
+
+ b = &xr->buf;
+
+ atomic_subtract_int(&b->nsegs_left, 1);
+
+ if (b->nsegs_left == 0) {
+ if (xchan->caps & XCHAN_CAP_BUSDMA) {
+ if (xr->direction == XDMA_MEM_TO_DEV) {
+ bus_dmamap_sync(xchan->dma_tag_bufs, b->map,
+ BUS_DMASYNC_POSTWRITE);
+ } else {
+ bus_dmamap_sync(xchan->dma_tag_bufs, b->map,
+ BUS_DMASYNC_POSTREAD);
+ }
+ bus_dmamap_unload(xchan->dma_tag_bufs, b->map);
}
+ xr->status.error = st->error;
+ xr->status.transferred = st->transferred;
+
+ QUEUE_PROC_LOCK(xchan);
+ TAILQ_REMOVE(&xchan->processing, xr, xr_next);
+ QUEUE_PROC_UNLOCK(xchan);
+
+ QUEUE_OUT_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->queue_out, xr, xr_next);
+ QUEUE_OUT_UNLOCK(xchan);
}
return (0);
}
-void
-xdma_assert_locked(void)
+int
+xdma_callback(xdma_channel_t *xchan, xdma_transfer_status_t *status)
{
+ struct xdma_intr_handler *ih_tmp;
+ struct xdma_intr_handler *ih;
+ xdma_controller_t *xdma;
+
+ xdma = xchan->xdma;
+
+ TAILQ_FOREACH_SAFE(ih, &xchan->ie_handlers, ih_next, ih_tmp) {
+ if (ih->cb != NULL) {
+ ih->cb(ih->cb_user, status);
+ }
+ }
- XDMA_ASSERT_LOCKED();
+ wakeup(xdma);
+
+ return (0);
}
#ifdef FDT
@@ -561,7 +1128,8 @@
{
uint32_t ret;
- ret = XDMA_OFW_MD_DATA(xdma->dma_dev, cells, ncells, (void **)&xdma->data);
+ ret = XDMA_OFW_MD_DATA(xdma->dma_dev,
+ cells, ncells, (void **)&xdma->data);
return (ret);
}
@@ -637,6 +1205,18 @@
xdma_ofw_md_data(xdma, cells, ncells);
free(cells, M_OFWPROP);
+ mtx_init(&xdma->proc_mtx, "xDMA ofw controller", NULL, MTX_DEF);
+ error = kproc_create(&xdma_task, xdma, &xdma->xdma_proc, 0, 0, "xdma drainer");
+ if (error) {
+ device_printf(dev,
+ "%s failed to create kproc.\n", __func__);
+
+ /* Cleanup */
+ free(xdma, M_XDMA);
+
+ return (NULL);
+ }
+
return (xdma);
}
#endif
@@ -656,6 +1236,9 @@
return (-1);
}
+ kproc_shutdown(&xdma->xdma_proc, 0);
+ mtx_destroy(&xdma->proc_mtx);
+
free(xdma->data, M_DEVBUF);
free(xdma, M_XDMA);
Index: sys/dev/xdma/xdma_bio.c
===================================================================
--- /dev/null
+++ sys/dev/xdma/xdma_bio.c
@@ -0,0 +1,123 @@
+/*-
+ * Copyright (c) 2017 Ruslan Bukin <br@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/mbuf.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>
+
+int
+xdma_dequeue_bio(xdma_channel_t *xchan, struct bio **bp,
+ xdma_transfer_status_t *status)
+{
+ struct xdma_request *xr_tmp;
+ struct xdma_request *xr;
+
+ QUEUE_OUT_LOCK(xchan);
+ TAILQ_FOREACH_SAFE(xr, &xchan->queue_out, xr_next, xr_tmp) {
+ TAILQ_REMOVE(&xchan->queue_out, xr, xr_next);
+ break;
+ }
+ QUEUE_OUT_UNLOCK(xchan);
+
+ if (xr == NULL) {
+ return (-1);
+ }
+
+ *bp = xr->bp;
+
+ status->error = xr->status.error;
+ status->transferred = xr->status.transferred;
+
+ xchan_bank_put(xchan, xr);
+
+ return (0);
+}
+
+int
+xdma_enqueue_bio(xdma_channel_t *xchan, struct bio **bp,
+ bus_addr_t addr, uint8_t src_width, uint8_t dst_width,
+ enum xdma_direction dir)
+{
+ struct xdma_request *xr;
+ xdma_controller_t *xdma;
+
+ xdma = xchan->xdma;
+
+ xr = xchan_bank_get(xchan);
+ if (xr == NULL) {
+ /* No space is available yet. */
+ return (-1);
+ };
+
+ xr->direction = dir;
+ xr->bp = *bp;
+ xr->type = XR_TYPE_BIO;
+ xr->src_width = src_width;
+ xr->dst_width = dst_width;
+ if (dir == XDMA_MEM_TO_DEV) {
+ xr->dst_addr = addr;
+ xr->src_addr = 0;
+ } else {
+ xr->dst_addr = 0;
+ xr->src_addr = addr;
+ }
+
+ QUEUE_IN_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->queue_in, xr, xr_next);
+ QUEUE_IN_UNLOCK(xchan);
+
+ return (0);
+}
Index: sys/dev/xdma/xdma_fdt_test.c
===================================================================
--- sys/dev/xdma/xdma_fdt_test.c
+++ sys/dev/xdma/xdma_fdt_test.c
@@ -297,7 +297,12 @@
mtx_lock(&sc->mtx);
- xdmatest_test(sc);
+ if (xdmatest_test(sc) != 0) {
+ mtx_unlock(&sc->mtx);
+ device_printf(sc->dev,
+ "%s: Test failed.\n", __func__);
+ break;
+ }
timeout = 100;
Index: sys/dev/xdma/xdma_if.m
===================================================================
--- sys/dev/xdma/xdma_if.m
+++ sys/dev/xdma/xdma_if.m
@@ -59,6 +59,33 @@
};
#
+# Prepare xDMA channel for a scatter-gather transfer.
+#
+METHOD int channel_prep_sg {
+ device_t dev;
+ struct xdma_channel *xchan;
+};
+
+#
+# Check the amount of free entries for requests.
+#
+METHOD int channel_capacity {
+ device_t dev;
+ struct xdma_channel *xchan;
+ uint32_t *capacity;
+};
+
+#
+# Submit scatter-gather list to DMA.
+#
+METHOD int channel_submit_sg {
+ device_t dev;
+ struct xdma_channel *xchan;
+ struct xdma_sglist *sg;
+ uint32_t sg_n;
+};
+
+#
# Notify driver we have machine-dependend data.
#
METHOD int ofw_md_data {
@@ -77,7 +104,7 @@
};
#
-# Free the channel, including descriptors.
+# Free the channel.
#
METHOD int channel_free {
device_t dev;
Index: sys/dev/xdma/xdma_mbuf.c
===================================================================
--- /dev/null
+++ sys/dev/xdma/xdma_mbuf.c
@@ -0,0 +1,170 @@
+/*-
+ * Copyright (c) 2017 Ruslan Bukin <br@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/mbuf.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>
+
+int
+xdma_dequeue_mbuf(xdma_channel_t *xchan, struct mbuf **mp,
+ xdma_transfer_status_t *status)
+{
+ struct xdma_request *xr;
+ struct xdma_request *xr_tmp;
+
+ QUEUE_OUT_LOCK(xchan);
+ TAILQ_FOREACH_SAFE(xr, &xchan->queue_out, xr_next, xr_tmp) {
+ TAILQ_REMOVE(&xchan->queue_out, xr, xr_next);
+ break;
+ }
+ QUEUE_OUT_UNLOCK(xchan);
+
+ if (xr == NULL) {
+ return (-1);
+ }
+
+ *mp = xr->m;
+ status->error = xr->status.error;
+ status->transferred = xr->status.transferred;
+
+ xchan_bank_put(xchan, xr);
+
+ return (0);
+}
+
+int
+xdma_enqueue_mbuf(xdma_channel_t *xchan, struct mbuf **mp,
+ uintptr_t addr, uint8_t src_width, uint8_t dst_width,
+ enum xdma_direction dir)
+{
+ struct xdma_request *xr;
+ xdma_controller_t *xdma;
+
+ xdma = xchan->xdma;
+
+ xr = xchan_bank_get(xchan);
+ if (xr == NULL) {
+ printf("%s: no space\n", __func__);
+ /* No space is available yet. */
+ return (-1);
+ };
+
+ xr->direction = dir;
+ xr->m = *mp;
+ xr->type = XR_TYPE_MBUF;
+ if (dir == XDMA_MEM_TO_DEV) {
+ xr->dst_addr = addr;
+ xr->src_addr = 0;
+ } else {
+ xr->src_addr = addr;
+ xr->dst_addr = 0;
+ }
+ xr->src_width = src_width;
+ xr->dst_width = dst_width;
+
+ QUEUE_IN_LOCK(xchan);
+ TAILQ_INSERT_TAIL(&xchan->queue_in, xr, xr_next);
+ QUEUE_IN_UNLOCK(xchan);
+
+ return (0);
+}
+
+uint32_t
+xdma_mbuf_chain_count(struct mbuf *m0)
+{
+ struct mbuf *m;
+ uint32_t c;
+
+ c = 0;
+
+ for (m = m0; m != NULL; m = m->m_next) {
+ c++;
+ }
+
+ return (c);
+}
+
+uint32_t
+xdma_mbuf_defrag(xdma_channel_t *xchan, struct xdma_request *xr)
+{
+ xdma_controller_t *xdma;
+ struct mbuf *m;
+ uint32_t c;
+
+ xdma = xchan->xdma;
+
+ c = xdma_mbuf_chain_count(xr->m);
+ if (c == 1) {
+ /* Nothing to do. */
+ return (c);
+ }
+
+ if (xchan->caps & XCHAN_CAP_BUSDMA) {
+ if ((xchan->caps & XCHAN_CAP_BUSDMA_NOSEG) || \
+ (c > xchan->maxnsegs)) {
+ if ((m = m_defrag(xr->m, M_NOWAIT)) == NULL) {
+ device_printf(xdma->dma_dev,
+ "%s: Can't defrag mbuf\n",
+ __func__);
+ return (c);
+ }
+ xr->m = m;
+ c = 1;
+ }
+ }
+
+ return (c);
+}
Index: sys/dev/xdma/xdma_sglist.c
===================================================================
--- /dev/null
+++ sys/dev/xdma/xdma_sglist.c
@@ -0,0 +1,125 @@
+/*-
+ * Copyright (c) 2017 Ruslan Bukin <br@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>
+
+int
+xchan_sglist_init(xdma_channel_t *xchan)
+{
+ uint32_t sz;
+
+ if (xchan->flags & XCHAN_SGLIST_ALLOCATED) {
+ return (-1);
+ }
+
+ sz = (sizeof(struct xdma_sglist) * XDMA_SGLIST_MAXLEN);
+ xchan->sg = malloc(sz, M_XDMA, M_WAITOK | M_ZERO);
+ if (xchan->sg == NULL) {
+ return (-1);
+ }
+
+ xchan->flags |= XCHAN_SGLIST_ALLOCATED;
+
+ return (0);
+}
+
+int
+xchan_sglist_free(xdma_channel_t *xchan)
+{
+
+ if (xchan->flags & XCHAN_SGLIST_ALLOCATED) {
+ free(xchan->sg, M_XDMA);
+ }
+
+ xchan->flags &= ~XCHAN_SGLIST_ALLOCATED;
+
+ return (0);
+}
+
+int
+xdma_sglist_add(struct xdma_sglist *sg, struct bus_dma_segment *seg,
+ uint32_t nsegs, struct xdma_request *xr)
+{
+ int i;
+
+ if (nsegs == 0) {
+ return (-1);
+ }
+
+ for (i = 0; i < nsegs; i++) {
+ sg[i].src_width = xr->src_width;
+ sg[i].dst_width = xr->dst_width;
+
+ if (xr->direction == XDMA_MEM_TO_DEV) {
+ sg[i].src_addr = seg[i].ds_addr;
+ sg[i].dst_addr = xr->dst_addr;
+ } else {
+ sg[i].src_addr = xr->src_addr;
+ sg[i].dst_addr = seg[i].ds_addr;
+ }
+ sg[i].len = seg[i].ds_len;
+ sg[i].direction = xr->direction;
+
+ sg[i].first = 0;
+ sg[i].last = 0;
+ }
+
+ sg[0].first = 1;
+ sg[nsegs - 1].last = 1;
+
+ return (0);
+}
File Metadata
Details
Attached
Mime Type
text/plain
Expires
Tue, Jan 14, 7:04 AM (21 h, 27 m)
Storage Engine
blob
Storage Format
Raw Data
Storage Handle
15792063
Default Alt Text
D9617.diff (44 KB)
Attached To
Mode
D9617: xDMA enqueuing (scatter-gather) support
Attached
Detach File
Event Timeline
Log In to Comment