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 <[email protected]>
---
 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


_______________________________________________
barebox mailing list
[email protected]
http://lists.infradead.org/mailman/listinfo/barebox

Reply via email to