summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-09-23 13:54:32 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-09-23 13:54:32 -0600
commit7e12770b3e30996db6660b99fd3df393aa1918de (patch)
tree77ef7f6126194ab20ab0286f75aee51f923fd1ab
parent26893da5a3dc13c01c65dd11d638b8a71ed0a97f (diff)
downloadnuttx-7e12770b3e30996db6660b99fd3df393aa1918de.tar.gz
nuttx-7e12770b3e30996db6660b99fd3df393aa1918de.tar.bz2
nuttx-7e12770b3e30996db6660b99fd3df393aa1918de.zip
SAMA5 HSMCI: Disable TX DMA. it is not reliable
-rw-r--r--nuttx/ChangeLog2
-rwxr-xr-xnuttx/arch/arm/src/sama5/chip/sam_dmac.h2
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_hsmci.h1
-rw-r--r--nuttx/arch/arm/src/sama5/sam_hsmci.c177
4 files changed, 156 insertions, 26 deletions
diff --git a/nuttx/ChangeLog b/nuttx/ChangeLog
index 589dd5c86..ca8467099 100644
--- a/nuttx/ChangeLog
+++ b/nuttx/ChangeLog
@@ -5619,4 +5619,6 @@
the 0xff value (2013-9-22).
* configs/zkit-arm-1769: LED1 is not user controllable after booting.
From Rashid Fatah (2013-9-23).
+ * arch/arm/src/sama5/sam_hsmci.c: TX DMA disabled. It is just not
+ reliable. No idea why. RX DMA is still used (2013-9-23).
diff --git a/nuttx/arch/arm/src/sama5/chip/sam_dmac.h b/nuttx/arch/arm/src/sama5/chip/sam_dmac.h
index 68a0cbd46..67b47f405 100755
--- a/nuttx/arch/arm/src/sama5/chip/sam_dmac.h
+++ b/nuttx/arch/arm/src/sama5/chip/sam_dmac.h
@@ -829,7 +829,7 @@ struct dma_crc16_linklist_s
uint32_t ctrla; /* 8 Control A value */
uint32_t ctrlb; /* 12 Control B value */
uint32_t dscr; /* 16 Next descriptor address */
- uint32_t crc16; /* 10 Next descriptor address */
+ uint32_t crc16; /* 20 CRC */
};
/****************************************************************************************
diff --git a/nuttx/arch/arm/src/sama5/chip/sam_hsmci.h b/nuttx/arch/arm/src/sama5/chip/sam_hsmci.h
index 9cc286aea..61c5d77a7 100644
--- a/nuttx/arch/arm/src/sama5/chip/sam_hsmci.h
+++ b/nuttx/arch/arm/src/sama5/chip/sam_hsmci.h
@@ -348,7 +348,6 @@
#define HSMCI_WPSR_VSRC_SHIFT (8) /* Bits 8-23: Write Protection Violation Source */
#define HSMCI_WPSR_VSRC_MASK (0xffff << HSMCI_WPSR_VSRC_SHIFT)
-
/****************************************************************************************
* Public Types
****************************************************************************************/
diff --git a/nuttx/arch/arm/src/sama5/sam_hsmci.c b/nuttx/arch/arm/src/sama5/sam_hsmci.c
index e57ec0438..93c6fec98 100644
--- a/nuttx/arch/arm/src/sama5/sam_hsmci.c
+++ b/nuttx/arch/arm/src/sama5/sam_hsmci.c
@@ -118,6 +118,22 @@
# endif
#endif
+/* TX-DMA is not reliable. Often, the TX DMA will hang after transferring 64 bytes or so.
+ * I don't have any clue why at the moment. This option suppresses TX DMA (only).
+ */
+
+#define HSCMI_NOTXDMA 1
+
+/* There are two ways to DMA:
+ *
+ * (1) Use the FIFO address, incrementing the address on the HSMCI side
+ * (2) Use the TDR/RDR address with no address increment
+ *
+ * Both work.
+ */
+
+#undef HSCMI_FIFODMA
+
/* Timing */
#define HSMCI_CMDTIMEOUT (100000)
@@ -127,19 +143,36 @@
#define HSMCI_DTIMER_DATATIMEOUT (0x000fffff)
-/* DMA configuration flags
+/* DMA configuration flags. There are two ways to to this:
+ *
+ * (1) Use the FIFO address, incrementing the address on the HSMCI side
+ * (2) Use the TDR/RDR address with no address increment
+ *
* REVISIT: Is memory always on IF0?
*/
#define HSMCI_DMA_CHKSIZE HSMCI_DMA_CHKSIZE_1
-#define DMA_FLAGS(pid) \
- (((pid) << DMACH_FLAG_PERIPHPID_SHIFT) | DMACH_FLAG_PERIPHAHB_AHB_IF2 | \
- DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH | \
- DMACH_FLAG_PERIPHWIDTH_32BITS | DMACH_FLAG_PERIPHCHUNKSIZE_1 | \
- ((0x3f) << DMACH_FLAG_MEMPID_SHIFT) | DMACH_FLAG_MEMAHB_AHB_IF0 | \
- DMACH_FLAG_MEMWIDTH_32BITS | DMACH_FLAG_MEMINCREMENT | \
- DMACH_FLAG_MEMCHUNKSIZE_4)
+#ifdef HSCMI_FIFODMA
+# define DMA_FLAGS(pid) \
+ (((pid) << DMACH_FLAG_PERIPHPID_SHIFT) | DMACH_FLAG_PERIPHAHB_AHB_IF2 | \
+ DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH | \
+ DMACH_FLAG_PERIPHWIDTH_32BITS | DMACH_FLAG_PERIPHINCREMENT | \
+ DMACH_FLAG_PERIPHCHUNKSIZE_1 | \
+ ((0x3f) << DMACH_FLAG_MEMPID_SHIFT) | DMACH_FLAG_MEMAHB_AHB_IF0 | \
+ DMACH_FLAG_MEMWIDTH_32BITS | DMACH_FLAG_MEMINCREMENT | \
+ DMACH_FLAG_MEMCHUNKSIZE_4)
+
+#else
+# define DMA_FLAGS(pid) \
+ (((pid) << DMACH_FLAG_PERIPHPID_SHIFT) | DMACH_FLAG_PERIPHAHB_AHB_IF2 | \
+ DMACH_FLAG_PERIPHH2SEL | DMACH_FLAG_PERIPHISPERIPH | \
+ DMACH_FLAG_PERIPHWIDTH_32BITS | DMACH_FLAG_PERIPHCHUNKSIZE_1 | \
+ ((0x3f) << DMACH_FLAG_MEMPID_SHIFT) | DMACH_FLAG_MEMAHB_AHB_IF0 | \
+ DMACH_FLAG_MEMWIDTH_32BITS | DMACH_FLAG_MEMINCREMENT | \
+ DMACH_FLAG_MEMCHUNKSIZE_4)
+
+#endif
/* Status errors:
*
@@ -261,6 +294,11 @@
# define DEBUG_NCMDSAMPLES 2
#endif
+/* Some semi-standard definitions */
+
+#define MAX(a,b) (((a) > (b)) ? (a) : (b))
+#define MIN(a,b) (((a) < (b)) ? (a) : (b))
+
/****************************************************************************
* Private Types
****************************************************************************/
@@ -315,7 +353,8 @@ struct sam_dev_s
volatile sdio_eventset_t wkupevent; /* The event that caused the wakeup */
WDOG_ID waitwdog; /* Watchdog that handles event timeouts */
uint8_t hsmci; /* HSMCI (0, 1, or 2) */
- bool dmabusy; /* TRUE: DMA is in progress */
+ bool dmabusy; /* TRUE: DMA transfer is in progress (not used) */
+ bool txbusy; /* TRUE: TX transfer is in progress (for delay calculation) */
/* Callback support */
@@ -350,6 +389,7 @@ struct sam_dev_s
bool cmdinitialized;
#endif
#ifdef CONFIG_SAMA5_HSMCI_XFRDEBUG
+ uint8_t smplset;
struct sam_xfrregs_s xfrsamples[DEBUG_NDMASAMPLES];
#endif
#ifdef CONFIG_SAMA5_HSMCI_CMDDEBUG
@@ -935,6 +975,7 @@ static void sam_xfrsample(struct sam_dev_s *priv, int index)
sam_dmasample(priv->dma, &regs->dma);
#endif
sam_hsmcisample(priv, &regs->hsmci);
+ priv->smplset |= (1 << index);
}
#endif
@@ -949,6 +990,7 @@ static void sam_xfrsample(struct sam_dev_s *priv, int index)
#ifdef CONFIG_SAMA5_HSMCI_XFRDEBUG
static void sam_xfrsampleinit(struct sam_dev_s *priv)
{
+ priv->smplset = 0;
memset(priv->xfrsamples, 0xff,
DEBUG_NDMASAMPLES * sizeof(struct sam_xfrregs_s));
@@ -992,20 +1034,40 @@ static void sam_xfrdump(struct sam_dev_s *priv)
if (priv->xfrinitialized)
#endif
{
- sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_BEFORE_SETUP],
- "Before setup");
+ if ((priv->smplset & (1 << SAMPLENDX_BEFORE_SETUP)) != 0)
+ {
+ sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_BEFORE_SETUP],
+ "Before setup");
+ }
#ifdef CONFIG_DEBUG_DMA
- sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_BEFORE_ENABLE],
- "Before DMA enable");
+ if ((priv->smplset & (1 << SAMPLENDX_BEFORE_SETUP)) != 0)
+ {
+ sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_BEFORE_ENABLE],
+ "Before DMA enable");
+ }
#endif
- sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_AFTER_SETUP],
- "After setup");
- sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_END_TRANSFER],
- "End of transfer");
+
+ if ((priv->smplset & (1 << SAMPLENDX_BEFORE_SETUP)) != 0)
+ {
+ sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_AFTER_SETUP],
+ "After setup");
+ }
+
+ if ((priv->smplset & (1 << SAMPLENDX_BEFORE_SETUP)) != 0)
+ {
+ sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_END_TRANSFER],
+ "End of transfer");
+ }
+
#ifdef CONFIG_DEBUG_DMA
- sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_DMA_CALLBACK],
- "DMA Callback");
+ if ((priv->smplset & (1 << SAMPLENDX_BEFORE_SETUP)) != 0)
+ {
+ sam_xfrdumpone(priv, &priv->xfrsamples[SAMPLENDX_DMA_CALLBACK],
+ "DMA Callback");
+ }
#endif
+
+ priv->smplset = 0;
#ifdef CONFIG_SAMA5_HSMCI_CMDDEBUG
priv->xfrinitialized = false;
#endif
@@ -1233,7 +1295,7 @@ static void sam_endtransfer(struct sam_dev_s *priv,
/* Make sure that the DMA is stopped (it will be stopped automatically
* on normal transfers, but not necessarily when the transfer terminates
- * on an error condition.
+ * on an error condition).
*/
sam_dmastop(priv->dma);
@@ -1526,6 +1588,7 @@ static void sam_reset(FAR struct sdio_dev_s *dev)
priv->waitmask = 0; /* Interrupt enables for event waiting */
priv->wkupevent = 0; /* The event that caused the wakeup */
priv->dmabusy = false; /* No DMA in progress */
+ priv->txbusy = false; /* No TX in progress */
wd_cancel(priv->waitwdog); /* Cancel any timeouts */
/* Interrupt mode data transfer support */
@@ -1949,6 +2012,7 @@ static int sam_cancel(FAR struct sdio_dev_s *dev)
sam_dmastop(priv->dma);
priv->dmabusy = false;
+ priv->txbusy = false;
/* Disable the DMA handshaking */
@@ -2366,10 +2430,11 @@ static sdio_eventset_t sam_eventwait(FAR struct sdio_dev_s *dev,
* currently seeing some additional delays when DMA is used.
*/
-#warning REVISIT: This should not be necessary
- if (priv->dmabusy)
+ if (priv->txbusy)
{
- timeout += 500;
+ /* TX transfers can be VERY long in the worst case */
+
+ timeout = MAX(5000, timeout);
}
delay = (timeout + (MSEC_PER_TICK-1)) / MSEC_PER_TICK;
@@ -2542,7 +2607,11 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
* in RAM.
*/
+#ifdef HSCMI_FIFODMA
+ paddr = hsmci_physregaddr(priv, SAM_HSMCI_FIFO_OFFSET);
+#else
paddr = hsmci_physregaddr(priv, SAM_HSMCI_RDR_OFFSET);
+#endif
maddr = sam_physramaddr((uintptr_t)buffer);
/* Setup register sampling */
@@ -2562,6 +2631,7 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
/* Start the DMA */
priv->dmabusy = true;
+ priv->txbusy = false;
sam_dmastart(priv->dma, sam_dmacallback, priv);
/* Configure transfer-related interrupts. Transfer interrupts are not
@@ -2596,6 +2666,7 @@ static int sam_dmarecvsetup(FAR struct sdio_dev_s *dev, FAR uint8_t *buffer,
static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
FAR const uint8_t *buffer, size_t buflen)
{
+#ifndef HSCMI_NOTXDMA
struct sam_dev_s *priv = (struct sam_dev_s *)dev;
uint32_t paddr;
uint32_t maddr;
@@ -2605,7 +2676,11 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
/* Physical address of the HSCMI TDR registr */
+#ifdef HSCMI_FIFODMA
+ paddr = hsmci_physregaddr(priv, SAM_HSMCI_FIFO_OFFSET);
+#else
paddr = hsmci_physregaddr(priv, SAM_HSMCI_TDR_OFFSET);
+#endif
maddr = sam_physramaddr((uintptr_t)buffer);
/* Setup register sampling */
@@ -2625,6 +2700,7 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
/* Start the DMA */
priv->dmabusy = true;
+ priv->txbusy = true;
sam_dmastart(priv->dma, sam_dmacallback, priv);
/* Configure transfer-related interrupts. Transfer interrupts are not
@@ -2633,7 +2709,60 @@ static int sam_dmasendsetup(FAR struct sdio_dev_s *dev,
*/
sam_xfrsample(priv, SAMPLENDX_AFTER_SETUP);
- sam_configxfrints(priv, HSMCI_DMARECV_INTS);
+ sam_configxfrints(priv, HSMCI_DMASEND_INTS);
+
+#else
+ struct sam_dev_s *priv = (struct sam_dev_s *)dev;
+ unsigned int nwords;
+ const uint32_t *ptr;
+ uint32_t sr;
+
+ DEBUGASSERT(priv != NULL && buffer != NULL && buflen > 0);
+ DEBUGASSERT(((uint32_t)buffer & 3) == 0);
+
+ /* Disable DMA handshaking */
+
+ sam_putreg(priv, 0, SAM_HSMCI_DMA_OFFSET);
+ sam_configxfrints(priv, HSMCI_DMASEND_INTS);
+
+ priv->dmabusy = false;
+ priv->txbusy = true;
+
+ /* Nullify register sampling */
+
+ sam_xfrsampleinit(priv);
+
+ /* Copy each word to the TX FIFO
+ *
+ * REVISIT: If TX data underruns occur, then it may be necessary to
+ * disable pre-emption around this loop.
+ */
+
+ nwords = (buflen + 3) >> 2;
+ ptr = (const uint32_t *)buffer;
+
+ while (nwords > 0)
+ {
+ /* Check the HSMCI status */
+
+ sr = sam_getreg(priv, SAM_HSMCI_SR_OFFSET);
+ if ((sr & HSMCI_DATA_DMASEND_ERRORS) != 0)
+ {
+ /* Some fatal error has occurred */
+
+ fdbg("ERROR: sr %08x\n", sr);
+ return -EIO;
+ }
+ else if ((sr & HSMCI_INT_TXRDY) != 0)
+ {
+ /* TXRDY -- transfer another word */
+
+ sam_putreg(priv, *ptr++, SAM_HSMCI_TDR_OFFSET);
+ nwords--;
+ }
+ }
+
+#endif
return OK;
}