SAMA5 NAND: Finish upper part of PMECC logic; add HSMC interrupt handling
This commit is contained in:
parent
d1b4f1c466
commit
15f6e382cb
@ -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);
|
||||
|
@ -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 */
|
||||
|
||||
|
@ -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,
|
||||
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 */
|
||||
|
@ -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*
|
||||
*
|
||||
|
@ -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*
|
||||
*
|
||||
|
Loading…
Reference in New Issue
Block a user