Module Name:    src
Committed By:   jmcneill
Date:           Mon Sep  8 11:06:03 UTC 2014

Modified Files:
        src/sys/arch/arm/allwinner: awin_mmc.c awin_reg.h

Log Message:
use dma for mmc data transfers


To generate a diff of this commit:
cvs rdiff -u -r1.5 -r1.6 src/sys/arch/arm/allwinner/awin_mmc.c
cvs rdiff -u -r1.22 -r1.23 src/sys/arch/arm/allwinner/awin_reg.h

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/allwinner/awin_mmc.c
diff -u src/sys/arch/arm/allwinner/awin_mmc.c:1.5 src/sys/arch/arm/allwinner/awin_mmc.c:1.6
--- src/sys/arch/arm/allwinner/awin_mmc.c:1.5	Sun Sep  7 15:38:06 2014
+++ src/sys/arch/arm/allwinner/awin_mmc.c	Mon Sep  8 11:06:03 2014
@@ -1,4 +1,4 @@
-/* $NetBSD: awin_mmc.c,v 1.5 2014/09/07 15:38:06 jmcneill Exp $ */
+/* $NetBSD: awin_mmc.c,v 1.6 2014/09/08 11:06:03 jmcneill Exp $ */
 
 /*-
  * Copyright (c) 2014 Jared D. McNeill <jmcne...@invisible.ca>
@@ -29,13 +29,14 @@
 #include "locators.h"
 
 #include <sys/cdefs.h>
-__KERNEL_RCSID(0, "$NetBSD: awin_mmc.c,v 1.5 2014/09/07 15:38:06 jmcneill Exp $");
+__KERNEL_RCSID(0, "$NetBSD: awin_mmc.c,v 1.6 2014/09/08 11:06:03 jmcneill Exp $");
 
 #include <sys/param.h>
 #include <sys/bus.h>
 #include <sys/device.h>
 #include <sys/intr.h>
 #include <sys/systm.h>
+#include <sys/kernel.h>
 
 #include <dev/sdmmc/sdmmcvar.h>
 #include <dev/sdmmc/sdmmcchip.h>
@@ -44,9 +45,14 @@ __KERNEL_RCSID(0, "$NetBSD: awin_mmc.c,v
 #include <arm/allwinner/awin_reg.h>
 #include <arm/allwinner/awin_var.h>
 
+#define AWIN_MMC_NDESC		16
+#define AWIN_MMC_WATERMARK	0x20070008
+
 static int	awin_mmc_match(device_t, cfdata_t, void *);
 static void	awin_mmc_attach(device_t, device_t, void *);
 
+static int	awin_mmc_intr(void *);
+
 static int	awin_mmc_host_reset(sdmmc_chipset_handle_t);
 static uint32_t	awin_mmc_host_ocr(sdmmc_chipset_handle_t);
 static int	awin_mmc_host_maxblklen(sdmmc_chipset_handle_t);
@@ -82,6 +88,10 @@ struct awin_mmc_softc {
 	bus_space_handle_t sc_bsh;
 	bus_dma_tag_t sc_dmat;
 
+	void *sc_ih;
+	kmutex_t sc_intr_lock;
+	kcondvar_t sc_intr_cv;
+
 	int sc_mmc_number;
 	int sc_mmc_width;
 	int sc_mmc_present;
@@ -90,6 +100,16 @@ struct awin_mmc_softc {
 	unsigned int sc_pll_freq;
 	unsigned int sc_mod_clk;
 
+	uint32_t sc_idma_xferlen;
+	bus_dma_segment_t sc_idma_segs[1];
+	int sc_idma_nsegs;
+	bus_size_t sc_idma_size;
+	bus_dmamap_t sc_idma_map;
+	int sc_idma_ndesc;
+	void *sc_idma_desc;
+
+	uint32_t sc_idma_idst;
+
 	bool sc_has_gpio_detect;
 	struct awin_gpio_pindata sc_gpio_detect;	/* card detect */
 	bool sc_has_gpio_wp;
@@ -150,6 +170,49 @@ awin_mmc_probe_clocks(struct awin_mmc_so
 #endif
 }
 
