[PATCH 1/3] mci: sdhci: Add DMA transfer helpers

Sascha Hauer s.hauer at pengutronix.de
Thu Jun 10 07:47:18 PDT 2021


The SDHCI helpers only use PIO mode so far. This patch implements SDMA
mode helpers. The helpers with _pio suffix explicitly do PIO while the
DMA helpers have a _dma suffix and first try to do DMA, but fall back
to PIO when DMA is not possible.

Signed-off-by: Sascha Hauer <s.hauer at pengutronix.de>
---
 drivers/mci/arasan-sdhci.c       |   2 +-
 drivers/mci/atmel-sdhci-common.c |   2 +-
 drivers/mci/imx-esdhc-common.c   |   2 +-
 drivers/mci/mci-bcm2835.c        |   2 +-
 drivers/mci/sdhci.c              | 118 ++++++++++++++++++++++++++++++-
 drivers/mci/sdhci.h              |  10 ++-
 6 files changed, 130 insertions(+), 6 deletions(-)

diff --git a/drivers/mci/arasan-sdhci.c b/drivers/mci/arasan-sdhci.c
index dd5ad9d69b..04fce62bf4 100644
--- a/drivers/mci/arasan-sdhci.c
+++ b/drivers/mci/arasan-sdhci.c
@@ -213,7 +213,7 @@ static int arasan_sdhci_send_cmd(struct mci_host *mci, struct mci_cmd *cmd,
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, mask);
 
 	if (data)
-		ret = sdhci_transfer_data(&host->sdhci, data);
+		ret = sdhci_transfer_data_pio(&host->sdhci, data);
 
 error:
 	if (ret) {
diff --git a/drivers/mci/atmel-sdhci-common.c b/drivers/mci/atmel-sdhci-common.c
index 9d78c59e95..eff2a993db 100644
--- a/drivers/mci/atmel-sdhci-common.c
+++ b/drivers/mci/atmel-sdhci-common.c
@@ -166,7 +166,7 @@ int at91_sdhci_send_command(struct at91_sdhci *host, struct mci_cmd *cmd,
 	sdhci_write32(sdhci, SDHCI_INT_STATUS, mask);
 
 	if (data)
-		sdhci_transfer_data(sdhci, data);
+		sdhci_transfer_data_pio(sdhci, data);
 
 	udelay(1000);
 
diff --git a/drivers/mci/imx-esdhc-common.c b/drivers/mci/imx-esdhc-common.c
index 7980278801..a85459d29c 100644
--- a/drivers/mci/imx-esdhc-common.c
+++ b/drivers/mci/imx-esdhc-common.c
@@ -109,7 +109,7 @@ static int esdhc_do_data(struct fsl_esdhc_host *host, struct mci_data *data,
 	u32 irqstat;
 
 	if (esdhc_use_pio_mode())
-		return sdhci_transfer_data(&host->sdhci, data);
+		return sdhci_transfer_data_pio(&host->sdhci, data);
 
 	do {
 		irqstat = sdhci_read32(&host->sdhci, SDHCI_INT_STATUS);
diff --git a/drivers/mci/mci-bcm2835.c b/drivers/mci/mci-bcm2835.c
index 91027857be..0450f899c6 100644
--- a/drivers/mci/mci-bcm2835.c
+++ b/drivers/mci/mci-bcm2835.c
@@ -172,7 +172,7 @@ static int bcm2835_mci_request(struct mci_host *mci, struct mci_cmd *cmd,
 	}
 
 	if (!ret && data)
-		ret = sdhci_transfer_data(&host->sdhci, data);
+		ret = sdhci_transfer_data_pio(&host->sdhci, data);
 
 	sdhci_write32(&host->sdhci, SDHCI_INT_STATUS, 0xFFFFFFFF);
 	if (ret) {
diff --git a/drivers/mci/sdhci.c b/drivers/mci/sdhci.c
index 0783f6d420..aca4a5a6f9 100644
--- a/drivers/mci/sdhci.c
+++ b/drivers/mci/sdhci.c
@@ -4,6 +4,7 @@
 #include <driver.h>
 #include <mci.h>
 #include <io.h>
+#include <dma.h>
 #include <linux/bitfield.h>
 
 #include "sdhci.h"
@@ -123,12 +124,112 @@ void sdhci_set_bus_width(struct sdhci *host, int width)
 
 #endif
 
-int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data)
+void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data)
+{
+	if (!data)
+		return;
+
+	sdhci_write16(sdhci, SDHCI_BLOCK_SIZE, sdhci->sdma_boundary |
+		    SDHCI_TRANSFER_BLOCK_SIZE(data->blocksize));
+	sdhci_write16(sdhci, SDHCI_BLOCK_COUNT, data->blocks);
+}
+
+void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data,
+			  dma_addr_t *dma)
+{
+	struct device_d *dev = sdhci->mci->hw_dev;
+	int nbytes;
+
+	if (!data)
+		return;
+
+	sdhci_setup_data_pio(sdhci, data);
+
+	if (!dma)
+		return;
+
+	nbytes = data->blocks * data->blocksize;
+
+	if (data->flags & MMC_DATA_READ)
+		*dma = dma_map_single(dev, (void *)data->src, nbytes,
+				      DMA_FROM_DEVICE);
+	else
+		*dma = dma_map_single(dev, data->dest, nbytes,
+				      DMA_TO_DEVICE);
+
+	if (dma_mapping_error(dev, *dma)) {
+		*dma = SDHCI_NO_DMA;
+		return;
+	}
+
+	sdhci_write32(sdhci, SDHCI_DMA_ADDRESS, *dma);
+}
+
+int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
+			    dma_addr_t dma)
+{
+	struct device_d *dev = sdhci->mci->hw_dev;
+	int nbytes;
+	u32 irqstat;
+	int ret;
+
+	if (!data)
+		return 0;
+
+	nbytes = data->blocks * data->blocksize;
+
+	do {
+		irqstat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
+
+		if (irqstat & SDHCI_INT_DATA_END_BIT) {
+			ret = -EIO;
+			goto out;
+		}
+
+		if (irqstat & SDHCI_INT_DATA_CRC) {
+			ret = -EBADMSG;
+			goto out;
+		}
+
+		if (irqstat & SDHCI_INT_DATA_TIMEOUT) {
+			ret = -ETIMEDOUT;
+			goto out;
+		}
+
+		if (irqstat & SDHCI_INT_DMA) {
+			u32 addr = sdhci_read32(sdhci, SDHCI_DMA_ADDRESS);
+
+			/*
+			 * DMA engine has stopped on buffer boundary. Acknowledge
+			 * the interrupt and kick the DMA engine again.
+			 */
+			sdhci_write32(sdhci, SDHCI_INT_STATUS, SDHCI_INT_DMA);
+			sdhci_write32(sdhci, SDHCI_DMA_ADDRESS, addr);
+		}
+
+		if (irqstat & SDHCI_INT_XFER_COMPLETE)
+			break;
+	} while (1);
+
+	ret = 0;
+out:
+	if (data->flags & MMC_DATA_READ)
+		dma_unmap_single(dev, dma, nbytes, DMA_FROM_DEVICE);
+	else
+		dma_unmap_single(dev, dma, nbytes, DMA_TO_DEVICE);
+
+	return 0;
+}
+
+int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data)
 {
 	unsigned int block = 0;
 	u32 stat, prs;
 	uint64_t start = get_time_ns();
 
+	if (!data)
+		return 0;
+
 	do {
 		stat = sdhci_read32(sdhci, SDHCI_INT_STATUS);
 		if (stat & SDHCI_INT_ERROR)
@@ -161,6 +262,19 @@ int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data)
 	return 0;
 }
 
