diff options
author | Sascha Hauer <s.hauer@pengutronix.de> | 2021-06-10 16:47:18 +0200 |
---|---|---|
committer | Sascha Hauer <s.hauer@pengutronix.de> | 2021-06-11 09:38:44 +0200 |
commit | 60b608b2714472aa22862a20d04f267cbbac0863 (patch) | |
tree | 90d51bf5aa566a22ae3a375a14d823261e04c444 /drivers/mci/sdhci.c | |
parent | 56ca2c197e7165add9a75c9f1d2ef42e1487b8be (diff) | |
download | barebox-60b608b2714472aa22862a20d04f267cbbac0863.tar.gz barebox-60b608b2714472aa22862a20d04f267cbbac0863.tar.xz |
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 <s.hauer@pengutronix.de>
Link: https://lore.barebox.org/20210610144720.25620-2-s.hauer@pengutronix.de
Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/mci/sdhci.c')
-rw-r--r-- | drivers/mci/sdhci.c | 118 |
1 files changed, 117 insertions, 1 deletions
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; } |