Module Name:    src
Committed By:   jakllsch
Date:           Sat Mar  2 03:21:17 UTC 2019

Modified Files:
        src/sys/arch/arm/sunxi: sun6i_dma.c

Log Message:
Implement support for multi-segment transfers.
Make more efficent use of DMA descriptor table memory.


To generate a diff of this commit:
cvs rdiff -u -r1.6 -r1.7 src/sys/arch/arm/sunxi/sun6i_dma.c

Please note that diffs are not public domain; they are subject to the
copyright notices on the relevant files.

Modified files:

Index: src/sys/arch/arm/sunxi/sun6i_dma.c
diff -u src/sys/arch/arm/sunxi/sun6i_dma.c:1.6 src/sys/arch/arm/sunxi/sun6i_dma.c:1.7
--- src/sys/arch/arm/sunxi/sun6i_dma.c:1.6	Sat Nov 17 20:35:41 2018
+++ src/sys/arch/arm/sunxi/sun6i_dma.c	Sat Mar  2 03:21:17 2019
@@ -1,4 +1,4 @@
-/* $NetBSD: sun6i_dma.c,v 1.6 2018/11/17 20:35:41 jmcneill Exp $ */
+/* $NetBSD: sun6i_dma.c,v 1.7 2019/03/02 03:21:17 jakllsch Exp $ */
 
 /*-
  * Copyright (c) 2014-2017 Jared McNeill <jmcne...@invisible.ca>
@@ -29,7 +29,7 @@
 #include "opt_ddb.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: sun6i_dma.c,v 1.6 2018/11/17 20:35:41 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: sun6i_dma.c,v 1.7 2019/03/02 03:21:17 jakllsch Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
@@ -140,11 +140,7 @@ struct sun6idma_channel {
 	void			(*ch_callback)(void *);
 	void			*ch_callbackarg;
 	u_int			ch_portid;
-
-	bus_dma_segment_t	ch_dmasegs[1];
-	bus_dmamap_t		ch_dmamap;
 	void			*ch_dmadesc;
-	bus_size_t		ch_dmadesclen;
 };
 
 struct sun6idma_softc {
@@ -161,6 +157,11 @@ struct sun6idma_softc {
 
 	struct sun6idma_channel	*sc_chan;
 	u_int			sc_nchan;
+	u_int			sc_ndesc_ch;
+
+	bus_dma_segment_t	sc_dmasegs[1];
+	bus_dmamap_t		sc_dmamap;
+	void			*sc_dmadescs;
 };
 
 #define DMA_READ(sc, reg)		\
@@ -168,6 +169,14 @@ struct sun6idma_softc {
 #define DMA_WRITE(sc, reg, val)		\
     bus_space_write_4((sc)->sc_bst, (sc)->sc_bsh, (reg), (val))
 
+#define DESC_NUM			((MAXPHYS / MIN_PAGE_SIZE + 1) + 1)
+#define DESC_LEN(n)			\
+    (sizeof(struct sun6idma_desc) * (n))
+#define DESC_OFFS(ch, n)		\
+    ((ch) * roundup2(DESC_LEN(DESC_NUM), COHERENCY_UNIT) + DESC_LEN(n))
+#define DESC_ADDR(sc, chp, n)		\
+    ((sc)->sc_dmamap->dm_segs[0].ds_addr + DESC_OFFS((chp)->ch_index, (n)))
+
 static void *
 sun6idma_acquire(device_t dev, const void *data, size_t len,
     void (*cb)(void *), void *cbarg)
@@ -247,7 +256,7 @@ sun6idma_transfer(device_t dev, void *pr
 	uint32_t src, dst, len, cfg, mem_cfg, dev_cfg;
 	uint32_t mem_width, dev_width, mem_burst, dev_burst;
 
-	if (req->dreq_nsegs != 1)
+	if (req->dreq_nsegs > sc->sc_ndesc_ch)
 		return EINVAL;
 
 	mem_width = DMA_CFG_DATA_WIDTH(req->dreq_mem_opt.opt_bus_width);
@@ -264,29 +273,34 @@ sun6idma_transfer(device_t dev, void *pr
 	    __SHIFTIN(DMA_CFG_ADDR_MODE_IO, DMA_CFG_SRC_ADDR_MODE) |
 	    __SHIFTIN(ch->ch_portid, DMA_CFG_SRC_DRQ_TYPE);
 
-	if (req->dreq_dir == FDT_DMA_READ) {
-		src = req->dreq_dev_phys;
-		dst = req->dreq_segs[0].ds_addr;
-		cfg = mem_cfg << 16 | dev_cfg;
-	} else {
-		src = req->dreq_segs[0].ds_addr;
-		dst = req->dreq_dev_phys;
-		cfg = dev_cfg << 16 | mem_cfg;
-	}
-	len = req->dreq_segs[0].ds_len;
-
-	desc->dma_config = htole32(cfg);
-	desc->dma_srcaddr = htole32(src);
-	desc->dma_dstaddr = htole32(dst);
-	desc->dma_bcnt = htole32(len);
-	desc->dma_para = htole32(0);
-	desc->dma_next = htole32(DMA_NULL);
+	for (size_t j = 0; j < req->dreq_nsegs; j++) {
+		if (req->dreq_dir == FDT_DMA_READ) {
+			src = req->dreq_dev_phys;
+			dst = req->dreq_segs[j].ds_addr;
+			cfg = mem_cfg << 16 | dev_cfg;
+		} else {
+			src = req->dreq_segs[j].ds_addr;
+			dst = req->dreq_dev_phys;
+			cfg = dev_cfg << 16 | mem_cfg;
+		}
+		len = req->dreq_segs[j].ds_len;
+
+		desc[j].dma_config = htole32(cfg);
+		desc[j].dma_srcaddr = htole32(src);
+		desc[j].dma_dstaddr = htole32(dst);
+		desc[j].dma_bcnt = htole32(len);
+		desc[j].dma_para = htole32(0);
+		if (j < req->dreq_nsegs - 1)
+			desc[j].dma_next = htole32(DESC_ADDR(sc, ch, j + 1));
+		else
+			desc[j].dma_next = htole32(DMA_NULL);
+	}
 
-	bus_dmamap_sync(sc->sc_dmat, ch->ch_dmamap, 0, ch->ch_dmadesclen,
-	    BUS_DMASYNC_PREWRITE);
+	bus_dmamap_sync(sc->sc_dmat, sc->sc_dmamap, DESC_OFFS(ch->ch_index, 0),
+	    DESC_LEN(req->dreq_nsegs), BUS_DMASYNC_PREWRITE);
 
 	DMA_WRITE(sc, DMA_START_ADDR_REG(ch->ch_index),
-	    ch->ch_dmamap->dm_segs[0].ds_addr);
+	    DESC_ADDR(sc, ch, 0));
 	DMA_WRITE(sc, DMA_EN_REG(ch->ch_index), DMA_EN_EN);
 
 	if ((DMA_READ(sc, DMA_EN_REG(ch->ch_index)) & DMA_EN_EN) == 0) {
@@ -360,7 +374,7 @@ sun6idma_attach(device_t parent, device_
 	struct sun6idma_softc * const sc = device_private(self);
 	struct fdt_attach_args * const faa = aux;
 	const int phandle = faa->faa_phandle;
-	const size_t desclen = sizeof(struct sun6idma_desc);
+	size_t desclen;
 	const struct sun6idma_config *conf;
 	struct fdtbus_reset *rst;
 	struct clk *clk;
@@ -406,6 +420,8 @@ sun6idma_attach(device_t parent, device_
 	sc->sc_burst_mask = conf->burst_mask;
 	sc->sc_nchan = conf->num_channels;
 	sc->sc_chan = kmem_alloc(sizeof(*sc->sc_chan) * sc->sc_nchan, KM_SLEEP);
+	desclen = DESC_OFFS(sc->sc_nchan, 0);
+	sc->sc_ndesc_ch = DESC_OFFS(1, 0) / sizeof(struct sun6idma_desc);
 
 	aprint_naive("\n");
 	aprint_normal(": DMA controller (%u channels)\n", sc->sc_nchan);
@@ -415,29 +431,29 @@ sun6idma_attach(device_t parent, device_
 	DMA_WRITE(sc, DMA_IRQ_PEND_REG0_REG, ~0);
 	DMA_WRITE(sc, DMA_IRQ_PEND_REG1_REG, ~0);
 
+	error = bus_dmamem_alloc(sc->sc_dmat, desclen, 0, 0,
+	    sc->sc_dmasegs, 1, &nsegs, BUS_DMA_WAITOK);
+	if (error)
+		panic("bus_dmamem_alloc failed: %d", error);
+	error = bus_dmamem_map(sc->sc_dmat, sc->sc_dmasegs, nsegs,
+	    desclen, (void **)&sc->sc_dmadescs, BUS_DMA_WAITOK);
+	if (error)
+		panic("bus_dmamem_map failed: %d", error);
+	error = bus_dmamap_create(sc->sc_dmat, desclen, 1, desclen, 0,
+	    BUS_DMA_WAITOK, &sc->sc_dmamap);
+	if (error)
+		panic("bus_dmamap_create failed: %d", error);
+	error = bus_dmamap_load(sc->sc_dmat, sc->sc_dmamap,
+	    sc->sc_dmadescs, desclen, NULL, BUS_DMA_WAITOK);
+	if (error)
+		panic("bus_dmamap_load failed: %d", error);
+
 	for (index = 0; index < sc->sc_nchan; index++) {
 		struct sun6idma_channel *ch = &sc->sc_chan[index];
 		ch->ch_index = index;
+		ch->ch_dmadesc = (void *)((uintptr_t)sc->sc_dmadescs + DESC_OFFS(index, 0));
 		ch->ch_callback = NULL;
 		ch->ch_callbackarg = NULL;
-		ch->ch_dmadesclen = desclen;
-
-		error = bus_dmamem_alloc(sc->sc_dmat, desclen, 0, 0,
-		    ch->ch_dmasegs, 1, &nsegs, BUS_DMA_WAITOK);
-		if (error)
-			panic("bus_dmamem_alloc failed: %d", error);
-		error = bus_dmamem_map(sc->sc_dmat, ch->ch_dmasegs, nsegs,
-		    desclen, &ch->ch_dmadesc, BUS_DMA_WAITOK);
-		if (error)
-			panic("bus_dmamem_map failed: %d", error);
-		error = bus_dmamap_create(sc->sc_dmat, desclen, 1, desclen, 0,
-		    BUS_DMA_WAITOK, &ch->ch_dmamap);
-		if (error)
-			panic("bus_dmamap_create failed: %d", error);
-		error = bus_dmamap_load(sc->sc_dmat, ch->ch_dmamap,
-		    ch->ch_dmadesc, desclen, NULL, BUS_DMA_WAITOK);
-		if (error)
-			panic("bus_dmamap_load failed: %d", error);
 
 		DMA_WRITE(sc, DMA_EN_REG(index), 0);
 	}

Reply via email to