+static int
+awin_mmc_idma_setup(struct awin_mmc_softc *sc)
+{
+	int error;
+
+	if (awin_chip_id() == AWIN_CHIP_ID_A10) {
+		sc->sc_idma_xferlen = 0x2000;
+	} else {
+		sc->sc_idma_xferlen = 0x10000;
+	}
+
+	sc->sc_idma_ndesc = AWIN_MMC_NDESC;
+	sc->sc_idma_size = sizeof(struct awin_mmc_idma_descriptor) *
+	    sc->sc_idma_ndesc;
+	error = bus_dmamem_alloc(sc->sc_dmat, sc->sc_idma_size, 0,
+	    sc->sc_idma_size, sc->sc_idma_segs, 1,
+	    &sc->sc_idma_nsegs, BUS_DMA_WAITOK);
+	if (error)
+		return error;
+	error = bus_dmamem_map(sc->sc_dmat, sc->sc_idma_segs,
+	    sc->sc_idma_nsegs, sc->sc_idma_size,
+	    &sc->sc_idma_desc, BUS_DMA_WAITOK);
+	if (error)
+		goto free;
+	error = bus_dmamap_create(sc->sc_dmat, sc->sc_idma_size, 1,
+	    sc->sc_idma_size, 0, BUS_DMA_WAITOK, &sc->sc_idma_map);
+	if (error)
+		goto unmap;
+	error = bus_dmamap_load(sc->sc_dmat, sc->sc_idma_map,
+	    sc->sc_idma_desc, sc->sc_idma_size, NULL, BUS_DMA_WAITOK);
+	if (error)
+		goto destroy;
+	return 0;
+
+destroy:
+	bus_dmamap_destroy(sc->sc_dmat, sc->sc_idma_map);
+unmap:
+	bus_dmamem_unmap(sc->sc_dmat, sc->sc_idma_desc, sc->sc_idma_size);
+free:
+	bus_dmamem_free(sc->sc_dmat, sc->sc_idma_segs, sc->sc_idma_nsegs);
+	return error;
+}
+
 static void
 awin_mmc_attach(device_t parent, device_t self, void *aux)
 {
@@ -164,6 +227,8 @@ awin_mmc_attach(device_t parent, device_
 	sc->sc_bst = aio->aio_core_bst;
 	sc->sc_dmat = aio->aio_dmat;
 	sc->sc_mmc_number = loc->loc_port;
+	mutex_init(&sc->sc_intr_lock, MUTEX_DEFAULT, IPL_BIO);
+	cv_init(&sc->sc_intr_cv, "awinmmcio");
 	bus_space_subregion(sc->sc_bst, aio->aio_core_bsh,
 	    loc->loc_offset, loc->loc_size, &sc->sc_bsh);
 
@@ -197,6 +262,20 @@ awin_mmc_attach(device_t parent, device_
 		}
 	}
 
+	if (awin_mmc_idma_setup(sc) != 0) {
+		aprint_error_dev(self, "failed to setup DMA\n");
+		return;
+	}
+
+	sc->sc_ih = intr_establish(loc->loc_intr, IPL_BIO, IST_LEVEL,
+	    awin_mmc_intr, sc);
+	if (sc->sc_ih == NULL) {
+		aprint_error_dev(self, "couldn't establish interrupt %d\n",
+		    loc->loc_intr);
+		return;
+	}
+	aprint_normal_dev(self, "interrupting at irq %d\n", loc->loc_intr);
+
 	awin_mmc_host_reset(sc);
 	awin_mmc_bus_width(sc, 1);
 
@@ -204,13 +283,16 @@ awin_mmc_attach(device_t parent, device_
 	saa.saa_busname = "sdmmc";
 	saa.saa_sct = &awin_mmc_chip_functions;
 	saa.saa_sch = sc;
+	saa.saa_dmat = sc->sc_dmat;
 	saa.saa_clkmin = 400;
-	saa.saa_clkmax = 52000;
+	saa.saa_clkmax = 50000;
 	saa.saa_caps = SMC_CAPS_4BIT_MODE|
 		       SMC_CAPS_8BIT_MODE|
 		       SMC_CAPS_SD_HIGHSPEED|
 		       SMC_CAPS_MMC_HIGHSPEED|
-		       SMC_CAPS_AUTO_STOP;
+		       SMC_CAPS_AUTO_STOP|
+		       SMC_CAPS_DMA|
+		       SMC_CAPS_MULTI_SEG_DMA;
 	if (sc->sc_has_gpio_detect) {
 		saa.saa_caps |= SMC_CAPS_POLL_CARD_DET;
 	}
@@ -218,6 +300,28 @@ awin_mmc_attach(device_t parent, device_
 	sc->sc_sdmmc_dev = config_found(self, &saa, NULL);
 }
 
+static int
+awin_mmc_intr(void *priv)
+{
+	struct awin_mmc_softc *sc = priv;
+	uint32_t idst;
+
+	mutex_enter(&sc->sc_intr_lock);
+	idst = MMC_READ(sc, AWIN_MMC_IDST);
+	if (!idst) {
+		mutex_exit(&sc->sc_intr_lock);
+		return 0;
+	}
+	MMC_WRITE(sc, AWIN_MMC_IDST, idst);
+
+	sc->sc_idma_idst |= idst;
+	cv_broadcast(&sc->sc_intr_cv);
+
+	mutex_exit(&sc->sc_intr_lock);
+
+	return 1;
+}
+
 static void
 awin_mmc_led(struct awin_mmc_softc *sc, int on)
 {
@@ -250,7 +354,9 @@ awin_mmc_host_ocr(sdmmc_chipset_handle_t
 static int
 awin_mmc_host_maxblklen(sdmmc_chipset_handle_t sch)
 {
-	return 4096;
+	struct awin_mmc_softc *sc = sch;
+
+	return sc->sc_idma_xferlen;
 }
 
 static int
@@ -298,6 +404,10 @@ awin_mmc_update_clock(struct awin_mmc_so
 	uint32_t cmd;
 	int retry;
 
+#ifdef AWIN_MMC_DEBUG
+	aprint_normal_dev(sc->sc_dev, "update clock\n");
+#endif
+
 	cmd = AWIN_MMC_CMD_START |
 	      AWIN_MMC_CMD_UPCLK_ONLY |
 	      AWIN_MMC_CMD_WAIT_PRE_OVER;
@@ -387,37 +497,78 @@ awin_mmc_bus_rod(sdmmc_chipset_handle_t 
 }
 
 static int
-awin_mmc_xfer_wait(struct awin_mmc_softc *sc, struct sdmmc_command *cmd)
-{
-	int retry = 0xfffff;
-	uint32_t bit = (cmd->c_flags & SCF_CMD_READ) ?
-	    AWIN_MMC_STATUS_FIFO_EMPTY : AWIN_MMC_STATUS_FIFO_FULL;
-
-	while (--retry > 0) {
-		uint32_t status = MMC_READ(sc, AWIN_MMC_STATUS);
-		if (!(status & bit))
-			return 0;
-		delay(10);
-	}
-
-	return ETIMEDOUT;
-}
-
-static int
-awin_mmc_xfer_data(struct awin_mmc_softc *sc, struct sdmmc_command *cmd)
+awin_mmc_dma_prepare(struct awin_mmc_softc *sc, struct sdmmc_command *cmd)
 {
-	uint32_t *datap = (uint32_t *)cmd->c_buf;
-	int i;
-
-	for (i = 0; i < (cmd->c_resid >> 2); i++) {
-		if (awin_mmc_xfer_wait(sc, cmd))
-			return ETIMEDOUT;
-		if (cmd->c_flags & SCF_CMD_READ) {
-			datap[i] = MMC_READ(sc, AWIN_MMC_FIFO);
-		} else {
-			MMC_WRITE(sc, AWIN_MMC_FIFO, datap[i]);
+	struct awin_mmc_idma_descriptor *dma = sc->sc_idma_desc;
+	bus_addr_t desc_paddr = sc->sc_idma_map->dm_segs[0].ds_addr;
+	bus_size_t off;
+	int desc, resid, seg;
+	uint32_t val;
+
+	desc = 0;
+	for (seg = 0; seg < cmd->c_dmamap->dm_nsegs; seg++) {
+		bus_addr_t paddr = cmd->c_dmamap->dm_segs[seg].ds_addr;
+		bus_size_t len = cmd->c_dmamap->dm_segs[seg].ds_len;
+		resid = min(len, cmd->c_resid);
+		off = 0;
+		while (resid > 0) {
+			if (desc == sc->sc_idma_ndesc)
+				break;
+			len = min(sc->sc_idma_xferlen, resid);
+			dma[desc].dma_buf_size = len;
+			dma[desc].dma_buf_addr = paddr + off;
+			dma[desc].dma_config = AWIN_MMC_IDMA_CONFIG_CH |
+					       AWIN_MMC_IDMA_CONFIG_OWN;
+			cmd->c_resid -= len;
+			resid -= len;
+			off += len;
+			if (desc == 0) {
+				dma[desc].dma_config |= AWIN_MMC_IDMA_CONFIG_FD;
+			}
+			if (cmd->c_resid == 0) {
+				dma[desc].dma_config |= AWIN_MMC_IDMA_CONFIG_LD;
+				dma[desc].dma_config |= AWIN_MMC_IDMA_CONFIG_ER;
+				dma[desc].dma_next = 0;
+			} else {
+				dma[desc].dma_config |=
+				    AWIN_MMC_IDMA_CONFIG_DIC;
+				dma[desc].dma_next =
+				    desc_paddr + ((desc+1) *
+				    sizeof(struct awin_mmc_idma_descriptor));
+			}
+			++desc;
 		}
 	}
+	if (desc == sc->sc_idma_ndesc) {
+		aprint_error_dev(sc->sc_dev,
+		    "not enough descriptors for %d byte transfer!\n",
+		    cmd->c_datalen);
+		return EIO;
+	}
+
+	bus_dmamap_sync(sc->sc_dmat, sc->sc_idma_map, 0,
+	    sc->sc_idma_size, BUS_DMASYNC_PREWRITE);
+
+	sc->sc_idma_idst = 0;
+
+	val = MMC_READ(sc, AWIN_MMC_GCTRL);
+	val |= AWIN_MMC_GCTRL_DMAEN;
+	val |= AWIN_MMC_GCTRL_INTEN;
+	MMC_WRITE(sc, AWIN_MMC_GCTRL, val);
+	val |= AWIN_MMC_GCTRL_DMARESET;
+	MMC_WRITE(sc, AWIN_MMC_GCTRL, val);
+	MMC_WRITE(sc, AWIN_MMC_DMAC, AWIN_MMC_DMAC_SOFTRESET);
+	MMC_WRITE(sc, AWIN_MMC_DMAC,
+	    AWIN_MMC_DMAC_IDMA_ON|AWIN_MMC_DMAC_FIX_BURST);
+	val = MMC_READ(sc, AWIN_MMC_IDIE);
+	val &= ~(AWIN_MMC_IDST_RECEIVE_INT|AWIN_MMC_IDST_TRANSMIT_INT);
+	if (cmd->c_flags & SCF_CMD_READ)
+		val |= AWIN_MMC_IDST_RECEIVE_INT;
+	else
+		val |= AWIN_MMC_IDST_TRANSMIT_INT;
+	MMC_WRITE(sc, AWIN_MMC_IDIE, val);
+	MMC_WRITE(sc, AWIN_MMC_DLBA, desc_paddr);
+	MMC_WRITE(sc, AWIN_MMC_FTRGLEVEL, AWIN_MMC_WATERMARK);
 
 	return 0;
 }
@@ -432,8 +583,9 @@ awin_mmc_exec_command(sdmmc_chipset_hand
 
 #ifdef AWIN_MMC_DEBUG
 	aprint_normal_dev(sc->sc_dev,
-	    "opcode %d flags 0x%x data %p datalen %d\n",
-	    cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen);
+	    "opcode %d flags 0x%x data %p datalen %d blklen %d\n",
+	    cmd->c_opcode, cmd->c_flags, cmd->c_data, cmd->c_datalen,
+	    cmd->c_blklen);
 #endif
 
 	if (cmd->c_opcode == 0)
@@ -473,17 +625,25 @@ awin_mmc_exec_command(sdmmc_chipset_hand
 	if (cmd->c_datalen == 0) {
 		MMC_WRITE(sc, AWIN_MMC_CMD, cmdval | cmd->c_opcode);
 	} else {
-		MMC_WRITE(sc, AWIN_MMC_GCTRL,
-		    MMC_READ(sc, AWIN_MMC_GCTRL) | AWIN_MMC_GCTRL_ACCESS_BY_AHB);
-		MMC_WRITE(sc, AWIN_MMC_CMD, cmdval | cmd->c_opcode);
+		mutex_enter(&sc->sc_intr_lock);
 		cmd->c_resid = cmd->c_datalen;
-		cmd->c_buf = cmd->c_data;
+		cmd->c_error = awin_mmc_dma_prepare(sc, cmd);
 		awin_mmc_led(sc, 0);
-		cmd->c_error = awin_mmc_xfer_data(sc, cmd);
+		MMC_WRITE(sc, AWIN_MMC_CMD, cmdval | cmd->c_opcode);
+		if (cmd->c_error == 0) {
+			cmd->c_error = cv_timedwait(&sc->sc_intr_cv,
+			    &sc->sc_intr_lock, hz*10);
+		}
+		if (sc->sc_idma_idst & AWIN_MMC_IDST_ERROR) {
+			cmd->c_error = EIO;
+		} else if (!(sc->sc_idma_idst & AWIN_MMC_IDST_COMPLETE)) {
+			cmd->c_error = ETIMEDOUT;
+		}
 		awin_mmc_led(sc, 1);
+		mutex_exit(&sc->sc_intr_lock);
 		if (cmd->c_error) {
 			aprint_error_dev(sc->sc_dev,
-			    "xfer data timeout\n");
+			    "xfer failed, error %d\n", cmd->c_error);
 			goto done;
 		}
 	}
@@ -491,11 +651,11 @@ awin_mmc_exec_command(sdmmc_chipset_hand
 	retry = 0xfffff;
 	while (--retry > 0) {
 		status = MMC_READ(sc, AWIN_MMC_RINT);
-		if (status & 0xbfc2) {
+		if (status & AWIN_MMC_INT_ERROR) {
 			retry = 0;
 			break;
 		}
-		if (status & 0x4)
+		if (status & AWIN_MMC_INT_CMD_DONE)
 			break;
 		delay(10);
 	}
@@ -579,6 +739,7 @@ done:
 	cmd->c_flags |= SCF_ITSDONE;
 
 	if (cmd->c_error) {
+		aprint_error_dev(sc->sc_dev, "i/o error %d\n", cmd->c_error);
 		MMC_WRITE(sc, AWIN_MMC_GCTRL, AWIN_MMC_GCTRL_RESET);
 		awin_mmc_update_clock(sc);
 	}

Index: src/sys/arch/arm/allwinner/awin_reg.h
diff -u src/sys/arch/arm/allwinner/awin_reg.h:1.22 src/sys/arch/arm/allwinner/awin_reg.h:1.23
--- src/sys/arch/arm/allwinner/awin_reg.h:1.22	Sun Sep  7 22:06:36 2014
+++ src/sys/arch/arm/allwinner/awin_reg.h	Mon Sep  8 11:06:03 2014
@@ -724,6 +724,11 @@
 #define AWIN_MMC_IDST_FATAL_BUS_ERR	__BIT(2)
 #define AWIN_MMC_IDST_RECEIVE_INT	__BIT(1)
 #define AWIN_MMC_IDST_TRANSMIT_INT	__BIT(0)
+#define AWIN_MMC_IDST_ERROR \
+	(AWIN_MMC_IDST_ABNORMAL_INT_SUM | AWIN_MMC_IDST_CARD_ERR_SUM | \
+	 AWIN_MMC_IDST_DES_INVALID | AWIN_MMC_IDST_FATAL_BUS_ERR)
+#define AWIN_MMC_IDST_COMPLETE \
+	(AWIN_MMC_IDST_RECEIVE_INT | AWIN_MMC_IDST_TRANSMIT_INT)
 
 #if !defined(_LOCORE)
 struct awin_mmc_idma_descriptor {

Reply via email to