Re: [PATCH mmc-next v3 3/3] mmc: sdhci-of-dwcmshc: solve 128MB DMA boundary limitation

From: Matthew Leon
Date: Sun Jul 29 2018 - 22:56:25 EST


Hey Jisheng,

Shouldn't we be splitting until all DMA blocks are less than 128M boundary? I am a noob, but I think we should be prepared for boundaries that when split in two, will still be greater than 128M. Feel free to disagree but please explain why I may be wrong. Thank-you.

Sincerely,
Matthew Leon

On Sun, Jul 29, 2018 at 10:46 PM, Jisheng Zhang <Jisheng.Zhang@xxxxxxxxxxxxx> wrote:
When using DMA, if the DMA addr spans 128MB boundary, we have to split
the DMA transfer into two so that each one doesn't exceed the boundary.

Signed-off-by: Jisheng Zhang <Jisheng.Zhang@xxxxxxxxxxxxx>
---
Âdrivers/mmc/host/sdhci-of-dwcmshc.c | 43 +++++++++++++++++++++++++++++
Â1 file changed, 43 insertions(+)

diff --git a/drivers/mmc/host/sdhci-of-dwcmshc.c b/drivers/mmc/host/sdhci-of-dwcmshc.c
index 1b7cd144fb01..e890fc8f5284 100644
--- a/drivers/mmc/host/sdhci-of-dwcmshc.c
+++ b/drivers/mmc/host/sdhci-of-dwcmshc.c
@@ -8,21 +8,52 @@
 */

Â#include <linux/clk.h>
+#include <linux/mm.h>
Â#include <linux/module.h>
Â#include <linux/of.h>
+#include <linux/sizes.h>

Â#include "sdhci-pltfm.h"

+#define BOUNDARY_OK(addr, len) \
+Â Â Â Â((addr | (SZ_128M - 1)) == ((addr + len - 1) | (SZ_128M - 1)))
+
Âstruct dwcmshc_priv {
    struct clk   *bus_clk;
Â};

+/*
+ * if DMA addr spans 128MB boundary, we split the DMA transfer into two
+ * so that the DMA transfer doesn't exceed the boundary.
+ */
+static unsigned int dwcmshc_adma_write_desc(struct sdhci_host *host,
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âvoid *desc, dma_addr_t addr,
+Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Â Âint len, unsigned int cmd)
+{
+Â Â Â Âint tmplen, offset;
+
+Â Â Â Âif (likely(!len || BOUNDARY_OK(addr, len)))
+Â Â Â Â Â Â Â Âreturn _sdhci_adma_write_desc(host, desc, addr, len, cmd);
+
+Â Â Â Âoffset = addr & (SZ_128M - 1);
+Â Â Â Âtmplen = SZ_128M - offset;
+Â Â Â Â_sdhci_adma_write_desc(host, desc, addr, tmplen, cmd);
+
+Â Â Â Âaddr += tmplen;
+Â Â Â Âlen -= tmplen;
+Â Â Â Âdesc += host->desc_sz;
+Â Â Â Â_sdhci_adma_write_desc(host, desc, addr, len, cmd);
+
+Â Â Â Âreturn host->desc_sz * 2;
+}
+
Âstatic const struct sdhci_ops sdhci_dwcmshc_ops = {
    .set_clock       = sdhci_set_clock,
    .set_bus_width     = sdhci_set_bus_width,
    .set_uhs_signaling   = sdhci_set_uhs_signaling,
    .get_max_clock     = sdhci_pltfm_clk_get_max_clock,
    .reset         = sdhci_reset,
+   Â.adma_write_desc    = dwcmshc_adma_write_desc,
Â};

Âstatic const struct sdhci_pltfm_data sdhci_dwcmshc_pdata = {
@@ -36,12 +67,24 @@ static int dwcmshc_probe(struct platform_device *pdev)
    struct sdhci_host *host;
    struct dwcmshc_priv *priv;
    int err;
+Â Â Â Âu32 extra;

    host = sdhci_pltfm_init(pdev, &sdhci_dwcmshc_pdata,
                sizeof(struct dwcmshc_priv));
    if (IS_ERR(host))
        return PTR_ERR(host);

+Â Â Â Â/*
+Â Â Â Â * The DMA descriptor table number is calculated as the maximum
+Â Â Â Â * number of segments times 2, to allow for an alignment
+Â Â Â Â * descriptor for each segment, plus 1 for a nop end descriptor,
+Â Â Â Â * plus extra number for cross 128M boundary handling.
+Â Â Â Â */
+Â Â Â Âextra = DIV_ROUND_UP(totalram_pages, SZ_128M / PAGE_SIZE);
+Â Â Â Âif (extra > SDHCI_MAX_SEGS)
+Â Â Â Â Â Â Â Âextra = SDHCI_MAX_SEGS;
+Â Â Â Âhost->adma_table_num = SDHCI_MAX_SEGS * 2 + 1 + extra;
+
    pltfm_host = sdhci_priv(host);
    priv = sdhci_pltfm_priv(pltfm_host);
Â
--
2.18.0