From 15f6e382cbb869fa712134cd78116ce7c9501633 Mon Sep 17 00:00:00 2001 From: Gregory Nutt Date: Sun, 24 Nov 2013 11:51:30 -0600 Subject: [PATCH] SAMA5 NAND: Finish upper part of PMECC logic; add HSMC interrupt handling --- arch/arm/src/lpc31xx/lpc31_ehci.c | 4 +- arch/arm/src/sama5/chip/sam_hsmc.h | 4 +- arch/arm/src/sama5/sam_nand.c | 524 ++++++++++++++++++++++------- arch/arm/src/sama5/sam_nand.h | 57 +++- arch/arm/src/sama5/sam_pmecc.c | 53 ++- arch/arm/src/sama5/sam_pmecc.h | 21 ++ 6 files changed, 527 insertions(+), 136 deletions(-) diff --git a/arch/arm/src/lpc31xx/lpc31_ehci.c b/arch/arm/src/lpc31xx/lpc31_ehci.c index ae96c41152..22c105c33f 100755 --- a/arch/arm/src/lpc31xx/lpc31_ehci.c +++ b/arch/arm/src/lpc31xx/lpc31_ehci.c @@ -3055,7 +3055,7 @@ static void lpc31_ehci_bottomhalf(FAR void *arg) lpc31_givesem(&g_ehci.exclsem); /* Re-enable relevant EHCI interrupts. Interrupts should still be enabled - * at the level of the AIC. + * at the level of the interrupt controller. */ lpc31_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); @@ -4497,7 +4497,7 @@ FAR struct usbhost_connection_s *lpc31_ehci_initialize(int controller) } /* Enable EHCI interrupts. Interrupts are still disabled at the level of - * the AIC. + * the interrupt controller. */ lpc31_putreg(EHCI_HANDLED_INTS, &HCOR->usbintr); diff --git a/arch/arm/src/sama5/chip/sam_hsmc.h b/arch/arm/src/sama5/chip/sam_hsmc.h index e933a29800..8ad5109c9b 100644 --- a/arch/arm/src/sama5/chip/sam_hsmc.h +++ b/arch/arm/src/sama5/chip/sam_hsmc.h @@ -352,7 +352,9 @@ #define HSMC_NFCINT_UNDEF (1 << 21) /* Bit 21: Undefined Area Access Interrupt */ #define HSMC_NFCINT_AWB (1 << 22) /* Bit 22: Accessing While Busy Interrupt */ #define HSMC_NFCINT_NFCASE (1 << 23) /* Bit 23: NFC Access Size Error Interrupt */ -#define HSMC_NFCINT_RB_EDGE0 (1 << 24) /* Bit 24: Ready/Busy Line 0 Interrupt */ +#define HSMC_NFCINT_RBEDGE0 (1 << 24) /* Bit 24: Ready/Busy Line 0 Interrupt */ + +#define HSMC_NFCINT_ALL (0x01f300030) /* HSMC NFC Address Cycle Zero Register */ diff --git a/arch/arm/src/sama5/sam_nand.c b/arch/arm/src/sama5/sam_nand.c index 99dfa8fe94..5cf25f9ce9 100644 --- a/arch/arm/src/sama5/sam_nand.c +++ b/arch/arm/src/sama5/sam_nand.c @@ -57,12 +57,14 @@ #include #include +#include #include #include #include #include #include +#include #include #include "up_arch.h" @@ -132,14 +134,18 @@ ****************************************************************************/ /* Low-level HSMC Helpers */ +#if NAND_NBANKS > 1 +void nand_lock(void); +void nand_unlock(void); +#else +# define nand_lock() +# define nand_unlock() +#endif + static void nand_wait_ready(struct sam_nandcs_s *priv); static void nand_cmdsend(struct sam_nandcs_s *priv, uint32_t cmd, uint32_t acycle, uint32_t cycle0); static bool nand_operation_complete(struct sam_nandcs_s *priv); -static void nand_coladdr_write(struct sam_nandcs_s *priv, - uint16_t coladdr); -static void nand_rowaddr_write(struct sam_nandcs_s *priv, - uint32_t rowaddr); static int nand_translate_address(struct sam_nandcs_s *priv, uint16_t coladdr, uint32_t rowaddr, uint32_t *acycle0, uint32_t *acycle1234, bool rowonly); @@ -151,8 +157,12 @@ static void nand_nfc_configure(struct sam_nandcs_s *priv, /* Interrupt Handling */ static void nand_wait_cmddone(struct sam_nandcs_s *priv); +static void nand_setup_cmddone(struct sam_nandcs_s *priv); static void nand_wait_xfrdone(struct sam_nandcs_s *priv); +static void nand_setup_xfrdone(struct sam_nandcs_s *priv); static void nand_wait_rbedge(struct sam_nandcs_s *priv); +static void nand_setup_rbedge(struct sam_nandcs_s *priv); +static int hsmc_interrupt(int irq, void *context); /* DMA Helpers */ @@ -171,7 +181,7 @@ static int nand_read(struct sam_nandcs_s *priv, bool nfcsram, #ifdef NAND_HAVE_PMECC static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block, - unsigned int page, const void *data); + unsigned int page, void *data); #endif static int nand_nfcsram_write(const uint8_t *src, uintptr_t dest, @@ -252,6 +262,55 @@ struct sam_nand_s g_nand; * Private Functions ****************************************************************************/ +/**************************************************************************** + * Name: nand_lock + * + * Description: + * Get exclusive access to PMECC hardware + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if NAND_NBANKS > 1 +void nand_lock(void) +{ + int ret; + + do + { + ret = sem_wait(&g_nand.exclsem); + DEBUGASSERT(ret == OK || errno == EINTR); + } + while (ret != OK); +} +#endif + +/**************************************************************************** + * Name: nand_unlock + * + * Description: + * Relinquish exclusive access to PMECC hardware + * + * Input Parameters: + * None + * + * Returned Value: + * None + * + ****************************************************************************/ + +#if NAND_NBANKS > 1 +void nand_unlock(void) +{ + sem_post(&g_nand.exclsem); +} +#endif + /**************************************************************************** * Name: nand_wait_ready * @@ -301,7 +360,7 @@ static void nand_cmdsend(struct sam_nandcs_s *priv, uint32_t cmd, /* Wait until host controller is not busy. */ while ((nand_getreg(NFCCMD_BASE + NFCADDR_CMD_NFCCMD) & 0x8000000) != 0); - priv->cmddone = false; + nand_setup_cmddone(priv); /* Send the command plus the ADDR_CYCLE */ @@ -343,87 +402,6 @@ static bool nand_operation_complete(struct sam_nandcs_s *priv) return true; } -/**************************************************************************** - * Name: nand_coladdr_write - * - * Description: - * Send a column address to the NAND FLASH chip. - * - * Input parameters: - * priv - Lower-half, private NAND FLASH device state - * - * Returned value. - * None - * - ****************************************************************************/ - -static void nand_coladdr_write(struct sam_nandcs_s *priv, uint16_t coladdr) -{ - uint16_t pagesize = nandmodel_getpagesize(&priv->raw.model); - - /* Check the data bus width of the NAND FLASH */ - - if (nandmodel_getbuswidth(&priv->raw.model) == 16) - { - /* Use word vs byte addressing */ - - coladdr >>= 1; - } - - /* Send single column address byte for small block devices, or two column - * address bytes for large block devices - */ - - while (pagesize > 2) - { - if (nandmodel_getbuswidth(&priv->raw.model) == 16) - { - WRITE_ADDRESS16(&priv->raw, coladdr & 0xff); - } - else - { - WRITE_ADDRESS8(&priv->raw, coladdr & 0xff); - } - - pagesize >>= 8; - coladdr >>= 8; - } -} - -/**************************************************************************** - * Name: nand_rowaddr_write - * - * Description: - * Send a row address to the NAND FLASH chip. - * - * Input parameters: - * priv - Lower-half, private NAND FLASH device state - * - * Returned value. - * None - * - ****************************************************************************/ - -static void nand_rowaddr_write(struct sam_nandcs_s *priv, uint32_t rowaddr) -{ - uint32_t npages = nandmodel_getdevpagesize(&priv->raw.model); - - while (npages > 0) - { - if (nandmodel_getbuswidth(&priv->raw.model) == 16) - { - WRITE_ADDRESS16(&priv->raw, rowaddr & 0xff); - } - else - { - WRITE_ADDRESS8(&priv->raw, rowaddr & 0xff); - } - - npages >>= 8; - rowaddr >>= 8; - } -} - /**************************************************************************** * Name: nand_translate_address * @@ -662,18 +640,57 @@ static void nand_nfc_configure(struct sam_nandcs_s *priv, uint8_t mode, static void nand_wait_cmddone(struct sam_nandcs_s *priv) { + irqstate_t flags; int ret; - while (!priv->cmddone) + /* Wait for the XFRDONE interrupt to occur */ + + flags = irqsave(); + while (!g_nand.cmddone) { - ret = sem_wait(&priv->waitsem); + ret = sem_wait(&g_nand.waitsem); if (ret < 0) { DEBUGASSERT(errno == EINTR); } } - priv->cmddone = false; + g_nand.cmddone = false; + nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_CMDDONE); + irqrestore(flags); +} + +/**************************************************************************** + * Name: nand_setup_cmddone + * + * Description: + * Setup to wait for CMDDONE event + * + * Input parameters: + * None + * + * Returned value. + * None + * + ****************************************************************************/ + +static void nand_setup_cmddone(struct sam_nandcs_s *priv) +{ + irqstate_t flags; + + /* Clear all pending interrupts */ + + flags = irqsave(); + nand_getreg(SAM_HSMC_SR); + + /* Mark CMDDONE not received */ + + g_nand.cmddone = false; + + /* Enable the CMDDONE interrupt */ + + nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_CMDDONE); + irqrestore(flags); } /**************************************************************************** @@ -692,18 +709,57 @@ static void nand_wait_cmddone(struct sam_nandcs_s *priv) static void nand_wait_xfrdone(struct sam_nandcs_s *priv) { + irqstate_t flags; int ret; - while (!priv->xfrdone) + /* Wait for the XFRDONE interrupt to occur */ + + flags = irqsave(); + while (!g_nand.xfrdone) { - ret = sem_wait(&priv->waitsem); + ret = sem_wait(&g_nand.waitsem); if (ret < 0) { DEBUGASSERT(errno == EINTR); } } - priv->xfrdone = false; + g_nand.xfrdone = false; + nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_XFRDONE); + irqrestore(flags); +} + +/**************************************************************************** + * Name: nand_setup_xfrdone + * + * Description: + * Setup to wait for XFDONE event + * + * Input parameters: + * None + * + * Returned value. + * None + * + ****************************************************************************/ + +static void nand_setup_xfrdone(struct sam_nandcs_s *priv) +{ + irqstate_t flags; + + /* Clear all pending interrupts */ + + flags = irqsave(); + nand_getreg(SAM_HSMC_SR); + + /* Mark XFRDONE not received */ + + g_nand.xfrdone = false; + + /* Enable the XFRDONE interrupt */ + + nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_XFRDONE); + irqrestore(flags); } /**************************************************************************** @@ -722,18 +778,110 @@ static void nand_wait_xfrdone(struct sam_nandcs_s *priv) static void nand_wait_rbedge(struct sam_nandcs_s *priv) { + irqstate_t flags; int ret; - while (!priv->rbedge) + /* Wait for the RBEDGE interrupt to occur */ + + flags = irqsave(); + while (!g_nand.rbedge) { - ret = sem_wait(&priv->waitsem); + ret = sem_wait(&g_nand.waitsem); if (ret < 0) { DEBUGASSERT(errno == EINTR); } } - priv->rbedge = false; + g_nand.rbedge = false; + nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_RBEDGE0); + irqrestore(flags); +} + +/**************************************************************************** + * Name: nand_setup_rbedge + * + * Description: + * Setup to wait for RBEDGE0 event + * + * Input parameters: + * None + * + * Returned value. + * None + * + ****************************************************************************/ + +static void nand_setup_rbedge(struct sam_nandcs_s *priv) +{ + irqstate_t flags; + + /* Clear all pending interrupts */ + + flags = irqsave(); + nand_getreg(SAM_HSMC_SR); + + /* Mark RBEDGE0 not received */ + + g_nand.rbedge = false; + + /* Enable the EBEDGE0 interrupt */ + + nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_RBEDGE0); + irqrestore(flags); +} + +/**************************************************************************** + * Name: hsmc_interrupt + * + * Description: + * HSMC interrupt handler + * + * Input parameters: + * Standard interrupt arguments + * + * Returned value. + * Always returns OK + * + ****************************************************************************/ + +static int hsmc_interrupt(int irq, void *context) +{ + uint32_t status = nand_getreg(SAM_HSMC_SR); + + /* When set to one, this XFRDONE indicates that the NFC has terminated + * the data transfer. This flag is reset after the status read. + */ + + if ((status & HSMC_NFCINT_XFRDONE) != 0) + { + g_nand.xfrdone = true; + sem_post(&g_nand.waitsem); + } + + /* When set to one, the CMDDONE flag indicates that the NFC has terminated + * the Command. This flag is reset after the status read. + */ + + if ((status & HSMC_NFCINT_CMDDONE) != 0) + { + g_nand.cmddone = true; + sem_post(&g_nand.waitsem); + } + + /* If set to one, the RBEDGE0 flag indicates that an edge has been detected + * on the Ready/Busy Line x. Depending on the EDGE CTRL field located in the + * SMC_CFG register, only rising or falling edge is detected. This flag is + * reset after the status read. + */ + + if ((status & HSMC_NFCINT_RBEDGE0) != 0) + { + g_nand.rbedge = true; + sem_post(&g_nand.waitsem); + } + + return OK; } /**************************************************************************** @@ -1091,6 +1239,7 @@ static int nand_read(struct sam_nandcs_s *priv, bool nfcsram, * block - Number of the block where the page to read resides. * page - Number of the page to read inside the given block. * data - Buffer where the data area will be stored. + * spare - Buffer where the spare area will be stored. * * Returned value. * OK is returned in succes; a negated errno value is returned on failure. @@ -1099,9 +1248,8 @@ static int nand_read(struct sam_nandcs_s *priv, bool nfcsram, #ifdef NAND_HAVE_PMECC static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block, - unsigned int page, const void *data) + unsigned int page, void *data) { - uint32_t eccpagesize; uint32_t rawaddr; uint32_t regval; uint16_t pagesize; @@ -1151,7 +1299,6 @@ static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block, HSMC_CFG_NFCSPARESIZE((sparesize-1) >> 2)); nand_putreg(SAM_HSMC_CFG, regval); - /* Calculate actual address of the page */ rawaddr = block * nandmodel_pagesperblock(&priv->raw.model) + page; @@ -1431,7 +1578,7 @@ static int nand_readpage_noecc(struct sam_nandcs_s *priv, off_t block, /* Initialize the NFC */ - priv->xfrdone = false; + nand_setup_xfrdone(priv); nand_nfc_configure(priv, HSMC_ALE_COL_EN | HSMC_ALE_ROW_EN | HSMC_CLE_VCMD2_EN | HSMC_CLE_DATA_EN, COMMAND_READ_1, COMMAND_READ_2, coladdr, rowaddr); @@ -1444,7 +1591,7 @@ static int nand_readpage_noecc(struct sam_nandcs_s *priv, off_t block, nand_read(priv, true, (uint8_t *)data, pagesize); } - /* Read the spare are is so requrest */ + /* Read the spare are is so requested */ if (spare) { @@ -1476,13 +1623,66 @@ static int nand_readpage_noecc(struct sam_nandcs_s *priv, off_t block, static int nand_readpage_pmecc(struct sam_nandcs_s *priv, off_t block, unsigned int page, void *data) { + uint32_t regval; + uint16_t sparesize; + int ret; + int i; + + DEBUGASSERT(priv && data); + /* Get exclusive access to the PMECC */ - pmecc_lock(); + nand_lock(); + sparesize = nandmodel_getsparesize(&priv->raw.model); -#warning Missing logic - pmecc_unlock(); - return -ENOSYS; + /* Start by reading the spare data */ + + ret = nand_read_pmecc(priv, block, page, data); + if (ret < 0) + { + fdbg("ERROR: Failed to read page\n"); + return ret; + } + + regval = nand_getreg(SAM_HSMC_PMECCISR); + if (regval) + { + /* Check if the spare area was erased */ + + nand_readpage_noecc(priv, block, page, NULL, priv->raw.spare); + for (i = 0 ; i < sparesize; i++) + { + if (priv->raw.spare[i] != 0xff) + { + break; + } + } + + /* The spare area has been erased */ + + if (i >= sparesize) + { + regval = 0; + } + } + + /* Bit correction will be done directly in destination buffer. */ + + ret = pmecc_correction(regval, (uintptr_t)data); + if (ret < 0) + { + fdbg("ERROR: Block %d page %d Unrecoverable data\n", block, page); + } + + /* Disable the HSMC */ + + regval = nand_getreg(SAM_HSMC_PMECCFG); + regval &= ~HSMC_PMECCFG_AUTO_MASK; + nand_putreg(SAM_HSMC_PMECCFG, regval); + + nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DISABLE); + nand_unlock(); + return ret; } #endif /* NAND_HAVE_PMECC */ @@ -1585,13 +1785,14 @@ static int nand_writepage_noecc(struct sam_nandcs_s *priv, off_t block, { /* Start a Data Phase */ - priv->xfrdone = false; + nand_setup_xfrdone(priv); nand_nfc_configure(priv, HSMC_CLE_WRITE_EN | HSMC_ALE_COL_EN | HSMC_ALE_ROW_EN | HSMC_CLE_DATA_EN, COMMAND_WRITE_1, 0, 0, rowaddr); nand_wait_xfrdone(priv); + nand_setup_rbedge(priv); nand_nfc_configure(priv, HSMC_CLE_WRITE_EN, COMMAND_WRITE_2, 0, 0, 0); nand_wait_rbedge(priv); @@ -1660,23 +1861,21 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, int i; int ret = 0; + fvdbg("Block %d Page %d\n", block, page); + /* Get exclusive access to the PMECC */ - pmecc_lock(); + nand_lock(); - /* Calculate physical address of the page */ - - rowaddr = block * nandmodel_pagesperblock(&priv->raw.model) + page; + /* Calculate the start page address */ regval = nand_getreg(SAM_HSMC_PMECCSADDR); pagesize = nandmodel_getpagesize(&priv->raw.model); startaddr = regval + pagesize; - fvdbg("Block %d Page %d\n", block, page); - /* Calculate physical address of the page */ - rowaddr = block * nandmodel_pagesperblock(&priv->raw.model) + page; + rowaddr = block * nandmodel_pagesperblock(&priv->raw.model) + page; /* Write data area if needed */ @@ -1734,7 +1933,7 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, /* Configure the NFC */ - priv->xfrdone = false; + nand_setup_xfrdone(priv); nand_nfc_configure(priv, HSMC_CLE_WRITE_EN | HSMC_ALE_COL_EN | HSMC_ALE_ROW_EN | HSMC_CLE_DATA_EN, @@ -1798,7 +1997,7 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block, /* Disable the PMECC */ nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DISABLE); - pmecc_unlock(); + nand_unlock(); return ret; } #endif /* NAND_HAVE_PMECC */ @@ -1854,11 +2053,20 @@ static int nand_eraseblock(struct nand_raw_s *raw, off_t block) fvdbg("Block %d\n", (int)block); + /* Get exclusvie access to the HSMC hardware. + * REVISIT: The scope of this exclusivity is just NAND. + */ + + nand_lock(); + + /* Try up to NAND_ERASE_NRETRIES times to erase the FLASH */ + while (retries > 0) { ret = nand_tryeraseblock(priv, block); if (ret == OK) { + nand_unlock(); return OK; } @@ -1868,6 +2076,7 @@ static int nand_eraseblock(struct nand_raw_s *raw, off_t block) fdbg("ERROR: Failed to erase %d after %d tries\n", (int)block, NAND_ERASE_NRETRIES); + nand_unlock(); return -EAGAIN; } @@ -1894,9 +2103,18 @@ static int nand_rawread(struct nand_raw_s *raw, off_t block, unsigned int page, void *data, void *spare) { struct sam_nandcs_s *priv = (struct sam_nandcs_s *)raw; + int ret; + DEBUGASSERT(raw); - return nand_readpage_noecc(priv, block, page, data, spare); + /* Get exclusvie access to the HSMC hardware. + * REVISIT: The scope of this exclusivity is just NAND. + */ + + nand_lock(); + ret = nand_readpage_noecc(priv, block, page, data, spare); + nand_unlock(); + return ret; } /**************************************************************************** @@ -1923,9 +2141,18 @@ static int nand_rawwrite(struct nand_raw_s *raw, off_t block, const void *spare) { struct sam_nandcs_s *priv = (struct sam_nandcs_s *)raw; + int ret; + DEBUGASSERT(raw); - return nand_writepage_noecc(priv, block, page, data, spare); + /* Get exclusvie access to the HSMC hardware. + * REVISIT: The scope of this exclusivity is just NAND. + */ + + nand_lock(); + ret = nand_writepage_noecc(priv, block, page, data, spare); + nand_unlock(); + return ret; } /**************************************************************************** @@ -1953,29 +2180,42 @@ static int nand_readpage(struct nand_raw_s *raw, off_t block, unsigned int page, void *data, void *spare) { struct sam_nandcs_s *priv = (struct sam_nandcs_s *)raw; + int ret; + DEBUGASSERT(raw); + /* Get exclusvie access to the HSMC hardware. + * REVISIT: The scope of this exclusivity is just NAND. + */ + + nand_lock(); + + /* Read the page */ + #ifndef CONFIG_MTD_NAND_BLOCKCHECK - return nand_readpage_noecc(priv, block, page, data, spare); + ret = nand_readpage_noecc(priv, block, page, data, spare); #else DEBUGASSERT(raw->ecctype != NANDECC_SWECC); switch (raw->ecctype) { case NANDECC_NONE: case NANDECC_CHIPECC: - return nand_readpage_noecc(priv, block, page, data, spare); + ret = nand_readpage_noecc(priv, block, page, data, spare); #ifdef NAND_HAVE_PMECC case NANDECC_PMECC: DEBUGASSERT(!spare); - return nand_readpage_pmecc(priv, block, page, data); + ret = nand_readpage_pmecc(priv, block, page, data); #endif case NANDECC_SWECC: default: - return -EINVAL; + ret = -EINVAL; } #endif + + nand_unlock(); + return ret; } #endif @@ -2004,29 +2244,42 @@ static int nand_writepage(struct nand_raw_s *raw, off_t block, const void *spare) { struct sam_nandcs_s *priv = (struct sam_nandcs_s *)raw; + int ret; + DEBUGASSERT(raw); + /* Get exclusvie access to the HSMC hardware. + * REVISIT: The scope of this exclusivity is just NAND. + */ + + nand_lock(); + + /* Write the page */ + #ifndef CONFIG_MTD_NAND_BLOCKCHECK - return nand_writepage_noecc(priv, block, page, data, spare); + ret = nand_writepage_noecc(priv, block, page, data, spare); #else DEBUGASSERT(raw->ecctype != NANDECC_SWECC); switch (raw->ecctype) { case NANDECC_NONE: case NANDECC_CHIPECC: - return nand_writepage_noecc(priv, block, page, data, spare); + ret = nand_writepage_noecc(priv, block, page, data, spare); #ifdef NAND_HAVE_PMECC case NANDECC_PMECC: DEBUGASSERT(!spare); - return nand_writepage_pmecc(priv, block, page, data); + ret = nand_writepage_pmecc(priv, block, page, data); #endif case NANDECC_SWECC: default: - return -EINVAL; + ret = -EINVAL; } #endif + + nand_unlock(); + return ret; } #endif @@ -2243,6 +2496,11 @@ struct mtd_dev_s *sam_nand_initialize(int cs) if (!g_nand.initialized) { + /* Initialize the global nand state structure */ + + sem_init(&g_nand.exclsem, 0, 1); + sem_init(&g_nand.waitsem, 0, 0); + /* Enable the NAND FLASH Controller (The NFC is always used) */ nand_putreg(SAM_HSMC_CTRL, HSMC_CTRL_NFCEN); @@ -2260,6 +2518,22 @@ struct mtd_dev_s *sam_nand_initialize(int cs) nand_putreg(SAM_SMC_PMECCFG, 0); #endif + /* Attach the CAN interrupt handler */ + + ret = irq_attach(SAM_IRQ_HSMC, hsmc_interrupt); + if (ret < 0) + { + fdbg("Failed to attach HSMC IRQ (%d)", SAM_IRQ_HSMC); + return NULL; + } + + /* Disable all interrupts at the HSMC */ + + nand_putreg(SAM_HSMC_IDR, HSMC_NFCINT_ALL); + + /* Enable the HSMC interrupts at the interrupt controller */ + + up_enable_irq(SAM_IRQ_HSMC); g_nand.initialized = true; } diff --git a/arch/arm/src/sama5/sam_nand.h b/arch/arm/src/sama5/sam_nand.h index 2ef994d63e..5046ed89b7 100644 --- a/arch/arm/src/sama5/sam_nand.h +++ b/arch/arm/src/sama5/sam_nand.h @@ -184,6 +184,46 @@ # endif #endif /* CONFIG_SAMA5_EBICS3_NAND */ +/* Count the number of banks that configured for NAND with PMECC support + * enabled. + */ + +#undef HAVE_NAND +#ifdef CONFIG_SAMA5_EBICS0_NAND +# define HAVE_NAND 1 +# define NAND_HAVE_EBICS0 1 +#else +# define NAND_HAVE_EBICS0 0 +#endif + +#ifdef CONFIG_SAMA5_EBICS1_NAND +# define HAVE_NAND 1 +# define NAND_HAVE_EBICS1 1 +#else +# define NAND_HAVE_EBICS1 0 +#endif + +#ifdef CONFIG_SAMA5_EBICS2_NAND +# define HAVE_NAND 1 +# define NAND_HAVE_EBICS2 1 +#else +# define NAND_HAVE_EBICS2 0 +#endif + +#ifdef CONFIG_SAMA5_EBICS3_NAND +# define HAVE_NAND 1 +# define NAND_HAVE_EBICS3 1 +#else +# define NAND_HAVE_EBICS3 0 +#endif + +/* Count the number of banks configured for NAND */ + +#define NAND_NBANKS \ + (NAND_HAVE_EBICS0 + NAND_HAVE_EBICS1 + NAND_HAVE_EBICS2 + NAND_HAVE_EBICS3) + +#ifdef HAVE_NAND + /**************************************************************************** * Public Types ****************************************************************************/ @@ -200,14 +240,8 @@ struct sam_nandcs_s /* Static configuration */ uint8_t cs; /* Chip select number (0..3) */ - - /* Dynamic state */ - - volatile bool cmddone; /* True: NFC commnad has completed */ - volatile bool xfrdone; /* True: Transfer has completed */ - volatile bool rbedge; /* True: Ready/busy edge detected */ volatile bool dmadone; /* True: DMA has completed */ - sem_t waitsem; /* Used to wait for one of the above states */ + 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 */ @@ -216,6 +250,14 @@ struct sam_nandcs_s struct sam_nand_s { bool initialized; /* True: One time initialization is complete */ + sem_t exclsem; /* Enforce exclusive access to the SMC hardware */ + + /* Dynamic state */ + + volatile bool cmddone; /* True: NFC commnad 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 */ #ifdef NAND_HAVE_PMECC uint8_t ecctab[CONFIG_MTD_NAND_MAX_PMECCSIZE]; @@ -410,4 +452,5 @@ static inline void nand_putreg(uintptr_t regaddr, uint32_t regval) #endif #endif /* __ASSEMBLY__ */ +#endif /* HAVE_NAND */ #endif /* __ARCH_ARM_SRC_SAMA5_SAM_NAND_H */ diff --git a/arch/arm/src/sama5/sam_pmecc.c b/arch/arm/src/sama5/sam_pmecc.c index c49ef086ee..ed8ba2e1ad 100644 --- a/arch/arm/src/sama5/sam_pmecc.c +++ b/arch/arm/src/sama5/sam_pmecc.c @@ -134,11 +134,29 @@ struct sam_pmecc_s struct pmecc_desc_s desc; /* Atmel PMECC descriptor */ }; +/* This is the type of the ROM detection/correction function + * + * REVISIT: Whare are the types Pmecc and Pmerrloc? + */ + +#ifdef CONFIG_SAMA5_PMECC_EMBEDDEDALGO +typedef uint32_t (*pmecc_correctionalgo_t)(Pmecc *, Pmerrloc *, + struct pmecc_desc_s *desc, + uint32_t isr, uintptr_t data); +#endif + /**************************************************************************** * Private Function Prototypes ****************************************************************************/ - /**************************************************************************** +#ifdef CONFIG_SAMA5_PMECC_EMBEDDEDALGO +# define pmecc_correctionalgo \ + (pmecc_correctionalgo_t)CONFIG_SAMA5_PMECC_EMBEDDEDALGO_ADDR) +#else +static uint32_t pmecc_correctionalgo(uint32_t isr, uintptr_t data); +#endif + +/**************************************************************************** * Private Data ****************************************************************************/ /* PMECC state data */ @@ -418,6 +436,39 @@ void pmecc_unlock(void) } #endif +/**************************************************************************** + * Name: pmecc_correction + * + * Description: + * Perform the PMECC correction algorithm + * + * Input Parameters: + * isr - Value of the PMECC ISR register + * data - Data to be corrected + * + * Returned Value: + * OK on success; a negated errno value on failure + * + * Assumptions: + * PMECC has been initialized for the CS and the caller holds the PMECC + * lock. + * + ****************************************************************************/ + +int pmecc_correction(uint32_t isr, uintptr_t data) +{ +#ifdef CONFIG_SAMA5_PMECC_EMBEDDEDALGO + /* REVISIT: Whare are the types Pmecc and Pmerrloc? */ + /* REVISIT: Check returned value */ + + return pmecc_correctionalgo(??, ??, &g_pmecc, isr, data); +#else + /* REVISIT: Check returned value */ + + return pmecc_correctionalgo(isr, data); +#endif +} + /**************************************************************************** * Name: pmecc_get* * diff --git a/arch/arm/src/sama5/sam_pmecc.h b/arch/arm/src/sama5/sam_pmecc.h index fa4b2f3e16..cd63501347 100644 --- a/arch/arm/src/sama5/sam_pmecc.h +++ b/arch/arm/src/sama5/sam_pmecc.h @@ -294,6 +294,27 @@ struct sam_nandcs_s; int pmecc_configure(struct sam_nandcs_s *priv, uint16_t eccoffset, bool protected); +/**************************************************************************** + * Name: pmecc_correction + * + * Description: + * Perform the PMECC correction algorithm + * + * Input Parameters: + * isr - Value of the PMECC ISR register + * data - Data to be corrected + * + * Returned Value: + * OK on success; a negated errno value on failure + * + * Assumptions: + * PMECC has been initialized for the CS and the caller holds the PMECC + * lock. + * + ****************************************************************************/ + +int pmecc_correction(uint32_t isr, uintptr_t data); + /**************************************************************************** * Name: pmecc_get* *