blob: 571e4d5b69f3a0c84f28261ab77d301e38afc849 [file] [log] [blame]
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/nand.h>
#include <asm/io.h>
#include "ctrlEnv/sys/mvCpuIf.h"
#include "boardEnv/mvBoardEnvLib.h"
#ifdef CONFIG_MTD_NAND_LNC_RS_ECC
#include "nand_lnc.h"
#endif
#undef DEBUG
#ifdef DEBUG
#define DBG(fmt, arg...) mvOsPrintf(KERN_INFO fmt, ##arg)
#else
#define DBG(fmt, arg...)
#endif
extern MV_U32 boardGetDevCSNum(MV_32 devNum, MV_BOARD_DEV_CLASS devType);
static struct mtd_info *mv_mtd;
static unsigned long baseaddr;
unsigned int mv_nand_ecc;
#ifdef CONFIG_MTD_PARTITIONS
extern struct mtd_partition nand_parts_info[];
extern int nand_parts_num;
static const char *part_probes[] __initdata = { "cmdlinepart", NULL };
#define MV_NUM_OF_NAND_PARTS nand_parts_num
#endif
static void board_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{
struct nand_chip *this = (struct nand_chip *)mtd->priv;
if (ctrl & NAND_CTRL_CHANGE) {
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W & ~3);
ctrl &= ~NAND_CTRL_CHANGE;
switch (ctrl) {
case NAND_CTRL_CLE:
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | 1); /*x8=>1, x16=>2*/
break;
case NAND_CTRL_ALE:
this->IO_ADDR_W = (void __iomem *)((unsigned long)this->IO_ADDR_W | 2); /*x8=>2, x16=>4*/
break;
}
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
}
static void mv_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
{
struct nand_chip *chip = mtd->priv;
void __iomem *io_base = chip->IO_ADDR_R;
uint64_t *buf64;
int i = 0;
while (len && (unsigned long)buf & 7) {
*buf++ = readb(io_base);
len--;
}
buf64 = (uint64_t *)buf;
while (i < len/8) {
uint64_t x;
__asm__ __volatile__ ("ldrd\t%0, [%1]" : "=r" (x) : "r" (io_base));
buf64[i++] = x;
}
i *= 8;
while (i < len)
buf[i++] = readb(io_base);
}
int __init mv_nand_init(void)
{
struct nand_chip *this;
int err = 0;
int num_of_parts = 0;
const char *part_type = 0;
struct mtd_partition *mtd_parts = 0;
u32 physaddr;
int nand_dev_num;
MV_CPU_DEC_WIN addr_win;
nand_dev_num = boardGetDevCSNum(0, BOARD_DEV_NAND_FLASH);
if (-1 == nand_dev_num) {
printk(KERN_INFO "NAND init: NAND device not found on board\n");
err = -ENODEV;
goto out;
}
if (MV_OK != mvCpuIfTargetWinGet((nand_dev_num), &addr_win)) {
printk(KERN_INFO "Failed to init NAND MTD (boot-CS window %d err).\n", nand_dev_num);
err = -ENODEV;
goto out;
}
if (!addr_win.enable) {
printk(KERN_INFO "Failed to init NAND MTD (boot-CS window disabled).\n");
err = -ENODEV;
goto out;
}
physaddr = addr_win.addrWin.baseLow;
mv_mtd = kmalloc(sizeof(struct mtd_info)+sizeof(struct nand_chip), GFP_KERNEL);
if (!mv_mtd) {
printk(KERN_INFO "Failed to allocate NAND MTD structure\n");
err = -ENOMEM;
goto out;
}
memset((char *)mv_mtd, 0, sizeof(struct mtd_info) + sizeof(struct nand_chip));
baseaddr = (unsigned long)ioremap(physaddr, 1024);
if (!baseaddr) {
printk(KERN_INFO "Failed to remap NAND MTD\n");
err = -EIO;
goto out_mtd;
}
this = (struct nand_chip *)((char *)mv_mtd+sizeof(struct mtd_info));
mv_mtd->priv = this;
this->IO_ADDR_R = this->IO_ADDR_W = (void __iomem *)baseaddr;
this->cmd_ctrl = board_hwcontrol;
#ifdef CONFIG_MTD_NAND_LNC_8BYTE_READ
this->read_buf = mv_nand_read_buf;
#endif
#ifdef CONFIG_MTD_NAND_LNC_RS_ECC
printk(KERN_INFO "Using %s ECC for NAND device\n", (mv_nand_ecc == MV_NAND_ECC_4BIT ?
"Reed-Solomon 4-bit" : "Hamming 1-bit"));
if (mv_nand_ecc == MV_NAND_ECC_4BIT) {
this->ecc.hwctl = mv_nand_enable_hwecc;
this->ecc.calculate = mv_nand_calculate_ecc_rs;
this->ecc.correct = mv_nand_correct_data_rs;
this->ecc.size = 512;
this->ecc.bytes = 10;
this->ecc.layout = &mv_nand_rs_oobinfo;
this->ecc.mode = NAND_ECC_HW;
} else
#endif
this->ecc.mode = NAND_ECC_SOFT;
this->chip_delay = 30;
if (nand_scan(mv_mtd, 1)) {
err = -ENXIO;
goto out_ior;
}
#ifdef CONFIG_MTD_PARTITIONS
mv_mtd->name = "nand_mtd";
num_of_parts = parse_mtd_partitions(mv_mtd, part_probes, &mtd_parts, 0, "");
if (num_of_parts > 0)
part_type = "command line";
else
num_of_parts = 0;
if (num_of_parts == 0) {
mtd_parts = nand_parts_info;
num_of_parts = MV_NUM_OF_NAND_PARTS;
part_type = "static";
}
printk(KERN_INFO "Using %s partition definition\n", part_type);
add_mtd_partitions(mv_mtd, mtd_parts, num_of_parts);
#endif
goto out;
out_ior:
iounmap((void *)baseaddr);
out_mtd:
kfree(mv_mtd);
out:
return err;
}
module_init(mv_nand_init);
#ifdef MODULE
static void __exit board_cleanup(void)
{
nand_release(mv_mtd);
iounmap((void *)baseaddr);
kfree(mv_mtd);
}
module_exit(board_cleanup);
#endif
#ifdef CONFIG_MTD_NAND_LNC_RS_ECC
#define mm 10 /* RS code over GF(2**mm) - the size in bits of a symbol*/
#define nn 1023 /* nn=2^mm -1 length of codeword */
#define tt 4 /* number of errors that can be corrected */
#define kk 1015 /* kk = number of information symbols kk = nn-2*tt */
static char rs_initialized = 0;
//typedef unsigned int gf;
typedef u_short tgf; /* data type of Galois Functions */
/* Primitive polynomials - irriducibile polynomial [ 1+x^3+x^10 ]*/
short pp[mm+1] = { 1, 0, 0, 1, 0, 0, 0, 0, 0, 0, 1 };
/* index->polynomial form conversion table */
tgf alpha_to[nn + 1];
/* Polynomial->index form conversion table */
tgf index_of[nn + 1];
/* Generator polynomial g(x) = 2*tt with roots @, @^2, .. ,@^(2*tt) */
tgf Gg[nn - kk + 1];
#define minimum(a,b) ((a) < (b) ? (a) : (b))
#define BLANK(a,n) {\
short ci;\
for(ci=0; ci<(n); ci++)\
(a)[ci] = 0;\
}
#define COPY(a,b,n) {\
short ci;\
for(ci=(n)-1;ci >=0;ci--)\
(a)[ci] = (b)[ci];\
}
#define COPYDOWN(a,b,n) {\
short ci;\
for(ci=(n)-1;ci >=0;ci--)\
(a)[ci] = (b)[ci];\
}
/* generate GF(2^m) from the irreducible polynomial p(X) in p[0]..p[mm]
lookup tables: index->polynomial form alpha_to[] contains j=alpha^i;
polynomial form -> index form index_of[j=alpha^i] = i
alpha=2 is the primitive element of GF(2^m)
*/
void generate_gf(void)
{
register int i, mask;
mask = 1;
alpha_to[mm] = 0;
for (i = 0; i < mm; i++) {
alpha_to[i] = mask;
index_of[alpha_to[i]] = i;
if (pp[i] != 0)
alpha_to[mm] ^= mask;
mask <<= 1;
}
index_of[alpha_to[mm]] = mm;
mask >>= 1;
for (i = mm + 1; i < nn; i++) {
if (alpha_to[i - 1] >= mask)
alpha_to[i] = alpha_to[mm] ^ ((alpha_to[i - 1] ^ mask) << 1);
else
alpha_to[i] = alpha_to[i - 1] << 1;
index_of[alpha_to[i]] = i;
}
index_of[0] = nn;
alpha_to[nn] = 0;
}
/*
* Obtain the generator polynomial of the tt-error correcting,
* length nn = (2^mm -1)
* Reed Solomon code from the product of (X + @^i), i=1..2*tt
*/
void gen_poly(void)
{
register int i, j;
Gg[0] = alpha_to[1]; /* primitive element*/
Gg[1] = 1; /* g(x) = (X+@^1) initially */
for (i = 2; i <= nn - kk; i++) {
Gg[i] = 1;
/*
* Below multiply (Gg[0]+Gg[1]*x + ... +Gg[i]x^i) by
* (@^i + x)
*/
for (j = i - 1; j > 0; j--)
if (Gg[j] != 0)
Gg[j] = Gg[j - 1] ^ alpha_to[((index_of[Gg[j]]) + i)%nn];
else
Gg[j] = Gg[j - 1];
Gg[0] = alpha_to[((index_of[Gg[0]]) + i) % nn];
}
/* convert Gg[] to index form for quicker encoding */
for (i = 0; i <= nn - kk; i++)
Gg[i] = index_of[Gg[i]];
}
/*
* take the string of symbols in data[i], i=0..(k-1) and encode
* systematically to produce nn-kk parity symbols in bb[0]..bb[nn-kk-1] data[]
* is input and bb[] is output in polynomial form. Encoding is done by using
* a feedback shift register with appropriate connections specified by the
* elements of Gg[], which was generated above. Codeword is c(X) =
* data(X)*X**(nn-kk)+ b(X)
*/
static inline char encode_rs(dtype data[kk], dtype bb[nn-kk])
{
register int i, j;
tgf feedback;
BLANK(bb,nn-kk);
for (i = kk - 1; i >= 0; i--) {
if(data[i] > nn)
return -1; /* Illegal symbol */
feedback = index_of[data[i] ^ bb[nn - kk - 1]];
if (feedback != nn) { /* feedback term is non-zero */
for (j = nn - kk - 1; j > 0; j--)
if (Gg[j] != nn)
bb[j] = bb[j - 1] ^ alpha_to[(Gg[j] + feedback)%nn];
else
bb[j] = bb[j - 1];
bb[0] = alpha_to[(Gg[0] + feedback)%nn];
} else {
for (j = nn - kk - 1; j > 0; j--)
bb[j] = bb[j - 1];
bb[0] = 0;
}
}
return 0;
}
/* assume we have received bits grouped into mm-bit symbols in data[i],
i=0..(nn-1), We first compute the 2*tt syndromes, then we use the
Berlekamp iteration to find the error location polynomial elp[i].
If the degree of the elp is >tt, we cannot correct all the errors
and hence just put out the information symbols uncorrected. If the
degree of elp is <=tt, we get the roots, hence the inverse roots,
the error location numbers. If the number of errors located does not
equal the degree of the elp, we have more than tt errors and cannot
correct them. Otherwise, we then solve for the error value at the
error location and correct the error.The procedure is that found in
Lin and Costello.*/
static inline int decode_rs(dtype data[nn])
{
int deg_lambda, el, deg_omega;
int i, j, r;
tgf q,tmp,num1,num2,den,discr_r;
tgf recd[nn];
tgf lambda[nn-kk + 1], s[nn-kk + 1]; /* Err+Eras Locator poly
* and syndrome poly */
tgf b[nn-kk + 1], t[nn-kk + 1], omega[nn-kk + 1];
tgf root[nn-kk], reg[nn-kk + 1], loc[nn-kk];
int syn_error, count;
/* data[] is in polynomial form, copy and convert to index form */
for (i = nn-1; i >= 0; i--){
if(data[i] > nn)
return -1; /* Illegal symbol */
recd[i] = index_of[data[i]];
}
/* first form the syndromes; i.e., evaluate recd(x) at roots of g(x)
* namely @**(1+i), i = 0, ... ,(nn-kk-1)
*/
syn_error = 0;
for (i = 1; i <= nn-kk; i++) {
tmp = 0;
for (j = 0; j < nn; j++)
if (recd[j] != nn) /* recd[j] in index form */
tmp ^= alpha_to[(recd[j] + (1+i-1)*j)%nn];
syn_error |= tmp; /* set flag if non-zero syndrome =>
* error */
/* store syndrome in index form */
s[i] = index_of[tmp];
}
if (!syn_error) {
/*
* if syndrome is zero, data[] is a codeword and there are no
* errors to correct. So return data[] unmodified
*/
return 0;
}
BLANK(&lambda[1],nn-kk);
lambda[0] = 1;
for(i=0;i<nn-kk+1;i++)
b[i] = index_of[lambda[i]];
/*
* Begin Berlekamp-Massey algorithm to determine error
* locator polynomial
*/
r = 0;
el = 0;
while (++r <= nn-kk) { /* r is the step number */
/* Compute discrepancy at the r-th step in poly-form */
discr_r = 0;
for (i = 0; i < r; i++){
if ((lambda[i] != 0) && (s[r - i] != nn)) {
discr_r ^= alpha_to[(index_of[lambda[i]] + s[r - i])%nn];
}
}
discr_r = index_of[discr_r]; /* Index form */
if (discr_r == nn) {
/* 2 lines below: B(x) <-- x*B(x) */
COPYDOWN(&b[1],b,nn-kk);
b[0] = nn;
} else {
/* 7 lines below: T(x) <-- lambda(x) - discr_r*x*b(x) */
t[0] = lambda[0];
for (i = 0 ; i < nn-kk; i++) {
if(b[i] != nn)
//t[i+1] = lambda[i+1] ^ alpha_to[modnn(discr_r + b[i])];
t[i+1] = lambda[i+1] ^ alpha_to[(discr_r + b[i])%nn];
else
t[i+1] = lambda[i+1];
}
if (2 * el <= r - 1) {
el = r - el;
/*
* 2 lines below: B(x) <-- inv(discr_r) *
* lambda(x)
*/
for (i = 0; i <= nn-kk; i++)
//b[i] = (lambda[i] == 0) ? nn : modnn(index_of[lambda[i]] - discr_r + nn);
b[i] = (lambda[i] == 0) ? nn : ((index_of[lambda[i]] - discr_r + nn)%nn);
} else {
/* 2 lines below: B(x) <-- x*B(x) */
COPYDOWN(&b[1],b,nn-kk);
b[0] = nn;
}
COPY(lambda,t,nn-kk+1);
}
}
/* Convert lambda to index form and compute deg(lambda(x)) */
deg_lambda = 0;
for(i=0;i<nn-kk+1;i++){
lambda[i] = index_of[lambda[i]];
if(lambda[i] != nn)
deg_lambda = i;
}
/*
* Find roots of the error locator polynomial. By Chien
* Search
*/
COPY(&reg[1],&lambda[1],nn-kk);
count = 0; /* Number of roots of lambda(x) */
for (i = 1; i <= nn; i++) {
q = 1;
for (j = deg_lambda; j > 0; j--)
if (reg[j] != nn) {
//reg[j] = modnn(reg[j] + j);
reg[j] = (reg[j] + j)%nn;
q ^= alpha_to[reg[j]];
}
if (!q) {
/* store root (index-form) and error location number */
root[count] = i;
loc[count] = nn - i;
count++;
}
}
#ifdef DEBUG
/*
printk("\n Final error positions:\t");
for (i = 0; i < count; i++)
printk("%d ", loc[i]);
printk("\n");
*/
#endif
if (deg_lambda != count) {
/*
* deg(lambda) unequal to number of roots => uncorrectable
* error detected
*/
return -1;
}
/*
* Compute err evaluator poly omega(x) = s(x)*lambda(x) (modulo
* x**(nn-kk)). in index form. Also find deg(omega).
*/
deg_omega = 0;
for (i = 0; i < nn-kk;i++){
tmp = 0;
j = (deg_lambda < i) ? deg_lambda : i;
for(;j >= 0; j--){
if ((s[i + 1 - j] != nn) && (lambda[j] != nn))
//tmp ^= alpha_to[modnn(s[i + 1 - j] + lambda[j])];
tmp ^= alpha_to[(s[i + 1 - j] + lambda[j])%nn];
}
if(tmp != 0)
deg_omega = i;
omega[i] = index_of[tmp];
}
omega[nn-kk] = nn;
/*
* Compute error values in poly-form. num1 = omega(inv(X(l))), num2 =
* inv(X(l))**(1-1) and den = lambda_pr(inv(X(l))) all in poly-form
*/
for (j = count-1; j >=0; j--) {
num1 = 0;
for (i = deg_omega; i >= 0; i--) {
if (omega[i] != nn)
//num1 ^= alpha_to[modnn(omega[i] + i * root[j])];
num1 ^= alpha_to[(omega[i] + i * root[j])%nn];
}
//num2 = alpha_to[modnn(root[j] * (1 - 1) + nn)];
num2 = alpha_to[(root[j] * (1 - 1) + nn)%nn];
den = 0;
/* lambda[i+1] for i even is the formal derivative lambda_pr of lambda[i] */
for (i = minimum(deg_lambda,nn-kk-1) & ~1; i >= 0; i -=2) {
if(lambda[i+1] != nn)
//den ^= alpha_to[modnn(lambda[i+1] + i * root[j])];
den ^= alpha_to[(lambda[i+1] + i * root[j])%nn];
}
if (den == 0) {
#ifdef DEBUG
printk("\n ERROR: denominator = 0\n");
#endif
return -1;
}
/* Apply error to data */
if (num1 != 0) {
//data[loc[j]] ^= alpha_to[modnn(index_of[num1] + index_of[num2] + nn - index_of[den])];
data[loc[j]] ^= alpha_to[(index_of[num1] + index_of[num2] + nn - index_of[den])%nn];
}
}
return count;
}
/**
* mv_nand_calculate_ecc_rs - [NAND Interface] Calculate 4 symbol ECC code for 512 byte block
* @mtd: MTD block structure
* @dat: raw data
* @ecc_code: buffer for ECC
*/
int mv_nand_calculate_ecc_rs(struct mtd_info *mtd, const u_char *data, u_char *ecc_code)
{
int i;
u_short rsdata[nn];
/* Generate Tables in first run */
if (!rs_initialized) {
generate_gf();
gen_poly();
rs_initialized = 1;
}
for(i=512; i<nn; i++)
rsdata[i] = 0;
for(i=0; i<512; i++)
rsdata[i] = (u_short) data[i];
if ((encode_rs(rsdata,&(rsdata[kk]))) != 0)
return -1;
*(ecc_code) = (unsigned char) rsdata[kk];
*(ecc_code+1) = ((rsdata[0x3F7]) >> 8) | ((rsdata[0x3F7+1]) << 2);
*(ecc_code+2) = ((rsdata[0x3F7+1]) >> 6) | ((rsdata[0x3F7+2]) << 4);
*(ecc_code+3) = ((rsdata[0x3F7+2]) >> 4) | ((rsdata[0x3F7+3]) << 6);
*(ecc_code+4) = ((rsdata[0x3F7+3]) >> 2);
*(ecc_code+5) = (unsigned char) rsdata[kk+4];
*(ecc_code+6) = ((rsdata[0x3F7+4]) >> 8) | ((rsdata[0x3F7+1+4]) << 2);
*(ecc_code+7) = ((rsdata[0x3F7+1+4]) >> 6) | ((rsdata[0x3F7+2+4]) << 4);
*(ecc_code+8) = ((rsdata[0x3F7+2+4]) >> 4) | ((rsdata[0x3F7+3+4]) << 6);
*(ecc_code+9) = ((rsdata[0x3F7+3+4]) >> 2);
return 0;
}
/**
* mv_nand_correct_data - [NAND Interface] Detect and correct bit error(s)
* @mtd: MTD block structure
* @dat: raw data read from the chip
* @store_ecc: ECC from the chip
* @calc_ecc: the ECC calculated from raw data
*
* Detect and correct a 1 bit error for 256 byte block
*/
int mv_nand_correct_data_rs(struct mtd_info *mtd, u_char *data, u_char *store_ecc, u_char *calc_ecc)
{
int ret,i=0;
u_short rsdata[nn];
/* Generate Tables in first run */
if (!rs_initialized) {
generate_gf();
gen_poly();
rs_initialized = 1;
}
/* is decode needed ? */
if((*(u32*)store_ecc == *(u32*)calc_ecc) &&
(*(u32*)(store_ecc + 4) == *(u32*)(calc_ecc + 4)) &&
(*(u16*)(store_ecc + 8) == *(u16*)(calc_ecc + 8)))
return 0;
/* did we read an erased page ? */
for(i = 0; i < 512 ;i += 4) {
if(*(u32*)(data+i) != 0xFFFFFFFF) {
DBG("%s: trying to correct data\n",__FUNCTION__);
goto correct;
}
}
/* page was erased, return gracefully */
return 0;
correct:
for(i=512; i<nn; i++)
rsdata[i] = 0;
/* errors*/
//data[20] = 0xDD;
//data[30] = 0xDD;
//data[40] = 0xDD;
//data[50] = 0xDD;
//data[60] = 0xDD;
/* Ecc is calculated on chunks of 512B */
for(i=0; i<512; i++)
rsdata[i] = (u_short) data[i];
rsdata[kk] = ( (*(store_ecc+1) & 0x03) <<8) | (*(store_ecc));
rsdata[kk+1] = ( (*(store_ecc+2) & 0x0F) <<6) | (*(store_ecc+1)>>2);
rsdata[kk+2] = ( (*(store_ecc+3) & 0x3F) <<4) | (*(store_ecc+2)>>4);
rsdata[kk+3] = (*(store_ecc+4) <<2) | (*(store_ecc+3)>>6);
rsdata[kk+4] = ( (*(store_ecc+1+5) & 0x03) <<8) | (*(store_ecc+5));
rsdata[kk+5] = ( (*(store_ecc+2+5) & 0x0F) <<6) | (*(store_ecc+1+5)>>2);
rsdata[kk+6] = ( (*(store_ecc+3+5) & 0x3F) <<4) | (*(store_ecc+2+5)>>4);
rsdata[kk+7] = (*(store_ecc+4+5) <<2) | (*(store_ecc+3+5)>>6);
ret = decode_rs(rsdata);
/* Check for excessive errors */
if ((ret > tt) || (ret < 0)) {
printk("%s: uncorrectable error !!!\n",__FUNCTION__);
return -1;
}
/* Copy corrected data */
for (i=0; i<512; i++)
data[i] = (unsigned char) rsdata[i];
return 0;
}
static void mv_nand_enable_hwecc(struct mtd_info *mtd, int mode)
{
return;
}
#endif /* CONFIG_MTD_NAND_LNC_RS_ECC */