You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@nuttx.apache.org by xi...@apache.org on 2021/11/04 18:23:35 UTC

[incubator-nuttx] 04/08: imxrt:usdhc add proper support for dcache (wb)

This is an automated email from the ASF dual-hosted git repository.

xiaoxiang pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/incubator-nuttx.git

commit 532635129ce1e2d61859adf45eb668b840b0daed
Author: David Sidrane <Da...@NscDg.com>
AuthorDate: Thu Oct 28 14:08:03 2021 -0700

    imxrt:usdhc add proper support for dcache (wb)
---
 arch/arm/src/imxrt/imxrt_usdhc.c | 165 ++++++++++++++++++++++++++++++++++++---
 1 file changed, 153 insertions(+), 12 deletions(-)

diff --git a/arch/arm/src/imxrt/imxrt_usdhc.c b/arch/arm/src/imxrt/imxrt_usdhc.c
index 78cb199..ab754d1 100644
--- a/arch/arm/src/imxrt/imxrt_usdhc.c
+++ b/arch/arm/src/imxrt/imxrt_usdhc.c
@@ -101,6 +101,10 @@
 
 #define USDHC_MAX_WATERMARK         128
 
+/* Block size for multi-block transfers */
+
+#define SDMMC_MAX_BLOCK_SIZE        (512)
+
 /* Data transfer / Event waiting interrupt mask bits */
 
 #define USDHC_RESPERR_INTS          (USDHC_INT_CCE | USDHC_INT_CTOE | \
@@ -177,8 +181,13 @@ struct imxrt_dev_s
 
   volatile uint8_t xfrflags;          /* Used to synchronize SDIO and DMA
                                        * completion */
-  uint32_t *bufferend;                /* Far end of R/W buffer for cache
-                                       * invalidation */
+                                      /* DMA buffer for unaligned transfers */
+#if defined(CONFIG_ARMV7M_DCACHE)
+  uint32_t blocksize;                 /* Current block size */
+  uint8_t  rxbuffer[SDMMC_MAX_BLOCK_SIZE]
+                   __attribute__((aligned(ARMV7M_DCACHE_LINESIZE)));
+  bool     unaligned_rx;              /* buffer is not cache-line aligned */
+#endif
 #endif
 
   /* Card interrupt support for SDIO */
