From 006db0be7cb6e24d71fe3b3fe6b472640fef464b Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Mon, 2 Dec 2013 17:15:57 -0600 Subject: [PATCH] SAMA5 NAND: Can't DMA using DMAC1. Add lots of NAND DMA debug instrumentation --- arch/arm/src/sama5/Kconfig | 15 ++++- arch/arm/src/sama5/sam_dmac.c | 3 +- arch/arm/src/sama5/sam_nand.c | 123 +++++++++++++++++++++++++++++++++- arch/arm/src/sama5/sam_nand.h | 41 +++++++++--- arch/arm/src/sama5/sam_ssc.c | 2 +- 5 files changed, 169 insertions(+), 15 deletions(-) diff --git a/arch/arm/src/sama5/Kconfig b/arch/arm/src/sama5/Kconfig index b5b3d8a651..0f5660631f 100644 --- a/arch/arm/src/sama5/Kconfig +++ b/arch/arm/src/sama5/Kconfig @@ -3340,9 +3340,10 @@ if SAMA5_HAVE_NAND config SAMA5_NAND_DMA bool "NAND DMA Transfers" default y - depends on SAMA5_DMAC0 || SAMA5_DMAC1 + depends on SAMA5_DMAC0 ---help--- - Use DMA to perform NAND data transfers (highly recommended) + Use DMA to perform NAND data transfers. NOTE that DMAC0 must be + selected (DMAC1 cannot access NFC SRAM). (highly recommended) config SAMA5_NAND_READYBUSY bool "NAND Ready/Busy" @@ -3431,6 +3432,16 @@ config SAMA5_PMECC_GALOIS_CUSTOM endif # SAMA5_HAVE_PMECC +config SAMA5_NAND_DMADEBUG + bool "NAND DMA transfer debug" + depends on SAMA5_NAND_DMA && DEBUG && DEBUG_DMA + default n + ---help--- + Enable special debug instrumentation analyze NAND DMA data transfers. + This logic is as non-invasive as possible: It samples DMA + registers at key points in the data transfer and then dumps all of + the registers at the end of the transfer. + config SAMA5_NAND_REGDEBUG bool "Register-Level NAND Debug" default n diff --git a/arch/arm/src/sama5/sam_dmac.c b/arch/arm/src/sama5/sam_dmac.c index ff8a13735c..33541943cc 100644 --- a/arch/arm/src/sama5/sam_dmac.c +++ b/arch/arm/src/sama5/sam_dmac.c @@ -1807,7 +1807,8 @@ static int sam_dmac_interrupt(struct sam_dmac_s *dmac) { /* Yes... Terminate the transfer with an error? */ - sam_dmaterminate(dmach, -EIO); + dmalldbg("ERROR: DMA failed: %08x\n", regval); + sam_dmaterminate(dmach, -EIO); } /* Is the transfer complete? */ diff --git a/arch/arm/src/sama5/sam_nand.c b/arch/arm/src/sama5/sam_nand.c index 7d23e0b022..6fecea58f3 100644 --- a/arch/arm/src/sama5/sam_nand.c +++ b/arch/arm/src/sama5/sam_nand.c @@ -195,6 +195,18 @@ static int hsmc_interrupt(int irq, void *context); /* DMA Helpers */ #ifdef CONFIG_SAMA5_NAND_DMA +#ifdef CONFIG_SAMA5_NAND_DMADEBUG +static void nand_dma_sampleinit(struct sam_nandcs_s *priv); +# define nand_dma_sample(p,i) sam_dmasample((p)->dma, &(p)->dmaregs[i]) +static void nand_dma_sampledone(struct sam_nandcs_s *priv, int result); + +#else +# define nand_dma_sampleinit(p) +# define nand_dma_sample(p,i) +# define nand_dma_sampledone(p,r) + +#endif + static int nand_wait_dma(struct sam_nandcs_s *priv); static void nand_dmacallback(DMA_HANDLE handle, void *arg, int result); static int nand_dma_read(struct sam_nandcs_s *priv, @@ -202,7 +214,7 @@ static int nand_dma_read(struct sam_nandcs_s *priv, uint32_t dmaflags); static int nand_dma_write(struct sam_nandcs_s *priv, uintptr_t vsrc, uintptr_t vdest, size_t nbytes, - uint32_t dmaflags) + uint32_t dmaflags); #endif /* Raw Data Transfer Helpers */ @@ -1121,6 +1133,92 @@ static int hsmc_interrupt(int irq, void *context) } #endif /* CONFIG_SAMA5_NAND_HSMCINTERRUPTS */ +/**************************************************************************** + * Name: nand_dma_sampleinit + * + * Description: + * Initialize sampling of DMA registers (if CONFIG_SAMA5_NAND_DMADEBUG) + * + * Input Parameters: + * priv - Lower-half, private NAND FLASH device state + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_NAND_DMADEBUG +static void nand_dma_sampleinit(struct sam_nandcs_s *priv) +{ + /* Put contents of register samples into a known state */ + + memset(priv->dmaregs, 0xff, DMA_NSAMPLES * sizeof(struct sam_dmaregs_s)); + + /* Then get the initial samples */ + + sam_dmasample(priv->dma, &priv->dmaregs[DMA_INITIAL]); +} +#endif + +/**************************************************************************** + * Name: nand_dma_sampledone + * + * Description: + * Dump sampled RX DMA registers + * + * Input Parameters: + * priv - Lower-half, private NAND FLASH device state + * + * Returned Value: + * None + * + ****************************************************************************/ + +#ifdef CONFIG_SAMA5_NAND_DMADEBUG +static void nand_dma_sampledone(struct sam_nandcs_s *priv, int result) +{ + lldbg("result: %d\n", result); + + /* Sample the final registers */ + + sam_dmasample(priv->dma, &priv->dmaregs[DMA_END_TRANSFER]); + + /* Then dump the sampled DMA registers */ + /* Initial register values */ + + sam_dmadump(priv->dma, &priv->dmaregs[DMA_INITIAL], "Initial Registers"); + + /* Register values after DMA setup */ + + sam_dmadump(priv->dma, &priv->dmaregs[DMA_AFTER_SETUP], "After DMA Setup"); + + /* Register values after DMA start */ + + sam_dmadump(priv->dma, &priv->dmaregs[DMA_AFTER_START], "After DMA Start"); + + /* Register values at the time of the TX and RX DMA callbacks + * -OR- DMA timeout. + * + * If the DMA timedout, then there will not be any RX DMA + * callback samples. There is probably no TX DMA callback + * samples either, but we don't know for sure. + */ + +#if 0 /* No timeout */ + if (result == -ETIMEDOUT || result == -EINTR) + { + sam_dmadump(priv->dma, &priv->dmaregs[DMA_TIMEOUT], "At DMA timeout"); + } + else +#endif + { + sam_dmadump(priv->dma, &priv->dmaregs[DMA_CALLBACK], "At DMA callback"); + } + + sam_dmadump(priv->dma, &priv->dmaregs[DMA_END_TRANSFER], "At End-of-Transfer"); +} +#endif + /**************************************************************************** * Name: nand_wait_dma * @@ -1170,6 +1268,7 @@ static void nand_dmacallback(DMA_HANDLE handle, void *arg, int result) struct sam_nandcs_s *priv = (struct sam_nandcs_s *)arg; DEBUGASSERT(priv); + nand_dma_sample(priv, DMA_CALLBACK); /* Wake up the thread that is waiting for the DMA result */ @@ -1211,6 +1310,10 @@ static int nand_dma_read(struct sam_nandcs_s *priv, fvdbg("vsrc=%08x vdest=%08x nbytes=%d\n", (int)vsrc, (int)vdest, (int)nbytes); + /* Initialize sampling */ + + nand_dma_sampleinit(priv); + /* Invalidate the destination memory buffer before performing the DMA (so * that nothing gets flushed later, corrupting the DMA transfer, and so * that memory will be re-cached after the DMA completes). @@ -1240,12 +1343,15 @@ static int nand_dma_read(struct sam_nandcs_s *priv, return ret; } + nand_dma_sample(priv, DMA_AFTER_SETUP); + /* Start the DMA */ priv->dmadone = false; priv->result = -EBUSY; sam_dmastart(priv->dma, nand_dmacallback, priv); + nand_dma_sample(priv, DMA_AFTER_START); /* Wait for the DMA to complete */ @@ -1255,6 +1361,8 @@ static int nand_dma_read(struct sam_nandcs_s *priv, fdbg("ERROR: DMA failed: %d\n", ret); } + nand_dma_sample(priv, DMA_END_TRANSFER); + nand_dma_sampledone(priv, ret); return ret; } #endif @@ -1288,6 +1396,10 @@ static int nand_dma_write(struct sam_nandcs_s *priv, DEBUGASSERT(priv->dma); + /* Initialize sampling */ + + nand_dma_sampleinit(priv); + /* Clean the D-Cache associated with the source data buffer so that all of * the data to be transferred lies in physical memory */ @@ -1316,12 +1428,15 @@ static int nand_dma_write(struct sam_nandcs_s *priv, return ret; } + nand_dma_sample(priv, DMA_AFTER_SETUP); + /* Start the DMA */ priv->dmadone = false; priv->result = -EBUSY; sam_dmastart(priv->dma, nand_dmacallback, priv); + nand_dma_sample(priv, DMA_AFTER_START); /* Wait for the DMA to complete */ @@ -1331,6 +1446,8 @@ static int nand_dma_write(struct sam_nandcs_s *priv, fdbg("ERROR: DMA failed: %d\n", ret); } + nand_dma_sample(priv, DMA_END_TRANSFER); + nand_dma_sampledone(priv, ret); return ret; } #endif @@ -2871,8 +2988,8 @@ struct mtd_dev_s *sam_nand_initialize(int cs) /* Initialize the NAND hardware for this CS */ /* Perform board-specific SMC intialization for this CS. This should include: * - * 1. Enable clocking to the HSMC - * 2. Configuration timing for the HSMC CS + * 1. Enabling of clocking to the HSMC + * 2. Configuration of timing for the HSMC NAND CS * 3. Configuration of PIO pins * * Other than enabling the HSMC, these are all things that the board-cognizant diff --git a/arch/arm/src/sama5/sam_nand.h b/arch/arm/src/sama5/sam_nand.h index 505f33050d..01a1411074 100644 --- a/arch/arm/src/sama5/sam_nand.h +++ b/arch/arm/src/sama5/sam_nand.h @@ -60,15 +60,16 @@ * Pre-processor Definitions ****************************************************************************/ /* Configuration ************************************************************/ -/* DMA */ +/* DMA. DMA support requires that DMAC0 be enabled. According to + * "Table 15-2. SAMA5 Master to Slave Access", DMAC1 does not have access + * to NFC SRAM. + */ #ifdef CONFIG_SAMA5_NAND_DMA -# if defined(CONFIG_SAMA5_DMAC1) -# define NAND_DMAC 1 -# elif defined(CONFIG_SAMA5_DMAC0) +# ifdef CONFIG_SAMA5_DMAC0 # define NAND_DMAC 0 # else -# error "A DMA controller must be enabled to perform DMA transfers" +# error "DMA controller 0 (DMAC0) must be enabled to perform DMA transfers" # undef CONFIG_SAMA5_NAND_DMA # endif #endif @@ -235,12 +236,17 @@ /* Debug */ -#if !defined(CONFIG_DEBUG) +#if !defined(CONFIG_DEBUG) || !defined(CONFIG_DEBUG_FS) # undef CONFIG_DEBUG_FS +# undef CONFIG_SAMA5_NAND_DMADEBUG # undef CONFIG_SAMA5_NAND_REGDEBUG # undef CONFIG_SAMA5_NAND_DUMP #endif +#if !defined(CONFIG_SAMA5_NAND_DMA) || !defined(CONFIG_DEBUG_DMA) +# undef CONFIG_SAMA5_NAND_DMADEBUG +#endif + /* An early version of this driver used SMC interrupts to determine when * NAND commands completed, transfers completed, and RB edges occurred. It * turns out that those interrupts occurred so quickly that some really @@ -252,6 +258,16 @@ #undef CONFIG_SAMA5_NAND_HSMCINTERRUPTS +/* DMA Debug */ + +#define DMA_INITIAL 0 +#define DMA_AFTER_SETUP 1 +#define DMA_AFTER_START 2 +#define DMA_CALLBACK 3 +#define DMA_TIMEOUT 3 /* No timeout */ +#define DMA_END_TRANSFER 4 +#define DMA_NSAMPLES 5 + /**************************************************************************** * Public Types ****************************************************************************/ @@ -284,6 +300,10 @@ struct sam_nandcs_s sem_t waitsem; /* Used to wait for DMA done */ DMA_HANDLE dma; /* DMA channel assigned to this CS */ int result; /* The result of the DMA */ + +#ifdef CONFIG_SAMA5_NAND_DMADEBUG + struct sam_dmaregs_s dmaregs[DMA_NSAMPLES]; +#endif #endif }; @@ -374,7 +394,12 @@ struct mtd_dev_s *sam_nand_initialize(int cs); * If CONFIG_SAMA5_BOOT_CS3FLASH is defined, then NAND FLASH support is * enabled. This function provides the board-specific implementation of * the logic to reprogram the SMC to support NAND FLASH on the specified - * CS. + * CS. As a minimum, this board-specific initialization should do the + * following: + * + * 1. Enable clocking to the HSMC + * 2. Configure timing for the HSMC CS + * 3. Configure NAND PIO pins * * Input Parameters: * cs - Chip select number (in the event that multiple NAND devices @@ -411,7 +436,7 @@ bool board_nand_busy(int cs); #endif /**************************************************************************** - * Name: board_nandflash_config + * Name: board_nand_ce * * Description: * Must be provided if the board logic supports and interface to control diff --git a/arch/arm/src/sama5/sam_ssc.c b/arch/arm/src/sama5/sam_ssc.c index 9420af3500..f578a8dee8 100644 --- a/arch/arm/src/sama5/sam_ssc.c +++ b/arch/arm/src/sama5/sam_ssc.c @@ -901,7 +901,7 @@ static void ssc_buf_initialize(struct sam_ssc_s *priv) * Name: ssc_dma_sampleinit * * Description: - * Initialize sampling of RX DMA registers (if CONFIG_SAMA5_SSC_DMADEBUG) + * Initialize sampling of DMA registers (if CONFIG_SAMA5_SSC_DMADEBUG) * * Input Parameters: * priv - SSC state instance