SAMA5 NAND: Fix some PMECC setup logic

This commit is contained in:
Gregory Nutt 2013-12-04 11:56:56 -06:00
parent 9632d2a7f6
commit 8a97561ceb
3 changed files with 161 additions and 81 deletions

View File

@ -1739,9 +1739,7 @@ static int nand_read_pmecc(struct sam_nandcs_s *priv, off_t block,
/* Start a Data Phase */ /* Start a Data Phase */
regval = nand_getreg(SAM_HSMC_PMECCTRL); nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DATA);
regval |= HSMC_PMECCTRL_DATA;
nand_putreg(SAM_HSMC_PMECCTRL, regval);
regval = nand_getreg(SAM_HSMC_PMECCEADDR); regval = nand_getreg(SAM_HSMC_PMECCEADDR);
ret = nand_read(priv, true, (uint8_t *)data, pagesize + (regval + 1)); 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 ret;
int i; int i;
fvdbg("block=%d page=%d data=%p\n", (int)block, page, data);
DEBUGASSERT(priv && data); DEBUGASSERT(priv && data);
/* Make sure that we have exclusive access to the PMECC and that the PMECC /* 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_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 */ /* 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; int ret = 0;
fvdbg("block=%d page=%d data=%p\n", (int)block, page, data); 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 /* Make sure that we have exclusive access to the PMECC and that the PMECC
* is properly configured for this CS. * is properly configured for this CS.
*/ */
pmecc_lock(); 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 */ /* 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 */ /* Start a data phase */
regval = nand_getreg(SAM_HSMC_PMECCTRL); nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DATA);
regval |= HSMC_PMECCTRL_DATA;
nand_putreg(SAM_HSMC_PMECCTRL, regval);
regval = nand_getreg(SAM_HSMC_PMECCFG); regval = nand_getreg(SAM_HSMC_PMECCFG);
regval |= HSMC_PMECCFG_NANDWR_WRITE; regval |= HSMC_PMECCFG_NANDWR_WRITE;

View File

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

View File

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