@@ -270,6 +279,9 @@ static void imxrt_dataconfig(struct imxrt_dev_s *priv, bool bwrite,
 #ifndef CONFIG_IMXRT_USDHC_DMA
 static void imxrt_transmit(struct imxrt_dev_s *priv);
 static void imxrt_receive(struct imxrt_dev_s *priv);
+#if defined(CONFIG_ARMV7M_DCACHE)
+static void imxrt_recvdma(struct imxrt_dev_s *priv);
+#endif
 #endif
 
 static void imxrt_eventtimeout(wdparm_t arg);
@@ -765,6 +777,18 @@ static void imxrt_dataconfig(struct imxrt_dev_s *priv, bool bwrite,
   regval |= timeout << USDHC_SYSCTL_DTOCV_SHIFT;
   putreg32(regval, priv->addr + IMXRT_USDHC_SYSCTL_OFFSET);
 
+#if defined(CONFIG_IMXRT_USDHC_DMA) && defined(CONFIG_ARMV7M_DCACHE)
+      /* If cache is enabled, and this is an unaligned receive,
+       * receive one block at a time to the internal buffer
+       */
+
+      if (!bwrite && priv->unaligned_rx)
+        {
+          DEBUGASSERT(priv->blocksize <= sizeof(priv->rxbuffer));
+          datalen = priv->blocksize;
+        }
+#endif
+
   /* Set the watermark level */
 
   /* Set the Read Watermark Level to the datalen to be read (limited to half
@@ -982,6 +1006,81 @@ static void imxrt_receive(struct imxrt_dev_s *priv)
 #endif
 
 /****************************************************************************
+ * Name: imxrt_recvdma
+ *
+ * Description:
+ *   Receive SDIO data in dma mode
+ *
+ * Input Parameters:
+ *   priv  - Instance of the SDMMC private state structure.
+ *
+ * Returned Value:
+ *   None
+ *
+ ****************************************************************************/
+
+#if defined(CONFIG_IMXRT_USDHC_DMA) && defined(CONFIG_ARMV7M_DCACHE)
+static void imxrt_recvdma(struct imxrt_dev_s *priv)
+{
+  unsigned int watermark;
+
+  if (priv->unaligned_rx)
+    {
+      /* If we are receiving multiple blocks to an unaligned buffers,
+       * we receive them one-by-one
+       */
+
+      /* Copy the received data to client buffer */
+
+      memcpy(priv->buffer, priv->rxbuffer, priv->blocksize);
+
+      /* Invalidate the cache before receiving next block */
+
+      up_invalidate_dcache((uintptr_t)priv->rxbuffer,
+                           (uintptr_t)priv->rxbuffer + priv->blocksize);
+
+      /* Update how much there is left to receive */
+
+      priv->remaining -= priv->blocksize;
+    }
+  else
+    {
+      /* In an aligned case, we have always received all blocks */
+
+      priv->remaining = 0;
+    }
+
+  if (priv->remaining == 0)
+    {
+      /* no data remaining, end the transfer */
+
+      imxrt_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
+    }
+  else
+    {
+      /* We end up here only in unaligned rx-buffers case, and are receiving
+       * the data one block at a time
+       */
+
+      /* Update where to receive the following block */
+
+      priv->buffer = (uint32_t *)((uintptr_t)priv->buffer + priv->blocksize);
+
+      watermark = (priv->blocksize + 3) >> 2;
+      if (watermark > (USDHC_MAX_WATERMARK / 2))
+        {
+          watermark = (USDHC_MAX_WATERMARK / 2);
+        }
+
+      /* Re-enable datapath and wait for next block */
+
+      putreg32(watermark << USDHC_WML_RD_SHIFT,
+               priv->addr + IMXRT_USDHC_WML_OFFSET);
+    }
+}
+
+#endif
+/****************************************************************************
  * Name: imxrt_eventtimeout
  *
  * Description:
@@ -1091,13 +1190,6 @@ static void imxrt_endtransfer(struct imxrt_dev_s *priv,
 
   priv->remaining = 0;
 
-#ifdef CONFIG_IMXRT_USDHC_DMA
-  /* DMA modified the buffer, so we need to flush its cache lines. */
-
-  up_invalidate_dcache((uintptr_t) priv->buffer,
-                       (uintptr_t) priv->bufferend);
-#endif
-
   /* Debug instrumentation */
 
   imxrt_sample(priv, SAMPLENDX_END_TRANSFER);
@@ -1184,8 +1276,11 @@ static int imxrt_interrupt(int irq, void *context, FAR void *arg)
       if ((pending & USDHC_INT_TC) != 0)
         {
           /* Terminate the transfer */
-
+#if defined(CONFIG_IMXRT_USDHC_DMA) && defined(CONFIG_ARMV7M_DCACHE)
+          imxrt_recvdma(priv);
+#else
           imxrt_endtransfer(priv, SDIOWAIT_TRANSFERDONE);
+#endif
         }
 
       /* ... data block send/receive CRC failure */
@@ -2123,6 +2218,8 @@ static void imxrt_blocksetup(FAR struct sdio_dev_s *dev,
 
   /* Configure block size for next transfer */
 
+  priv->blocksize = blocklen;
+
   putreg32(USDHC_BLKATTR_SIZE(blocklen) | USDHC_BLKATTR_CNT(nblocks),
            priv->addr + IMXRT_USDHC_BLKATTR_OFFSET);
 }
@@ -2860,13 +2957,34 @@ static int imxrt_dmarecvsetup(FAR struct sdio_dev_s *dev,
   imxrt_sampleinit();
   imxrt_sample(priv, SAMPLENDX_BEFORE_SETUP);
 
+#if defined(CONFIG_ARMV7M_DCACHE)
+  if (((uintptr_t)buffer & (ARMV7M_DCACHE_LINESIZE - 1)) != 0 ||
+       (buflen & (ARMV7M_DCACHE_LINESIZE - 1)) != 0)
+    {
+      /* The read buffer is not cache-line aligned. Read to an internal
+       * buffer instead.
+       */
+
+      up_invalidate_dcache((uintptr_t)priv->rxbuffer,
+                           (uintptr_t)priv->rxbuffer + priv->blocksize);
+
+      priv->unaligned_rx = true;
+    }
+  else
+    {
+      up_invalidate_dcache((uintptr_t)buffer,
+                           (uintptr_t)buffer + buflen);
+
+      priv->unaligned_rx = false;
+    }
+#endif
+
   /* Save the destination buffer information for use by the interrupt
    * handler
    */
 
   priv->buffer = (uint32_t *)buffer;
   priv->remaining = buflen;
-  priv->bufferend = (uint32_t *)(buffer + buflen);
 
   /* Then set up the SDIO data path */
 
@@ -2875,7 +2993,18 @@ static int imxrt_dmarecvsetup(FAR struct sdio_dev_s *dev,
   /* Configure the RX DMA */
 
   imxrt_configxfrints(priv, USDHC_DMADONE_INTS);
-  putreg32((uint32_t) buffer, priv->addr + IMXRT_USDHC_DSADDR_OFFSET);
+#if defined(CONFIG_ARMV7M_DCACHE)
+  if (priv->unaligned_rx)
+    {
+      putreg32((uint32_t) priv->rxbuffer,
+               priv->addr + IMXRT_USDHC_DSADDR_OFFSET);
+    }
+  else
+#endif
+    {
+      putreg32((uint32_t) priv->buffer,
+               priv->addr + IMXRT_USDHC_DSADDR_OFFSET);
+    }
 
   /* Sample the register state */
 
@@ -2918,6 +3047,18 @@ static int imxrt_dmasendsetup(FAR struct sdio_dev_s *dev,
 
   /* Save the source buffer information for use by the interrupt handler */
 
+#if defined(CONFIG_ARMV7M_DCACHE)
+  priv->unaligned_rx = false;
+
+  /* Flush cache to physical memory when not in DTCM memory */
+
+#  if !defined(CONFIG_ARMV7M_DCACHE_WRITETHROUGH)
+    {
+      up_clean_dcache((uintptr_t)buffer, (uintptr_t)buffer + buflen);
+    }
+
+#  endif
+#endif
   priv->buffer    = (uint32_t *) buffer;
   priv->remaining = buflen;