nuttx/arch/arm/src/sama5/sam_pmecc.c
anjiahao d07792a343 Initialize global mutext/sem by NXMUTEX_INITIALIZER and SEM_INITIALIZER
Signed-off-by: anjiahao <anjiahao@xiaomi.com>
Signed-off-by: Xiang Xiao <xiaoxiang@xiaomi.com>
2022-11-14 09:34:04 +09:00

1514 lines
39 KiB
C

/****************************************************************************
* arch/arm/src/sama5/sam_pmecc.c
*
* Copyright (C) 2013, 2017 Gregory Nutt. All rights reserved.
* Author: Gregory Nutt <gnutt@nuttx.org>
*
* All of the detailed PMECC operations are taken directly from the Atmel
* NoOS sample code. The Atmel sample code has a BSD compatible license
* that requires this copyright notice:
*
* Copyright (c) 2012, Atmel Corporation
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
*
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in
* the documentation and/or other materials provided with the
* distribution.
* 3. Neither the names NuttX nor Atmel nor the names of its contributors
* may be used to endorse or promote products derived from this software
* without specific prior written permission.
*
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
* FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
* COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
* BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS
* OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED
* AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
* ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
* POSSIBILITY OF SUCH DAMAGE.
*
****************************************************************************/
/* References:
* SAMA5D3 Series Data Sheet
* Atmel NoOS sample code.
*/
/****************************************************************************
* Included Files
****************************************************************************/
#include <nuttx/config.h>
#include <nuttx/mtd/nand_config.h>
#include <stdint.h>
#include <stdbool.h>
#include <assert.h>
#include <errno.h>
#include <debug.h>
#include <nuttx/mtd/nand_model.h>
#include <nuttx/mtd/nand_scheme.h>
#include <nuttx/mutex.h>
#include "sam_pmecc.h"
#include "sam_nand.h"
/* Compile this logic only if there is at least one CS configure for NAND
* and with PMECC support enabled.
*/
#ifdef CONFIG_SAMA5_HAVE_PMECC
/****************************************************************************
* Pre-processor Definitions
****************************************************************************/
/* Number of bits of correction. These much match the (unshifted) values
* in the SMC_PMECCFG register BCH_ERR field.
*/
#define BCH_ERR2 0 /* 2 bit errors */
#define BCH_ERR4 1 /* 4 bit errors */
#define BCH_ERR8 2 /* 8 bit errors */
#define BCH_ERR12 3 /* 12 bit errors */
#define BCH_ERR24 4 /* 24 bit errors */
/* Defines the maximum value of the error correcting capability */
#define PMECC_MAX_CORRECTABILITY 25
/****************************************************************************
* Private Types
****************************************************************************/
/* This is the form of the PMECC descriptor that is passed to the ECC
* detection correction algorithm in ROM. The binary for of this structure
* cannot be altered!
*/
struct pmecc_desc_s
{
uint32_t pagesize; /* 0-3: See HSMC_PMECCFG_PAGESIZE_* definitions */
uint32_t sparesize; /* 4-7: The spare area size is equal to (SPARESIZE+1) bytes */
uint32_t sectorsz; /* 8-11: See HSMC_PMECCFG_SECTORSZ_* definitions */
uint32_t bcherr; /* 12-15: See HSMC_PMECCFG_BCHERR_* definitions */
uint32_t eccsize; /* 16-19: Real size in bytes of ECC in spare */
uint32_t eccstart; /* 20-23: The first byte address of the ECC area */
uint32_t eccend; /* 24-27: The last byte address of the ECC area */
uint32_t nandwr; /* 28-31: NAND Write Access */
uint32_t sparena; /* 32-35: Spare Enable */
uint32_t automode; /* 36-39: Automatic Mode */
uint32_t clkctrl; /* 40-43: PMECC Module data path Setup Time is CLKCTRL+1. */
uint32_t interrupt; /* 44-47: */
int32_t tt; /* 48-51: Error correcting capability */
int32_t mm; /* 52-55: Degree of the remainders, GF(2**mm) */
int32_t nn; /* 56-59: Length of codeword = nn=2**mm -1 */
int16_t *alphato; /* 60-63: Gallois field table */
int16_t *indexof; /* 64-67: Index of Gallois field table */
int16_t partsyn[100]; /* 68-267: */
int16_t si[100]; /* 268-467: Current syndrome value */
/* 468-: Sigma table */
int16_t smu[PMECC_MAX_CORRECTABILITY + 2]
[2 * PMECC_MAX_CORRECTABILITY + 1];
/* Polynomial order */
int16_t lmu[PMECC_MAX_CORRECTABILITY + 1];
};
/* PMECC state data */
struct sam_pmecc_s
{
bool configured; /* True: Configured for some HSMC NAND bank */
#if NAND_NPMECC_BANKS > 1
mutex_t lock; /* For mutually exclusive access to the PMECC */
uint8_t cs; /* Currently configured for this bank */
#endif
bool sector1k; /* True: 1024B sector size; False: 512B sector size */
uint8_t nsectors; /* Number of sectors per page */
uint8_t correctability; /* Number of correctable bits per sector */
struct pmecc_desc_s desc; /* Atmel PMECC descriptor */
};
/* This is the type of the ROM detection/correction function
*
* REVISIT: Where 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 */
static struct sam_pmecc_s g_pmecc =
{
.lock = NXMUTEX_INITIALIZER,
};
/* Maps BCH_ERR correctability register value to number of errors per
* sector.
*/
static const uint8_t g_correctability[5] =
{
2, 4, 8, 12, 24
};
/****************************************************************************
* Private Functions
****************************************************************************/
/****************************************************************************
* Name: pmecc_gensyn
*
* Description:
* Build the pseudo syndromes table
*
* Input Parameters:
* sector - Targeted sector.
*
* Returned Value:
* None
*
****************************************************************************/
#ifndef CONFIG_SAMA5_PMECC_EMBEDDEDALGO
static void pmecc_gensyn(uint32_t sector)
{
int16_t *remainder;
int i;
remainder = (int16_t *)SAM_HSMC_REM_BASE(sector);
for (i = 0; i < (uint32_t)g_pmecc.desc.tt; i++)
{
/* Fill odd syndromes */
g_pmecc.desc.partsyn[1 + (2 * i)] = remainder[i];
}
}
#endif /* CONFIG_SAMA5_PMECC_EMBEDDEDALGO */
/****************************************************************************
* Name: pmecc_substitute
*
* Description:
* The pmecc_substitute function evaluates the polynomial remainder, with
* different values of the field primitive elements.
*
* Input Parameters:
* None
*
* Returned Value:
* Zero (always)
*
****************************************************************************/
#ifndef CONFIG_SAMA5_PMECC_EMBEDDEDALGO
static uint32_t pmecc_substitute(void)
{
int16_t *si = g_pmecc.desc.si;
int16_t *partsyn = g_pmecc.desc.partsyn;
int16_t *alphato = g_pmecc.desc.alphato;
int16_t *indexof = g_pmecc.desc.indexof;
int i;
int j;
/* si[] is a table that holds the current syndrome value, an element of
* that table belongs to the field.
*/
for (i = 1; i < 2 * PMECC_MAX_CORRECTABILITY; i++)
{
si[i] = 0;
}
/* Computation 2t syndromes based on S(x) */
/* Odd syndromes */
for (i = 1; i <= 2 * g_pmecc.desc.tt - 1; i = i + 2)
{
si[i] = 0;
for (j = 0; j < g_pmecc.desc.mm; j++)
{
if (partsyn[i] & ((uint16_t)0x1 << j))
{
si[i] = alphato[(i * j)] ^ si[i];
}
}
}
/* Even syndrome = (Odd syndrome) ** 2 */
for (i = 2; i <= 2 * g_pmecc.desc.tt; i = i + 2)
{
j = i / 2;
if (si[j] == 0)
{
si[i] = 0;
}
else
{
si[i] = alphato[(2 * indexof[si[j]]) % g_pmecc.desc.nn];
}
}
return 0;
}
#endif /* CONFIG_SAMA5_PMECC_EMBEDDEDALGO */
/****************************************************************************
* Name: pmecc_getsigma
*
* Description:
* The substitute function finding the value of the error location
* polynomial.
*
* Input Parameters:
* None
*
* Returned Value:
* Zero (always)
*
****************************************************************************/
#ifndef CONFIG_SAMA5_PMECC_EMBEDDEDALGO
static uint32_t pmecc_getsigma(void)
{
uint32_t dmu0count;
int16_t *lmu = g_pmecc.desc.lmu;
int16_t *si = g_pmecc.desc.si;
int16_t tt = g_pmecc.desc.tt;
int32_t mu[PMECC_MAX_CORRECTABILITY + 1]; /* Mu */
int32_t dmu[PMECC_MAX_CORRECTABILITY + 1]; /* Discrepancy */
int32_t delta[PMECC_MAX_CORRECTABILITY + 1]; /* Delta order */
int32_t largest;
int32_t diff;
int ro; /* Index of largest delta */
int i;
int j;
int k;
dmu0count = 0;
/* First Row */
/* Mu */
mu[0] = -1; /* Actually -1/2 */
/* Sigma(x) set to 1 */
for (i = 0; i < (2 * PMECC_MAX_CORRECTABILITY + 1); i++)
{
g_pmecc.desc.smu[0][i] = 0;
}
g_pmecc.desc.smu[0][0] = 1;
/* Discrepancy set to 1 */
dmu[0] = 1;
/* Polynom order set to 0 */
lmu[0] = 0;
/* delta set to -1 */
delta[0] = (mu[0] * 2 - lmu[0]) >> 1;
/* Second row */
/* Mu */
mu[1] = 0;
/* Sigma(x) set to 1 */
for (i = 0; i < (2 * PMECC_MAX_CORRECTABILITY + 1); i++)
{
g_pmecc.desc.smu[1][i] = 0;
}
g_pmecc.desc.smu[1][0] = 1;
/* Discrepancy set to S1 */
dmu[1] = si[1];
/* Polynom order set to 0 */
lmu[1] = 0;
/* Delta set to 0 */
delta[1] = (mu[1] * 2 - lmu[1]) >> 1;
/* Initialize the Sigma(x) last row */
for (i = 0; i < (2 * PMECC_MAX_CORRECTABILITY + 1); i++)
{
g_pmecc.desc.smu[tt + 1][i] = 0;
}
for (i = 1; i <= tt; i++)
{
mu[i + 1] = i << 1;
/* Compute Sigma (Mu+1) and L(mu). */
/* Check if discrepancy is set to 0 */
if (dmu[i] == 0)
{
dmu0count++;
if ((tt - (lmu[i] >> 1) - 1) & 0x1)
{
if (dmu0count == (uint32_t)((tt - (lmu[i] >> 1) - 1) / 2) + 2)
{
for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
{
g_pmecc.desc.smu[tt + 1][j] = g_pmecc.desc.smu[i][j];
}
lmu[tt + 1] = lmu[i];
return 0;
}
}
else
{
if (dmu0count == (uint32_t)((tt - (lmu[i] >> 1) - 1) / 2) + 1)
{
for (j = 0; j <= (lmu[i] >> 1) + 1; j++)
{
g_pmecc.desc.smu[tt + 1][j] = g_pmecc.desc.smu[i][j];
}
lmu[tt + 1] = lmu[i];
return 0;
}
}
/* Copy polynom */
for (j = 0; j <= lmu[i] >> 1; j++)
{
g_pmecc.desc.smu[i + 1][j] = g_pmecc.desc.smu[i][j];
}
/* Copy previous polynom order to the next */
lmu[i + 1] = lmu[i];
}
else
{
ro = 0;
largest = -1;
/* find largest delta with dmu != 0 */
for (j = 0; j < i; j++)
{
if (dmu[j])
{
if (delta[j] > largest)
{
largest = delta[j];
ro = j;
}
}
}
/* Compute difference */
diff = (mu[i] - mu[ro]);
/* Compute degree of the new smu polynomial */
if ((lmu[i] >> 1) > ((lmu[ro] >> 1) + diff))
{
lmu[i + 1] = lmu[i];
}
else
{
lmu[i + 1] = ((lmu[ro] >> 1) + diff) * 2;
}
/* Init smu[i+1] with 0 */
for (k = 0; k < (2 * PMECC_MAX_CORRECTABILITY + 1); k++)
{
g_pmecc.desc.smu[i + 1][k] = 0;
}
/* Compute smu[i+1] */
for (k = 0; k <= lmu[ro] >> 1; k++)
{
if (g_pmecc.desc.smu[ro][k] && dmu[i])
{
g_pmecc.desc.smu[i + 1][k + diff] =
g_pmecc.desc.alphato[(g_pmecc.desc.indexof[dmu[i]] +
(g_pmecc.desc.nn - g_pmecc.desc.indexof[dmu[ro]]) +
g_pmecc.desc.indexof[g_pmecc.desc.smu[ro][k]]) %
g_pmecc.desc.nn];
}
}
for (k = 0; k <= lmu[i] >> 1; k++)
{
g_pmecc.desc.smu[i + 1][k] ^= g_pmecc.desc.smu[i][k];
}
}
/* End Compute Sigma (Mu+1) and L(mu) */
/* In either case compute delta */
delta[i + 1] = (mu[i + 1] * 2 - lmu[i + 1]) >> 1;
/* Do not compute discrepancy for the last iteration */
if (i < tt)
{
for (k = 0 ; k <= (lmu[i + 1] >> 1); k++)
{
if (k == 0)
{
dmu[i + 1] = si[2 * (i - 1) + 3];
}
/* Check if one operand of the multiplier is null, its index
* is -1
*/
else if (g_pmecc.desc.smu[i + 1][k] &&
si[2 * (i - 1) + 3 - k])
{
dmu[i + 1] =
g_pmecc.desc.alphato[
(g_pmecc.desc.indexof[g_pmecc.desc.smu[i + 1][k]] +
g_pmecc.desc.indexof[si[2 * (i - 1) + 3 - k]]) %
g_pmecc.desc.nn] ^ dmu[i + 1];
}
}
}
}
return 0;
}
#endif /* CONFIG_SAMA5_PMECC_EMBEDDEDALGO */
/****************************************************************************
* Name: pmecc_errorlocation
*
* Description:
* Initialize the PMECC Error Location peripheral and start the error
* location processing
*
* Input Parameters:
* bitsize - Size of the sector in bits.
*
* Returned Value:
* Number of errors (or -1 on an error)
*
****************************************************************************/
#ifndef CONFIG_SAMA5_PMECC_EMBEDDEDALGO
static int32_t pmecc_errorlocation(uint32_t bitsize)
{
uint32_t *sigma;
uint32_t errornumber;
uint32_t nroots;
uint32_t regval;
uint32_t alphax;
/* Disable PMECC Error Location IP */
nand_putreg(SAM_HSMC_ELDIS, 0xffffffff);
errornumber = 0;
alphax = 0;
sigma = (uint32_t *)SAM_HSMC_SIGMA0;
for (alphax = 0;
alphax <= (uint32_t)(g_pmecc.desc.lmu[g_pmecc.desc.tt + 1] >> 1);
alphax++)
{
*sigma++ = g_pmecc.desc.smu[g_pmecc.desc.tt + 1][alphax];
errornumber++;
}
regval = nand_getreg(SAM_HSMC_ELCFG);
regval |= HSMC_ELCFG_ERRNUM(errornumber - 1);
nand_putreg(SAM_HSMC_ELCFG, regval);
/* Enable error location process */
nand_putreg(SAM_HSMC_ELEN, bitsize);
while ((nand_getreg(SAM_HSMC_ELISR) & HSMC_ELIINT_DONE) == 0);
nroots = (nand_getreg(SAM_HSMC_ELISR) & HSMC_ELISR_ERRCNT_MASK) >>
HSMC_ELISR_ERRCNT_SHIFT;
/* Number of roots == degree of smu hence <= tt */
if (nroots == (uint32_t)(g_pmecc.desc.lmu[g_pmecc.desc.tt + 1] >> 1))
{
return (errornumber - 1);
}
/* Number of roots not match the degree of smu ==> unable to correct
* error
*/
return -1;
}
#endif /* CONFIG_SAMA5_PMECC_EMBEDDEDALGO */
/****************************************************************************
* Name: pmecc_errorcorrection
*
* Description:
* Correct errors indicated in the PMECCEL error location registers.
*
* Input Parameters:
* sectorbase - Base address of the sector.
* extrabytes - Number of extra bytes of the sector (encoded spare area,
* only for the last sector)
* nerrors Number of error to correct
*
* Returned Value:
* Number of errors
*
****************************************************************************/
#ifndef CONFIG_SAMA5_PMECC_EMBEDDEDALGO
static uint32_t pmecc_errorcorrection(uintptr_t sectorbase,
uint32_t extrabytes, uint32_t nerrors)
{
uint32_t *errpos;
uint32_t bytepos;
uint32_t bitpos;
uint32_t sectorsz;
uint32_t eccsize;
uint32_t eccend;
errpos = (uint32_t *)SAM_HSMC_ERRLOC_BASE(0);
if ((nand_getreg(SAM_HSMC_PMECCFG) & HSMC_PMECCFG_SECTORSZ_MASK) ==
HSMC_PMECCFG_SECTORSZ_512)
{
sectorsz = 512;
}
else
{
sectorsz = 1024;
}
/* Get number of ECC bytes */
eccend = nand_getreg(SAM_HSMC_PMECCEADDR);
eccsize = (eccend - nand_getreg(SAM_HSMC_PMECCSADDR)) + 1;
while (nerrors)
{
bytepos = (*errpos - 1) / 8;
bitpos = (*errpos - 1) % 8;
/* If error is located in the data area (not in ECC) */
if (bytepos < (sectorsz + extrabytes))
{
/* If the error position is before ECC area */
if (bytepos < sectorsz + nand_getreg(SAM_HSMC_PMECCSADDR))
{
fwarn("WARNING: Correct error bit @[Byte %d, Bit %d]\n",
(int)bytepos, (int)bitpos);
if (*(uint8_t *)(sectorbase + bytepos) & (1 << bitpos))
{
*(uint8_t *)(sectorbase + bytepos) &=
(0xff ^ (1 << bitpos));
}
else
{
*(uint8_t *)(sectorbase + bytepos) |=
(1 << bitpos);
}
}
else
{
if (*(uint8_t *)(sectorbase + bytepos + eccsize) &
(1 << bitpos))
{
*(uint8_t *)(sectorbase + bytepos + eccsize) &=
(0xff ^ (1 << bitpos));
}
else
{
*(uint8_t *)(sectorbase + bytepos + eccsize) |=
(1 << bitpos);
}
}
}
errpos++;
nerrors--;
}
return 0;
}
#endif /* CONFIG_SAMA5_PMECC_EMBEDDEDALGO */
/****************************************************************************
* Name: pmecc_correctionalgo
*
* Description:
* Launch error detection functions and correct corrupted bits.
*
* Input Parameters:
* isr - Value of the PMECC status register.
* data - Base address of the buffer containing the page to be corrected.
*
* Returned Value:
* 0 if all errors have been corrected, 1 if too many errors detected
*
****************************************************************************/
#ifndef CONFIG_SAMA5_PMECC_EMBEDDEDALGO
static uint32_t pmecc_correctionalgo(uint32_t isr, uintptr_t data)
{
uintptr_t sectorbase;
uint32_t sector = 0;
uint32_t sectorsz;
int32_t nerrors;
unsigned int mm;
/* Set the sector size (512 or 1024 bytes) */
if ((g_pmecc.desc.sectorsz & HSMC_PMECCFG_SECTORSZ_MASK) != 0)
{
sectorsz = 1024;
mm = 14;
nand_putreg(SAM_HSMC_ELCFG, HSMC_ELCFG_SECTORSZ_1024);
}
else
{
sectorsz = 512;
mm = 13;
nand_putreg(SAM_HSMC_ELCFG, HSMC_ELCFG_SECTORSZ_512);
}
#define HSMC_PAGESIZE \
(1 << ((nand_getreg(SAM_HSMC_PMECCFG) & HSMC_PMECCFG_PAGESIZE_MASK) >> \
HSMC_PMECCFG_PAGESIZE_SHIFT))
while (sector < (uint32_t)HSMC_PAGESIZE && isr != 0)
{
nerrors = 0;
if ((isr & 1) != 0)
{
sectorbase = data + (sector * sectorsz);
pmecc_gensyn(sector);
pmecc_substitute();
pmecc_getsigma();
/* Number of bits of the sector + ecc */
nerrors = pmecc_errorlocation((sectorsz * 8) +
(g_pmecc.desc.tt * mm));
if (nerrors == -1)
{
return 1;
}
else
{
/* Extra byte is 0 */
pmecc_errorcorrection(sectorbase, 0, nerrors);
}
}
sector++;
isr = isr >> 1;
}
return 0;
}
#endif /* CONFIG_SAMA5_PMECC_EMBEDDEDALGO */
/****************************************************************************
* Name: pmecc_bcherr512
*
* Description:
* Get the correctabity that could be achieved using a 512 byte sector
*
****************************************************************************/
static int pmecc_bcherr512(uint8_t nsectors, uint16_t eccsize)
{
/* 39-bytes per 512 byte sector are required correctability of 24 errors */
if (eccsize >= 39 * ((unsigned int)nsectors))
{
return BCH_ERR24;
}
/* 20-bytes per 512 byte sector are required correctability of 12 errors */
else if (eccsize >= (20 * (unsigned int)nsectors))
{
return BCH_ERR12;
}
/* 13-bytes per 512 byte sector are required correctability of 8 errors */
else if (eccsize >= (13 * (unsigned int)nsectors))
{
return BCH_ERR8;
}
/* 7-bytes per 512 byte sector are required correctability of 4 errors */
else if (eccsize >= (7 * (unsigned int) nsectors))
{
return BCH_ERR4;
}
/* 4-bytes per 512 byte sector are required correctability of 2 errors */
else if (eccsize >= (4 * (unsigned int) nsectors))
{
return BCH_ERR2;
}
return -EINVAL;
}
/****************************************************************************
* Name: pmecc_bcherr512
*
* Description:
* Get the correctabity that could be achieved using a 512 byte sector
*
****************************************************************************/
static int pmecc_bcherr1k(uint8_t nsectors, uint16_t eccsize)
{
/* 42-bytes per 1024 byte sector are required correctability of 24 errors */
if (eccsize >= 42 * ((unsigned int)nsectors))
{
return BCH_ERR24;
}
/* 21-bytes per 1024 byte sector are required correctability of 12 errors */
else if (eccsize >= (21 * (unsigned int)nsectors))
{
return BCH_ERR12;
}
/* 14-bytes per 1024 byte sector are required correctability of 8 errors */
else if (eccsize >= (14 * (unsigned int)nsectors))
{
return BCH_ERR8;
}
/* 7-bytes per 1024 byte sector are required correctability of 4 errors */
else if (eccsize >= (7 * (unsigned int) nsectors))
{
return BCH_ERR4;
}
/* 4-bytes per 1024 byte sector are required correctability of 2 errors */
else if (eccsize >= (4 * (unsigned int) nsectors))
{
return BCH_ERR2;
}
return -EINVAL;
}
/****************************************************************************
* Name: pmecc_pagelayout
*
* Description:
* Given the size of the data region and the size of the ECC region,
* determine the optimal sector size and correctability.
*
****************************************************************************/
static int pmecc_pagelayout(uint16_t datasize, uint16_t eccsize)
{
uint16_t correctability512;
uint16_t correctability1k;
uint8_t nsectors512;
uint8_t nsectors1k;
uint8_t bcherr;
int bcherr512;
int bcherr1k;
int selector;
finfo("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, eccsize);
if (bcherr512 < 0)
{
fwarn("WARNING: Cannot realize 512B sectors\n");
}
else
{
selector = 1;
}
finfo("nsectors512=%d bcherr512=%d selector=%d\n",
nsectors512, bcherr512, selector);
/* Try for 1024 byte sectors */
if ((datasize & 0x000003ff) == 0)
{
nsectors1k = (datasize >> 10);
bcherr1k = pmecc_bcherr1k(nsectors1k, eccsize);
}
else
{
nsectors1k = 0;
bcherr1k = -EINVAL;
}
if (bcherr1k < 0)
{
fwarn("WARNING: Cannot realize 1KB sectors\n");
}
else
{
selector |= 2;
}
finfo("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);
}
break;
case 3: /* Both 512B and 1KB sectors possible */
{
correctability512 = nsectors512 * g_correctability[bcherr512];
correctability1k = nsectors1k * g_correctability[bcherr1k];
/* Use 512B sectors unless we can do better with 1K 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 */
g_pmecc.correctability = g_correctability[bcherr];
/* And the correctly shifted BCH_ERR register value */
g_pmecc.desc.bcherr = ((uint32_t)bcherr << HSMC_PMECCFG_BCHERR_SHIFT);
finfo("sector1k=%d nsectors=%d bcherr=%d correctability=%d\n",
g_pmecc.sector1k, g_pmecc.nsectors, bcherr, g_pmecc.correctability);
return OK;
}
/****************************************************************************
* Public Functions
****************************************************************************/
/****************************************************************************
* Name: pmecc_configure
*
* Description:
* Configure and Initialize the PMECC peripheral for this CS.
*
* Input Parameters:
* priv - Pointer to a struct sam_nandcs_s instance.
* protected - True: The spare area is protected with the last sector of
* data.
* False: The spare area is skipped in read or write mode.
*
* Returned Value:
* OK on success; a negated errno value on failure.
*
****************************************************************************/
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;
finfo("protected=%d configured=%d\n", protected, g_pmecc.configured);
/* Check if we need to re-configure */
#if NAND_NPMECC_BANKS > 1
if (g_pmecc.configured && g_pmecc.cs == priv->cs)
#else
if (g_pmecc.configured)
#endif
{
/* No, we are already configured */
finfo("Already configured\n");
return OK;
}
/* 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.
*/
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
* best overall correctability:
*
* g_pmecc.sector1k : True if we are using 1024B sectors
* g_pmecc.nsectors : The number of sectors per page
* g_pmecc.correctability : Number of correctable bits per sector
* g_pmecc.desc.bcherr : The BCH_ERR value for the PMECC CFG register
*/
ret = pmecc_pagelayout(priv->raw.model.pagesize, eccsize);
if (ret < 0)
{
ferr("ERROR: pmecc_pagelayout failed: %d\n", ret);
return ret;
}
/* Number of Sectors in one Page */
if (g_pmecc.sector1k)
{
/* 1024 bytes per sector */
g_pmecc.desc.sectorsz = HSMC_PMECCFG_SECTORSZ_1024;
sectorsperpage = (priv->raw.model.pagesize >> 10);
g_pmecc.desc.mm = 14;
#if defined (CONFIG_SAMA5_PMECC_GALOIS_TABLE1024_ROMADDR) && \
defined (CONFIG_SAMA5_PMECC_GALOIS_ROMTABLES)
g_pmecc.desc.alphato =
(int16_t *)&(pmecc_gf1024[PMECC_GF_SIZEOF_1024]);
g_pmecc.desc.indexof =
(int16_t *)&(pmecc_gf1024[0]);
#else
g_pmecc.desc.alphato =
(int16_t *)&(pmecc_gf1024[PMECC_GF_ALPHA_TO]);
g_pmecc.desc.indexof =
(int16_t *)&(pmecc_gf1024[PMECC_GF_INDEX_OF]);
#endif
}
else
{
/* 512 bytes per sector */
g_pmecc.desc.sectorsz = HSMC_PMECCFG_SECTORSZ_512;
sectorsperpage = (priv->raw.model.pagesize >> 9);
g_pmecc.desc.mm = 13;
#if defined (CONFIG_SAMA5_PMECC_GALOIS_TABLE512_ROMADDR) && \
defined (CONFIG_SAMA5_PMECC_GALOIS_ROMTABLES)
g_pmecc.desc.alphato =
(int16_t *)&(pmecc_gf512[PMECC_GF_SIZEOF_512]);
g_pmecc.desc.indexof =
(int16_t *)&(pmecc_gf512[0]);
#else
g_pmecc.desc.alphato =
(int16_t *)&(pmecc_gf512[PMECC_GF_ALPHA_TO]);
g_pmecc.desc.indexof =
(int16_t *)&(pmecc_gf512[PMECC_GF_INDEX_OF]);
#endif
}
finfo("sectorsz=%08x sectorsperpage=%d mm=%d\n",
g_pmecc.desc.sectorsz, sectorsperpage, g_pmecc.desc.mm);
switch (sectorsperpage)
{
case 1:
g_pmecc.desc.pagesize = HSMC_PMECCFG_PAGESIZE_1SEC;
break;
case 2:
g_pmecc.desc.pagesize = HSMC_PMECCFG_PAGESIZE_2SEC;
break;
case 4:
g_pmecc.desc.pagesize = HSMC_PMECCFG_PAGESIZE_4SEC;
break;
case 8:
g_pmecc.desc.pagesize = HSMC_PMECCFG_PAGESIZE_8SEC;
break;
default:
ferr("ERROR: Unsupported sectors per page: %d\n", sectorsperpage);
return -EINVAL;
}
g_pmecc.desc.nn = (1 << g_pmecc.desc.mm) - 1;
finfo("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) & 7) == 0)
{
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) >> 3) + 1) *
sectorsperpage;
}
finfo("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;
finfo("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)
{
ferr("ERROR: No room for ECC in spare bytes %d > %d\n",
g_pmecc.desc.eccend, priv->raw.model.sparesize);
return -ENOSPC;
}
/* Save the size of the spare area.
*
* REVISIT: Could we save a bit by setting this to eccend since there is
* no need to read beyond that?
*/
g_pmecc.desc.sparesize = priv->raw.model.sparesize;
#if 0
g_pmecc.desc.nandwr = PMECC_CFG_NANDWR; /* NAND write access */
#else
g_pmecc.desc.nandwr = 0; /* NAND Read access */
#endif
if (protected)
{
g_pmecc.desc.sparena = HSMC_PMECCFG_SPARE_ENABLE;
}
else
{
g_pmecc.desc.sparena = 0;
}
/* PMECC_CFG_AUTO indicates that the spare is error protected. In this
* case, the ECC computation takes into account the whole spare area
* minus the ECC area in the ECC computation operation
*
* NOTE: At 133 MHz, the clkctrl field must be programmed with 2,
* indicating that the setup time is 3 clock cycles.
*/
g_pmecc.desc.automode = 0;
g_pmecc.desc.clkctrl = 2;
g_pmecc.desc.interrupt = 0;
/* Disable ECC module */
nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_DISABLE);
/* Reset the ECC module */
nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_RST);
regval = g_pmecc.desc.bcherr | g_pmecc.desc.sectorsz |
g_pmecc.desc.pagesize | g_pmecc.desc.nandwr |
g_pmecc.desc.sparena | g_pmecc.desc.automode;
nand_putreg(SAM_HSMC_PMECCFG, regval);
nand_putreg(SAM_HSMC_PMECCSAREA, g_pmecc.desc.sparesize - 1);
nand_putreg(SAM_HSMC_PMECCSADDR, g_pmecc.desc.eccstart);
nand_putreg(SAM_HSMC_PMECCEADDR, g_pmecc.desc.eccend - 1);
/* Disable all interrupts */
nand_putreg(SAM_HSMC_PMECCIDR, 0xff);
/* Enable ECC module */
nand_putreg(SAM_HSMC_PMECCTRL, HSMC_PMECCTRL_ENABLE);
/* Now we are configured */
g_pmecc.configured = true;
#if NAND_NPMECC_BANKS > 1
g_pmecc.cs = priv->cs;
#endif
return OK;
}
/****************************************************************************
* Name: pmecc_lock
*
* Description:
* Get exclusive access to PMECC hardware
*
* Input Parameters:
* None
*
* Returned Value:
* Normally success (OK) is returned, but the error -ECANCELED may be
* return in the event that task has been canceled.
*
****************************************************************************/
#if NAND_NPMECC_BANKS > 1
int pmecc_lock(void)
{
return nxmutex_lock(&g_pmecc.lock);
}
#endif
/****************************************************************************
* Name: pmecc_unlock
*
* Description:
* Relinquish exclusive access to PMECC hardware
*
* Input Parameters:
* None
*
* Returned Value:
* None
*
****************************************************************************/
#if NAND_NPMECC_BANKS > 1
void pmecc_unlock(void)
{
nxmutex_unlock(&g_pmecc.lock);
}
#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:
* Various PMECC accessor functions
*
* pmecc_get_eccsize() - Returns the raw ECS size in bytes
* pmecc_get_pagesize() - Returns encoded HSMC_PMECCFG_PAGESIZE_* value
*
* Input Parameters:
* None
*
* Returned Value:
* The requested value
*
* Assumptions:
* PMECC has been initialized for the CS and the caller holds the PMECC
* lock.
*
****************************************************************************/
uint32_t pmecc_get_eccsize(void)
{
return g_pmecc.desc.eccsize;
}
uint32_t pmecc_get_pagesize(void)
{
return g_pmecc.desc.pagesize;
}
/****************************************************************************
* Name: pmecc_buildgf
*
* Description:
* This function is able to build Galois Field.
*
* Input Parameters:
* mm - Degree of the remainders.
* indexof - Pointer to a buffer for indexof table.
* alphato - Pointer to a buffer for alphato table.
*
* Returned Value:
* None
*
****************************************************************************/
#ifdef CONFIG_SAMA5_PMECC_GALOIS_CUSTOM
void pmecc_buildgf(uint32_t mm, int16_t *indexof, int16_t *alphato)
{
uint32_t i;
uint32_t mask;
uint32_t nn;
uint32_t p[15];
nn = (1 << mm) - 1;
/* Set default value */
for (i = 1; i < mm; i++)
{
p[i] = 0;
}
/* 1 + X^mm */
p[0] = 1;
p[mm] = 1;
/* others */
if (mm == 3)
{
p[1] = 1;
}
else if (mm == 4)
{
p[1] = 1;
}
else if (mm == 5)
{
p[2] = 1;
}
else if (mm == 6)
{
p[1] = 1;
}
else if (mm == 7)
{
p[3] = 1;
}
else if (mm == 8)
{
p[2] = p[3] = p[4] = 1;
}
else if (mm == 9)
{
p[4] = 1;
}
else if (mm == 10)
{
p[3] = 1;
}
else if (mm == 11)
{
p[2] = 1;
}
else if (mm == 12)
{
p[1] = p[4] = p[6] = 1;
}
else if (mm == 13)
{
p[1] = p[3] = p[4] = 1;
}
else if (mm == 14)
{
p[1] = p[6] = p[10] = 1;
}
else if (mm == 15)
{
p[1] = 1;
}
/* First
*
* build alpha ^ mm it will help to generate the field (primitive)
*/
alphato[mm] = 0;
for (i = 0; i < mm; i++)
{
if (p[i])
{
alphato[mm] |= 1 << i;
}
}
/* Second
*
* Build elements from 0 to mm - 1. Very easy because degree is less than
* mm so it is just a logical shift ! (only the remainder)
*/
mask = 1;
for (i = 0; i < mm; i++)
{
alphato[i] = mask;
indexof[alphato[i]] = i;
mask <<= 1;
}
indexof[alphato[mm]] = mm ;
/* Use a mask to select the MSB bit of the LFSR ! */
mask >>= 1; /* Previous value moust be decremented */
/* Then finish the building */
for (i = mm + 1; i <= nn; i++)
{
/* Check if the msb bit of the lfsr is set */
if (alphato[i - 1] & mask)
{
/* Feedback loop is set */
alphato[i] = alphato[mm] ^ ((alphato[i - 1] ^ mask) << 1);
}
else
{
/* Only shift is enabled */
alphato[i] = alphato[i - 1] << 1;
}
/* lookup table */
indexof[alphato[i]] = i % nn;
}
/* Of course index of 0 is undefined in a multiplicative field */
indexof[0] = -1;
}
#endif /* CONFIG_SAMA5_PMECC_GALOIS_CUSTOM */
#endif /* CONFIG_SAMA5_HAVE_PMECC */