summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorGregory Nutt <gnutt@nuttx.org>2013-11-24 11:51:30 -0600
committerGregory Nutt <gnutt@nuttx.org>2013-11-24 11:51:30 -0600
commita13a84cef58bad6c9df8991949fd7afc08479877 (patch)
treebe3ba8ae5670c08327c72cb5932b6383ee1f6fba
parentdb2fbbd924f010bce8980b2b8e9eafce79323839 (diff)
downloadnuttx-a13a84cef58bad6c9df8991949fd7afc08479877.tar.gz
nuttx-a13a84cef58bad6c9df8991949fd7afc08479877.tar.bz2
nuttx-a13a84cef58bad6c9df8991949fd7afc08479877.zip
SAMA5 NAND: Finish upper part of PMECC logic; add HSMC interrupt handling
-rwxr-xr-xnuttx/arch/arm/src/lpc31xx/lpc31_ehci.c4
-rw-r--r--nuttx/arch/arm/src/sama5/chip/sam_hsmc.h4
-rw-r--r--nuttx/arch/arm/src/sama5/sam_nand.c524
-rw-r--r--nuttx/arch/arm/src/sama5/sam_nand.h57
-rw-r--r--nuttx/arch/arm/src/sama5/sam_pmecc.c53
-rw-r--r--nuttx/arch/arm/src/sama5/sam_pmecc.h21
6 files changed, 527 insertions, 136 deletions
diff --git a/nuttx/arch/arm/src/lpc31xx/lpc31_ehci.c b/nuttx/arch/arm/src/lpc31xx/lpc31_ehci.c
index ae96c4115..22c105c33 100755
--- a/nuttx/arch/arm/src/lpc31xx/lpc31_ehci.c
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/chip/sam_hsmc.h b/nuttx/arch/arm/src/sama5/chip/sam_hsmc.h
index e933a2980..8ad5109c9 100644
--- a/nuttx/arch/arm/src/sama5/chip/sam_hsmc.h
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/sam_nand.c b/nuttx/arch/arm/src/sama5/sam_nand.c
index 99dfa8fe9..5cf25f9ce 100644
--- a/nuttx/arch/arm/src/sama5/sam_nand.c
+++ b/nuttx/arch/arm/src/sama5/sam_nand.c
@@ -57,12 +57,14 @@
#include <assert.h>
#include <debug.h>
+#include <nuttx/arch.h>
#include <nuttx/fs/ioctl.h>
#include <nuttx/mtd/mtd.h>
#include <nuttx/mtd/nand.h>
#include <nuttx/mtd/nand_raw.h>
#include <nuttx/mtd/nand_model.h>
+#include <arch/irq.h>
#include <arch/board/board.h>
#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,
@@ -253,6 +263,55 @@ struct sam_nand_s g_nand;
****************************************************************************/
/****************************************************************************
+ * 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
*
* Description:
@@ -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 */
@@ -344,87 +403,6 @@ static bool nand_operation_complete(struct sam_nandcs_s *priv)
}
/****************************************************************************
- * 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
*
* Description:
@@ -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);
+
+ /* 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);
-#warning Missing logic
- pmecc_unlock();
- return -ENOSYS;
+ 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;
- /* Get exclusive access to the PMECC */
+ fvdbg("Block %d Page %d\n", block, page);
- pmecc_lock();
+ /* Get exclusive access to the PMECC */
- /* Calculate physical address of the page */
+ nand_lock();
- 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/nuttx/arch/arm/src/sama5/sam_nand.h b/nuttx/arch/arm/src/sama5/sam_nand.h
index 2ef994d63..5046ed89b 100644
--- a/nuttx/arch/arm/src/sama5/sam_nand.h
+++ b/nuttx/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/nuttx/arch/arm/src/sama5/sam_pmecc.c b/nuttx/arch/arm/src/sama5/sam_pmecc.c
index c49ef086e..ed8ba2e1a 100644
--- a/nuttx/arch/arm/src/sama5/sam_pmecc.c
+++ b/nuttx/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 */
@@ -419,6 +437,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*
*
* Description:
diff --git a/nuttx/arch/arm/src/sama5/sam_pmecc.h b/nuttx/arch/arm/src/sama5/sam_pmecc.h
index fa4b2f3e1..cd6350134 100644
--- a/nuttx/arch/arm/src/sama5/sam_pmecc.h
+++ b/nuttx/arch/arm/src/sama5/sam_pmecc.h
@@ -295,6 +295,27 @@ 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*
*
* Description: