diff options
author | Gregory Nutt <gnutt@nuttx.org> | 2013-09-23 13:54:32 -0600 |
---|---|---|
committer | Gregory Nutt <gnutt@nuttx.org> | 2013-09-23 13:54:32 -0600 |
commit | 7e12770b3e30996db6660b99fd3df393aa1918de (patch) | |
tree | 77ef7f6126194ab20ab0286f75aee51f923fd1ab | |
parent | 26893da5a3dc13c01c65dd11d638b8a71ed0a97f (diff) | |
download | nuttx-7e12770b3e30996db6660b99fd3df393aa1918de.tar.gz nuttx-7e12770b3e30996db6660b99fd3df393aa1918de.tar.bz2 nuttx-7e12770b3e30996db6660b99fd3df393aa1918de.zip |
SAMA5 HSMCI: Disable TX DMA. it is not reliable
-rw-r--r-- | nuttx/ChangeLog | 2 | ||||
-rwxr-xr-x | nuttx/arch/arm/src/sama5/chip/sam_dmac.h | 2 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/chip/sam_hsmci.h | 1 | ||||
-rw-r--r-- | nuttx/arch/arm/src/sama5/sam_hsmci.c | 177 |
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, ®s->dma); #endif sam_hsmcisample(priv, ®s->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; } |