SAMA5 NAND: Don't use HSMC interrupts. They occur to quickly and cause mysterious race conditions

This commit is contained in:
Gregory Nutt 2013-11-29 11:52:47 -06:00
parent 49bcf2da73
commit 4b8ba0cddc
2 changed files with 66 additions and 1 deletions

View File

@ -662,6 +662,7 @@ static void nand_nfc_configure(struct sam_nandcs_s *priv, uint8_t mode,
static void nand_wait_cmddone(struct sam_nandcs_s *priv)
{
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
irqstate_t flags;
int ret;
@ -683,6 +684,12 @@ static void nand_wait_cmddone(struct sam_nandcs_s *priv)
g_nand.cmddone = false;
nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_CMDDONE);
irqrestore(flags);
#else
/* Poll for the CMDDONE event */
while((nand_getreg(SAM_HSMC_SR) & HSMC_NFCINT_CMDDONE) == 0);
#endif
}
/****************************************************************************
@ -701,6 +708,7 @@ static void nand_wait_cmddone(struct sam_nandcs_s *priv)
static void nand_setup_cmddone(struct sam_nandcs_s *priv)
{
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
irqstate_t flags;
/* Clear all pending interrupts. This must be done with interrupts
@ -718,6 +726,11 @@ static void nand_setup_cmddone(struct sam_nandcs_s *priv)
nand_putreg(SAM_HSMC_IER, HSMC_NFCINT_CMDDONE);
irqrestore(flags);
#else
/* Just clear any pending CMDDONE status */
nand_getreg(SAM_HSMC_SR);
#endif
}
/****************************************************************************
@ -736,6 +749,7 @@ static void nand_setup_cmddone(struct sam_nandcs_s *priv)
static void nand_wait_xfrdone(struct sam_nandcs_s *priv)
{
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
irqstate_t flags;
int ret;
@ -757,6 +771,12 @@ static void nand_wait_xfrdone(struct sam_nandcs_s *priv)
g_nand.xfrdone = false;
nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_XFRDONE);
irqrestore(flags);
#else
/* Poll for the XFRDONE event */
while((nand_getreg(SAM_HSMC_SR) & HSMC_NFCINT_XFRDONE) == 0);
#endif
}
/****************************************************************************
@ -775,6 +795,7 @@ static void nand_wait_xfrdone(struct sam_nandcs_s *priv)
static void nand_setup_xfrdone(struct sam_nandcs_s *priv)
{
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
irqstate_t flags;
/* Clear all pending interrupts. This must be done with interrupts
@ -792,6 +813,11 @@ static void nand_setup_xfrdone(struct sam_nandcs_s *priv)
nand_putreg(SAM_HSMC_IER, HSMC_NFCINT_XFRDONE);
irqrestore(flags);
#else
/* Just clear any pending XFRDONE status */
nand_getreg(SAM_HSMC_SR);
#endif
}
/****************************************************************************
@ -810,6 +836,7 @@ static void nand_setup_xfrdone(struct sam_nandcs_s *priv)
static void nand_wait_rbedge(struct sam_nandcs_s *priv)
{
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
irqstate_t flags;
int ret;
@ -831,6 +858,12 @@ static void nand_wait_rbedge(struct sam_nandcs_s *priv)
g_nand.rbedge = false;
nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_RBEDGE0);
irqrestore(flags);
#else
/* Poll for the RBEDGE0 event */
while((nand_getreg(SAM_HSMC_SR) & HSMC_NFCINT_RBEDGE0) == 0);
#endif
}
/****************************************************************************
@ -849,6 +882,7 @@ static void nand_wait_rbedge(struct sam_nandcs_s *priv)
static void nand_setup_rbedge(struct sam_nandcs_s *priv)
{
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
irqstate_t flags;
/* Clear all pending interrupts. This must be done with interrupts
@ -866,6 +900,12 @@ static void nand_setup_rbedge(struct sam_nandcs_s *priv)
nand_putreg(SAM_HSMC_IER, HSMC_NFCINT_RBEDGE0);
irqrestore(flags);
#else
/* Just clear any pending RBEDGE0 status */
nand_getreg(SAM_HSMC_SR);
#endif
}
/****************************************************************************
@ -882,6 +922,7 @@ static void nand_setup_rbedge(struct sam_nandcs_s *priv)
*
****************************************************************************/
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
static int hsmc_interrupt(int irq, void *context)
{
uint32_t sr = nand_getreg(SAM_HSMC_SR);
@ -924,6 +965,7 @@ static int hsmc_interrupt(int irq, void *context)
return OK;
}
#endif /* CONFIG_SAMA5_NAND_HSMCINTERRUPTS */
/****************************************************************************
* Name: nand_wait_dma
@ -1251,6 +1293,8 @@ static int nand_read(struct sam_nandcs_s *priv, bool nfcsram,
#endif
int buswidth;
fvdbg("nfcsram=%d buffer=%p buflen=%d\n", nfcsram, buffer, (int)buflen);
/* Get the buswidth */
buswidth = nandmodel_getbuswidth(&priv->raw.model);
@ -1550,6 +1594,9 @@ static int nand_write(struct sam_nandcs_s *priv, bool nfcsram,
#endif
int buswidth;
fvdbg("nfcsram=%d buffer=%p buflen=%d offset=%d\n",
nfcsram, buffer, (int)buflen, (int)offset);
/* Get the buswidth */
buswidth = nandmodel_getbuswidth(&priv->raw.model);
@ -2615,7 +2662,9 @@ struct mtd_dev_s *sam_nand_initialize(int cs)
#if NAND_NBANKS > 1
sem_init(&g_nand.exclsem, 0, 1);
#endif
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
sem_init(&g_nand.waitsem, 0, 0);
#endif
/* Enable the NAND FLASH Controller (The NFC is always used) */
@ -2634,6 +2683,7 @@ struct mtd_dev_s *sam_nand_initialize(int cs)
nand_putreg(SAM_HSMC_PMECCFG, 0);
#endif
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
/* Attach the CAN interrupt handler */
ret = irq_attach(SAM_IRQ_HSMC, hsmc_interrupt);
@ -2642,15 +2692,17 @@ struct mtd_dev_s *sam_nand_initialize(int cs)
fdbg("Failed to attach HSMC IRQ (%d)", SAM_IRQ_HSMC);
return NULL;
}
#endif
/* Disable all interrupts at the HSMC */
nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_ALL);
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
/* Enable the HSMC interrupts at the interrupt controller */
up_enable_irq(SAM_IRQ_HSMC);
g_nand.initialized = true;
#endif
}
/* Initialize the NAND hardware for this CS */

View File

@ -233,6 +233,17 @@
#ifdef CONFIG_SAMA5_HAVE_NAND
/* 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
* nasty race conditions were created. Rather than resolve those, I simply
* disabled the interrupt logic with this setting. The setting is retained
* in case, for some reason, someone wants to restore the interrupt-driven
* logic. Polling should be better solution in this case.
*/
#undef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
/****************************************************************************
* Public Types
****************************************************************************/
@ -274,10 +285,12 @@ struct sam_nand_s
/* Dynamic state */
#ifdef CONFIG_SAMA5_NAND_HSMCINTERRUPTS
volatile bool cmddone; /* True: NFC command has completed */
volatile bool xfrdone; /* True: Transfer has completed */
volatile bool rbedge; /* True: Ready/busy edge detected */
sem_t waitsem; /* Used to wait for one of the above states */
#endif
#ifdef CONFIG_SAMA5_HAVE_PMECC
uint8_t ecctab[CONFIG_MTD_NAND_MAX_PMECCSIZE];