+int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma)
+{
+	struct device_d *dev = sdhci->mci->hw_dev;
+
+	if (!data)
+		return 0;
+
+	if (dma_mapping_error(dev, dma))
+		return sdhci_transfer_data_pio(sdhci, data);
+	else
+		return sdhci_transfer_data_dma(sdhci, data, dma);
+}
+
 int sdhci_reset(struct sdhci *sdhci, u8 mask)
 {
 	u8 val;
@@ -428,5 +542,7 @@ int sdhci_setup_host(struct sdhci *host)
 	if (host->caps & SDHCI_CAN_DO_HISPD)
 		mci->host_caps |= MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED;
 
+	host->sdma_boundary = SDHCI_DMA_BOUNDARY_512K;
+
 	return 0;
 }
diff --git a/drivers/mci/sdhci.h b/drivers/mci/sdhci.h
index 0cdd558565..351940a511 100644
--- a/drivers/mci/sdhci.h
+++ b/drivers/mci/sdhci.h
@@ -2,6 +2,7 @@
 #define __MCI_SDHCI_H
 
 #include <pbl.h>
+#include <dma.h>
 #include <linux/iopoll.h>
 
 #define SDHCI_DMA_ADDRESS					0x00
@@ -201,6 +202,7 @@ struct sdhci {
 	u32 caps;	/* CAPABILITY_0 */
 	u32 caps1;	/* CAPABILITY_1 */
 	bool read_caps;	/* Capability flags have been read */
+	u32 sdma_boundary;
 
 	struct mci_host	*mci;
 };
@@ -253,11 +255,17 @@ static inline void sdhci_write8(struct sdhci *host, int reg, u32 val)
 		writeb(val, host->base + reg);
 }
 
+#define SDHCI_NO_DMA DMA_ERROR_CODE
 void sdhci_read_response(struct sdhci *host, struct mci_cmd *cmd);
 void sdhci_set_cmd_xfer_mode(struct sdhci *host, struct mci_cmd *cmd,
 			     struct mci_data *data, bool dma, u32 *command,
 			     u32 *xfer);
-int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data);
+void sdhci_setup_data_pio(struct sdhci *sdhci, struct mci_data *data);
+void sdhci_setup_data_dma(struct sdhci *sdhci, struct mci_data *data, dma_addr_t *dma);
+int sdhci_transfer_data(struct sdhci *sdhci, struct mci_data *data, dma_addr_t dma);
+int sdhci_transfer_data_pio(struct sdhci *sdhci, struct mci_data *data);
+int sdhci_transfer_data_dma(struct sdhci *sdhci, struct mci_data *data,
+			    dma_addr_t dma);
 int sdhci_reset(struct sdhci *sdhci, u8 mask);
 u16 sdhci_calc_clk(struct sdhci *host, unsigned int clock,
 		   unsigned int *actual_clock, unsigned int input_clock);
-- 
2.29.2




More information about the barebox mailing list