From 60b608b2714472aa22862a20d04f267cbbac0863 Mon Sep 17 00:00:00 2001 From: Sascha Hauer Date: Thu, 10 Jun 2021 16:47:18 +0200 Subject: mci: sdhci: Add DMA transfer helpers 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 Link: https://lore.barebox.org/20210610144720.25620-2-s.hauer@pengutronix.de Signed-off-by: Sascha Hauer --- 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(-) (limited to 'drivers') 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 #include #include +#include #include #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 +#include #include #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); -- cgit v1.2.3