SAMA5 NAND: Fix some PMECC setup logic

This commit is contained in:
Gregory Nutt 2013-12-04 11:56:56 -06:00
parent 68ccbc0855
commit ae01f868b2
6 changed files with 248 additions and 91 deletions

View File

@ -1739,9 +1739,7 @@ static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block,
/* Start a Data Phase */
regval = nand_getreg(SAM_HSMC_PMECCTRL);
regval |= HSMC_PMECCTRL_DATA;
nand_putreg(SAM_HSMC_PMECCTRL, regval);
nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DATA);
regval = nand_getreg(SAM_HSMC_PMECCEADDR);
ret = nand_read(priv, true, (uint8_t *)data, pagesize + (regval + 1));
@ -2088,6 +2086,7 @@ static int nand_readpage_pmecc(struct sam_nandcs_s *priv, off_t block,
int ret;
int i;
fvdbg("block=%d page=%d data=%p\n", (int)block, page, data);
DEBUGASSERT(priv && data);
/* Make sure that we have exclusive access to the PMECC and that the PMECC
@ -2095,7 +2094,12 @@ static int nand_readpage_pmecc(struct sam_nandcs_s *priv, off_t block,
*/
pmecc_lock();
pmecc_configure(priv, 0, false);
ret = pmecc_configure(priv, false);
if (ret < 0)
{
fdbg("ERROR: pmecc_configure failed: %d\n", ret);
goto errout;
}
/* Start by reading the spare data */
@ -2340,13 +2344,19 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block,
int ret = 0;
fvdbg("block=%d page=%d data=%p\n", (int)block, page, data);
DEBUGASSERT(priv && data);
/* Make sure that we have exclusive access to the PMECC and that the PMECC
* is properly configured for this CS.
*/
pmecc_lock();
pmecc_configure(priv, 0, false);
ret = pmecc_configure(priv, false);
if (ret < 0)
{
fdbg("ERROR: pmecc_configure failed: %d\n", ret);
goto errout;
}
/* Calculate the start page address */
@ -2409,9 +2419,7 @@ static int nand_writepage_pmecc(struct sam_nandcs_s *priv, off_t block,
/* Start a data phase */
regval = nand_getreg(SAM_HSMC_PMECCTRL);
regval |= HSMC_PMECCTRL_DATA;
nand_putreg(SAM_HSMC_PMECCTRL, regval);
nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DATA);
regval = nand_getreg(SAM_HSMC_PMECCFG);
regval |= HSMC_PMECCFG_NANDWR_WRITE;

View File

@ -57,6 +57,9 @@
#include <errno.h>
#include <debug.h>
#include <nuttx/mtd/nand_model.h>
#include <nuttx/mtd/nand_scheme.h>
#include "sam_pmecc.h"
#include "sam_nand.h"
@ -83,12 +86,6 @@
#define PMECC_MAX_CORRECTABILITY 25
/* Start address of ECC cvalue in spare zone, this must not be 0 since bad
* block tags are at address 0.
*/
#define PMECC_ECC_DEFAULT_STARTOFFSET 2
/****************************************************************************
* Private Types
****************************************************************************/
@ -753,44 +750,44 @@ static uint32_t pmecc_correctionalgo(uint32_t isr, uint32_t data)
*
****************************************************************************/
static int pmecc_bcherr512(uint8_t nsectors, uint16_t sparesize)
static int pmecc_bcherr512(uint8_t nsectors, uint16_t eccsize)
{
/* 39-bytes per 512 byte sector are required correctability of 24 errors */
if (sparesize <= 39 * ((unsigned int)nsectors))
if (eccsize >= 39 * ((unsigned int)nsectors))
{
return BCH_ERR24;
}
/* 20-bytes per 512 byte sector are required correctability of 12 errors */
else if (sparesize <= (20 * (unsigned int)nsectors))
else if (eccsize >= (20 * (unsigned int)nsectors))
{
return BCH_ERR12;
}
/* 13-bytes per 512 byte sector are required correctability of 8 errors */
else if (sparesize <= (13 * (unsigned int)nsectors))
else if (eccsize >= (13 * (unsigned int)nsectors))
{
return BCH_ERR8;
}
/* 7-bytes per 512 byte sector are required correctability of 4 errors */
else if (sparesize <= (7 *(unsigned int) nsectors))
else if (eccsize >= (7 *(unsigned int) nsectors))
{
return BCH_ERR4;
}
/* 4-bytes per 512 byte sector are required correctability of 2 errors */
else if (sparesize <= (4 *(unsigned int) nsectors))
else if (eccsize >= (4 *(unsigned int) nsectors))
{
return BCH_ERR2;
}
return 0;
return -EINVAL;
}
/****************************************************************************
@ -801,123 +798,156 @@ static int pmecc_bcherr512(uint8_t nsectors, uint16_t sparesize)
*
****************************************************************************/
static int pmecc_bcherr1k(uint8_t nsectors, uint16_t sparesize)
static int pmecc_bcherr1k(uint8_t nsectors, uint16_t eccsize)
{
/* 42-bytes per 1024 byte sector are required correctability of 24 errors */
if (sparesize <= 42 * ((unsigned int)nsectors))
if (eccsize >= 42 * ((unsigned int)nsectors))
{
return BCH_ERR24;
}
/* 21-bytes per 1024 byte sector are required correctability of 12 errors */
else if (sparesize <= (20 * (unsigned int)nsectors))
else if (eccsize >= (21 * (unsigned int)nsectors))
{
return BCH_ERR12;
}
/* 14-bytes per 1024 byte sector are required correctability of 8 errors */
else if (sparesize <= (13 * (unsigned int)nsectors))
else if (eccsize >= (14 * (unsigned int)nsectors))
{
return BCH_ERR8;
}
/* 7-bytes per 1024 byte sector are required correctability of 4 errors */
else if (sparesize <= (7 *(unsigned int) nsectors))
else if (eccsize >= (7 *(unsigned int) nsectors))
{
return BCH_ERR4;
}
/* 4-bytes per 1024 byte sector are required correctability of 2 errors */
else if (sparesize <= (4 *(unsigned int) nsectors))
else if (eccsize >= (4 *(unsigned int) nsectors))
{
return BCH_ERR2;
}
return 0;
return -EINVAL;
}
/****************************************************************************
* Name: pmecc_pagelayout
*
* Description:
* Given the data size and the spare size, determine the optimal sector
* size and correctability.
* Given the size of the data region and the size of the ECC region,
* determine the optimal sector size and correctability.
*
****************************************************************************/
static void pmecc_pagelayout(uint16_t datasize, uint16_t sparesize,
uint16_t offset)
static int pmecc_pagelayout(uint16_t datasize, uint16_t eccsize)
{
uint16_t correctability512;
uint16_t correctability1K;
uint8_t nsectors512;
uint8_t nsectors1k;
uint8_t bcherr512;
uint8_t bcherr1k;
uint8_t bcherr;
int bcherr512;
int bcherr1k;
int selector;
fvdbg("datasize=%d sparesize=%d offset=%d\n", datasize, sparesize, offset);
/* ECC must not start at address zero, since bad block tags are at offset
* zero.
*/
DEBUGASSERT(datasize != 0 && offset > 0);
/* Decrease the spare size by the offset */
sparesize -= offset;
fvdbg("datasize=%d eccsize=%d\n", datasize, eccsize);
DEBUGASSERT(datasize > 0 && eccsize > 0);
/* Try for 512 byte sectors */
DEBUGASSERT((datasize & 0x000001ff) == 0 && datasize >= 512);
selector = 0;
nsectors512 = (datasize >> 9);
bcherr512 = pmecc_bcherr512(nsectors512, sparesize);
bcherr512 = pmecc_bcherr512(nsectors512, eccsize);
if (bcherr512 < 0)
{
fdbg("WARNING: Cannot realize 512B sectors\n");
}
else
{
selector = 1;
}
fvdbg("nsectors512=%d bcherr512=%d selector=%d\n",
nsectors512, bcherr512, selector);
/* Try for 1024 byte sectors */
if ((datasize & 0x000003ff) == 0)
{
nsectors1k = (datasize >> 9);
bcherr1k = pmecc_bcherr1k(nsectors1k, sparesize);
nsectors1k = (datasize >> 10);
bcherr1k = pmecc_bcherr1k(nsectors1k, eccsize);
}
else
{
nsectors1k = 0;
bcherr1k = 0;
bcherr1k = -EINVAL;
}
/* Now pick the best (most likely 1024) */
DEBUGASSERT(bcherr512 > 0 || bcherr1k > 0);
if (bcherr1k == 0)
if (bcherr1k < 0)
{
g_pmecc.sector1k = false;
g_pmecc.nsectors = nsectors512;
bcherr = bcherr512;
fdbg("WARNING: Cannot realize 1KB sectors\n");
}
else
{
correctability512 = nsectors512 * g_correctability[bcherr512];
correctability1K = nsectors1k * g_correctability[bcherr1k];
if (correctability512 >= correctability1K)
selector |= 2;
}
fvdbg("nsectors1k=%d bcherr1k=%d selector=%d\n",
nsectors1k, bcherr1k, selector);
/* Now pick the best (most likely 1024) */
DEBUGASSERT(bcherr512 >= 0 || bcherr1k >= 0);
switch (selector)
{
case 1: /* 512B sectors possible; 1KB sectors not possible */
{
g_pmecc.sector1k = false;
g_pmecc.nsectors = nsectors512;
bcherr = bcherr512;
DEBUGASSERT(bcherr512 >= 0);
}
else
break;
case 3: /* Both 512B and 1KB sectors possible */
{
correctability512 = nsectors512 * g_correctability[bcherr512];
correctability1K = nsectors1k * g_correctability[bcherr1k];
/* Use 1K sectors unless we can do better with 512B sectors */
if (correctability512 > correctability1K)
{
g_pmecc.sector1k = false;
g_pmecc.nsectors = nsectors512;
bcherr = bcherr512;
DEBUGASSERT(bcherr512 >= 0);
break;
}
} /* Otherwise, fall through for the 1KB sectors */
case 2: /* 512B sectors not possible; 1KB sectors possible */
{
g_pmecc.sector1k = true;
g_pmecc.nsectors = nsectors1k;
bcherr = bcherr1k;
DEBUGASSERT(bcherr1k >= 0);
}
break;
case 0: /* Either 512B and 1KB sectors possible */
default:
return -ENOSYS;
}
/* Save the correctability value */
@ -927,6 +957,11 @@ static void pmecc_pagelayout(uint16_t datasize, uint16_t sparesize,
/* And the correctly shifted BCH_ERR register value */
g_pmecc.desc.bcherr = ((uint32_t)bcherr << HSMC_PMECCFG_BCHERR_SHIFT);
fvdbg("sector1k=%d nsectors=%d bcherr=%d correctability=%d\n",
g_pmecc.sector1k, g_pmecc.nsectors, bcherr, g_pmecc.correctability);
return OK;
}
/****************************************************************************
@ -963,7 +998,6 @@ void pmecc_initialize(void)
*
* Input Parameters:
* priv - Pointer to a struct sam_nandcs_s instance.
* eccoffset - offset of the first ecc byte in spare zone.
* protected - True: The spare area is protected with the last sector of
* data.
* False: The spare area is skipped in read or write mode.
@ -973,11 +1007,16 @@ void pmecc_initialize(void)
*
****************************************************************************/
int pmecc_configure(struct sam_nandcs_s *priv, uint16_t eccoffset,
bool protected)
int pmecc_configure(struct sam_nandcs_s *priv, bool protected)
{
struct nand_model_s *model;
unsigned int sectorsperpage = 0;
uint16_t eccoffset;
uint16_t eccsize;
uint32_t regval;
int ret;
fvdbg("protected=%d configured=%d\n", protected, g_pmecc.configured);
/* Check if we need to re-configure */
@ -989,19 +1028,21 @@ int pmecc_configure(struct sam_nandcs_s *priv, uint16_t eccoffset,
{
/* No, we are already configured */
fvdbg("Already configured\n");
return OK;
}
/* Make sure that the requested offset greater than or equal to the
* minimum. The first few bytes of the spare ares is reserved for
* bad block indications. Therefore, ECC data must begin at an offset
* to skip over the bad block indicators.
/* Get a convenience pointer to the NAND model */
model = &priv->raw.model;
/* Get the offset and size of the ECC information in the spare area from
* the NAND scheme.
*/
if (eccoffset < PMECC_ECC_DEFAULT_STARTOFFSET)
{
eccoffset = PMECC_ECC_DEFAULT_STARTOFFSET;
}
DEBUGASSERT(model->scheme);
eccoffset = nandscheme_eccoffset(model->scheme);
eccsize = nandscheme_eccsize(model->scheme);
/* Get the number of sectors and the error correction per sector. This
* function will set the following structure values in order to get the
@ -1013,7 +1054,12 @@ int pmecc_configure(struct sam_nandcs_s *priv, uint16_t eccoffset,
* g_pmecc.desc.bcherr : The BCH_ERR value for the PMECC CFG register
*/
pmecc_pagelayout(priv->raw.model.pagesize, priv->raw.model.sparesize, eccoffset);
ret = pmecc_pagelayout(priv->raw.model.pagesize, eccsize);
if (ret < 0)
{
fdbg("ERROR: pmecc_pagelayout failed: %d\n", ret);
return ret;
}
/* Number of Sectors in one Page */
@ -1048,6 +1094,9 @@ int pmecc_configure(struct sam_nandcs_s *priv, uint16_t eccoffset,
#endif
}
fvdbg("sectorsz=%08x sectorsperpage=%d mm=%d\n",
g_pmecc.desc.sectorsz, sectorsperpage, g_pmecc.desc.mm);
switch (sectorsperpage)
{
case 1:
@ -1063,28 +1112,43 @@ int pmecc_configure(struct sam_nandcs_s *priv, uint16_t eccoffset,
g_pmecc.desc.pagesize = HSMC_PMECCFG_PAGESIZE_8SEC;
break;
default:
fdbg("ERROR: Unsupported sectors per page: %d\n", sectorsperpage);
fdbg("ERROR: Unsupported sectors per page: %d\n", sectorsperpage);
return -EINVAL;
}
g_pmecc.desc.nn = (1 << g_pmecc.desc.mm) - 1;
fvdbg("pagesize=%08x nn=%d\n", g_pmecc.desc.pagesize, g_pmecc.desc.nn);
/* Real value of ECC bit number correction (2, 4, 8, 12, 24) */
g_pmecc.desc.tt = g_pmecc.correctability;
if (((g_pmecc.desc.mm * g_pmecc.correctability) % 8) == 0)
if (((g_pmecc.desc.mm * g_pmecc.correctability) & 7) == 0)
{
g_pmecc.desc.eccsize = ((g_pmecc.desc.mm * g_pmecc.correctability) / 8) * sectorsperpage;
g_pmecc.desc.eccsize =
((g_pmecc.desc.mm * g_pmecc.correctability) >> 3) * sectorsperpage;
}
else
{
g_pmecc.desc.eccsize = (((g_pmecc.desc.mm * g_pmecc.correctability) / 8) + 1) * sectorsperpage;
g_pmecc.desc.eccsize =
(((g_pmecc.desc.mm * g_pmecc.correctability) >> 3) + 1) * sectorsperpage;
}
fvdbg("mm=%d correctability=%d eccsize=%d\n",
g_pmecc.desc.mm, g_pmecc.correctability, g_pmecc.desc.eccsize);
g_pmecc.desc.eccstart = eccoffset;
g_pmecc.desc.eccend = eccoffset + g_pmecc.desc.eccsize;
fvdbg("eccstart=%d eccend=%d sparesize=%d\n",
g_pmecc.desc.eccstart, g_pmecc.desc.eccend,
priv->raw.model.sparesize);
if (g_pmecc.desc.eccend > priv->raw.model.sparesize)
{
fdbg("ERROR: No room for ECC in spare bytes %d > %d\n",
g_pmecc.desc.eccend, priv->raw.model.sparesize);
return -ENOSPC;
}
@ -1144,7 +1208,7 @@ int pmecc_configure(struct sam_nandcs_s *priv, uint16_t eccoffset,
#if NAND_NPMECC_BANKS > 1
g_pmecc.cs = priv->cs;
#endif
return 0;
return OK;
}
/****************************************************************************

View File

@ -65,6 +65,16 @@
# undef CONFIG_SAMA5_EBICS3_PMECC
#endif
/* Only CS3 can support NAND. The rest of what follows is a fantasy */
# undef CONFIG_SAMA5_EBICS0_NAND
# undef CONFIG_SAMA5_EBICS1_NAND
# undef CONFIG_SAMA5_EBICS2_NAND
# undef CONFIG_SAMA5_EBICS0_PMECC
# undef CONFIG_SAMA5_EBICS1_PMECC
# undef CONFIG_SAMA5_EBICS2_PMECC
/* Disable PMECC support for any banks not enabled or configured for NAND */
#if !defined(CONFIG_SAMA5_EBICS0) || !defined(CONFIG_SAMA5_EBICS0_NAND)
@ -310,7 +320,6 @@ void pmecc_initialize(void);
*
* Input Parameters:
* priv - Pointer to a struct sam_nandcs_s instance.
* eccoffset - offset of the first ecc byte in spare zone.
* protected - True: The spare area is protected with the last sector of
* data.
* False: The spare area is skipped in read or write mode.
@ -321,8 +330,7 @@ void pmecc_initialize(void);
****************************************************************************/
struct sam_nandcs_s;
int pmecc_configure(struct sam_nandcs_s *priv, uint16_t eccoffset,
bool protected);
int pmecc_configure(struct sam_nandcs_s *priv, bool protected);
/****************************************************************************
* Name: pmecc_correction
@ -405,7 +413,7 @@ void pmecc_buildgf(uint32_t mm, int16_t* indexof, int16_t* alphato);
# define pmecc_enable()
# define pmecc_disable()
# define pmecc_initialize()
# define pmecc_configure(a,b,c) (0)
# define pmecc_configure(a,b) (0)
# define pmecc_get_eccsize() (0)
# define pmecc_get_pagesize() (0)

View File

@ -440,7 +440,7 @@ Creating and Using NORBOOT
7. An option is to use the SAM-BA tool to write the NORBOOT image into
Serial FLASH. Then, the system will boot from Serial FLASH by
copying the NORBOOT image in SRAM which will run and then start the
image in NOR FLASH automatically.
image in NOR FLASH automatically. This is a very convenient usage!
NOTES: (1) There is jumper on the CM module that must be closed to
enable use of the AT25 Serial Flash. (2) If using SAM-BA, make sure
@ -1278,7 +1278,8 @@ NOR FLASH Support
STATUS: I have been unable to execute these configurations from NOR FLASH
by closing the BMS jumper (J9). As far as I can tell, this jumper does
nothing on my board??? So I have been using the norboot configuration
exclusively to start the program-under-test in NOR FLASH (see below).
exclusively to start the program-under-test in NOR FLASH (see the section
entitled "Creating and Using NORBOOT" above.)
SDRAM Support
=============
@ -1397,6 +1398,10 @@ NAND Support
or Serial FLASH is a problem: In that case, the code always ends up
in the SAM-BA bootloader.
My understanding is that you can enable JTAG in this case by simply
entering any data on the DBG serial port. I have not tried this.
Instead, I just changed to boot from Serial Flash:
2. Booting from Serial Flash. The work around for this case is to put
the NORBOOT image into Serial FLASH. Then, the system will boot from
Serial FLASH by copying the NORBOOT image in SRAM which will run and
@ -2698,7 +2703,8 @@ Configurations
- Waits for you to break in with GDB.
At that point, you can set the PC and begin executing from NOR FLASH
under debug control.
under debug control. See the section entitled "Creating and Using
NORBOOT" above.
NOTES:

View File

@ -75,16 +75,17 @@ config MTD_CONFIG_ERASEDVALUE
comment "MTD Device Drivers"
config MTD_NAND
bool "Enable NAND support"
default n
---help---
Enable support for NAND FLASH devices.
config ARCH_NAND_HWECC
bool
default n
menuconfig MTD_NAND
bool "MTD NAND support"
default n
---help---
Enable support for NAND FLASH devices.
if MTD_NAND
config MTD_NAND_MAXNUMBLOCKS

View File

@ -65,11 +65,11 @@ struct nand_scheme_s
uint8_t eccsize; /* Number of bytes of ECC correction */
uint8_t nxbytes; /* Number of extra bytes */
/* ECC byte positions */
/* ECC byte position offsets */
uint8_t eccbytepos[CONFIG_MTD_NAND_MAXSPAREECCBYTES];
/* Extra byte positions */
/* Extra byte position offsets */
uint8_t xbytepos[CONFIG_MTD_NAND_MAXSPAREEXTRABYTES];
};
@ -138,6 +138,41 @@ void nandscheme_readbadblockmarker(FAR const struct nand_scheme_s *scheme,
void nandscheme_writebadblockmarker(FAR const struct nand_scheme_s *scheme,
FAR uint8_t *spare, uint8_t marker);
/****************************************************************************
* Name: nandscheme_eccoffset
*
* Description:
* Return the offset to the first byte of ECC information. This define
* makes the assumption that the first byte of the eccbytepos[] array
* is an offset to beginning of the ECC area. This might not necessarily
* be true!
*
* Input Parameters:
* scheme Pointer to a nand_scheme_s instance.
*
* Returned Values:
* Offset in the spare area to the first ECC byte
*
****************************************************************************/
#define nandscheme_eccoffset(s) ((s)->eccbytepos[0])
/****************************************************************************
* Name: nandscheme_eccsize
*
* Description:
* Return the size of the ECC information in the spare area
*
* Input Parameters:
* scheme Pointer to a nand_scheme_s instance.
*
* Returned Values:
* Size of the ECC information in the spare area.
*
****************************************************************************/
#define nandscheme_eccsize(s) ((s)->eccsize)
/****************************************************************************
* Name: nandscheme_readecc
*
@ -176,6 +211,41 @@ void nandscheme_readecc(FAR const struct nand_scheme_s *scheme,
void nandscheme_writeecc(FAR const struct nand_scheme_s *scheme,
FAR uint8_t *spare, FAR const uint8_t *ecc);
/****************************************************************************
* Name: nandscheme_xoffset
*
* Description:
* Return the offset to the first byte of extra information. This define
* makes the assumption that the first byte of the xbytepos[] array
* is an offset to the beginning of the extra information area. This
* might not necessarily be true!
*
* Input Parameters:
* scheme Pointer to a nand_scheme_s instance.
*
* Returned Values:
* Offset in the spare area to the first extra byte
*
****************************************************************************/
#define nandscheme_xoffset(s) ((s)->xbytepos[0])
/****************************************************************************
* Name: nandscheme_xsize
*
* Description:
* Return the size of the extra information in the spare area
*
* Input Parameters:
* scheme Pointer to a nand_scheme_s instance.
*
* Returned Values:
* Size of the extra information in the spare area.
*
****************************************************************************/
#define nandscheme_xsize(s) ((s)->nxbytes)
/****************************************************************************
* Name: nandscheme_readextra
*