move remaining nand helper files
Move remaining NAND implementation files into src/flash/nand/.
This commit is contained in:
@@ -3,9 +3,12 @@ AM_CPPFLAGS = -I$(top_srcdir)/src
|
||||
noinst_LTLIBRARIES = libocdflashnand.la
|
||||
|
||||
libocdflashnand_la_SOURCES = \
|
||||
ecc.c \
|
||||
ecc_kw.c \
|
||||
core.c \
|
||||
fileio.c \
|
||||
tcl.c \
|
||||
arm_io.c \
|
||||
$(NAND_DRIVERS) \
|
||||
driver.c
|
||||
|
||||
@@ -22,6 +25,7 @@ NAND_DRIVERS = \
|
||||
s3c2443.c
|
||||
|
||||
noinst_HEADERS = \
|
||||
arm_io.h \
|
||||
lpc3180.h \
|
||||
driver.h \
|
||||
mx3.h \
|
||||
|
||||
246
src/flash/nand/arm_io.c
Normal file
246
src/flash/nand/arm_io.c
Normal file
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright (C) 2009 by Marvell Semiconductors, Inc.
|
||||
* Written by Nicolas Pitre <nico at marvell.com>
|
||||
*
|
||||
* Copyright (C) 2009 by David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include "arm_io.h"
|
||||
#include <target/armv4_5.h>
|
||||
#include <target/algorithm.h>
|
||||
|
||||
/**
|
||||
* Copies code to a working area. This will allocate room for the code plus the
|
||||
* additional amount requested if the working area pointer is null.
|
||||
*
|
||||
* @param target Pointer to the target to copy code to
|
||||
* @param code Pointer to the code area to be copied
|
||||
* @param code_size Size of the code being copied
|
||||
* @param additional Size of the additional area to be allocated in addition to
|
||||
* code
|
||||
* @param area Pointer to a pointer to a working area to copy code to
|
||||
* @return Success or failure of the operation
|
||||
*/
|
||||
int arm_code_to_working_area(struct target *target,
|
||||
const uint32_t *code, unsigned code_size,
|
||||
unsigned additional, struct working_area **area)
|
||||
{
|
||||
uint8_t code_buf[code_size];
|
||||
unsigned i;
|
||||
int retval;
|
||||
unsigned size = code_size + additional;
|
||||
|
||||
/* REVISIT this assumes size doesn't ever change.
|
||||
* That's usually correct; but there are boards with
|
||||
* both large and small page chips, where it won't be...
|
||||
*/
|
||||
|
||||
/* make sure we have a working area */
|
||||
if (NULL == *area) {
|
||||
retval = target_alloc_working_area(target, size, area);
|
||||
if (retval != ERROR_OK) {
|
||||
LOG_DEBUG("%s: no %d byte buffer", __FUNCTION__, (int) size);
|
||||
return ERROR_NAND_NO_BUFFER;
|
||||
}
|
||||
}
|
||||
|
||||
/* buffer code in target endianness */
|
||||
for (i = 0; i < code_size / 4; i++)
|
||||
target_buffer_set_u32(target, code_buf + i * 4, code[i]);
|
||||
|
||||
/* copy code to work area */
|
||||
retval = target_write_memory(target, (*area)->address,
|
||||
4, code_size / 4, code_buf);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* ARM-specific bulk write from buffer to address of 8-bit wide NAND.
|
||||
* For now this only supports ARMv4 and ARMv5 cores.
|
||||
*
|
||||
* Enhancements to target_run_algorithm() could enable:
|
||||
* - ARMv6 and ARMv7 cores in ARM mode
|
||||
*
|
||||
* Different code fragments could handle:
|
||||
* - Thumb2 cores like Cortex-M (needs different byteswapping)
|
||||
* - 16-bit wide data (needs different setup too)
|
||||
*
|
||||
* @param nand Pointer to the arm_nand_data struct that defines the I/O
|
||||
* @param data Pointer to the data to be copied to flash
|
||||
* @param size Size of the data being copied
|
||||
* @return Success or failure of the operation
|
||||
*/
|
||||
int arm_nandwrite(struct arm_nand_data *nand, uint8_t *data, int size)
|
||||
{
|
||||
struct target *target = nand->target;
|
||||
struct arm_algorithm algo;
|
||||
struct arm *armv4_5 = target->arch_info;
|
||||
struct reg_param reg_params[3];
|
||||
uint32_t target_buf;
|
||||
uint32_t exit = 0;
|
||||
int retval;
|
||||
|
||||
/* Inputs:
|
||||
* r0 NAND data address (byte wide)
|
||||
* r1 buffer address
|
||||
* r2 buffer length
|
||||
*/
|
||||
static const uint32_t code[] = {
|
||||
0xe4d13001, /* s: ldrb r3, [r1], #1 */
|
||||
0xe5c03000, /* strb r3, [r0] */
|
||||
0xe2522001, /* subs r2, r2, #1 */
|
||||
0x1afffffb, /* bne s */
|
||||
|
||||
/* exit: ARMv4 needs hardware breakpoint */
|
||||
0xe1200070, /* e: bkpt #0 */
|
||||
};
|
||||
|
||||
if (nand->op != ARM_NAND_WRITE || !nand->copy_area) {
|
||||
retval = arm_code_to_working_area(target, code, sizeof(code),
|
||||
nand->chunk_size, &nand->copy_area);
|
||||
if (retval != ERROR_OK) {
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
nand->op = ARM_NAND_WRITE;
|
||||
|
||||
/* copy data to work area */
|
||||
target_buf = nand->copy_area->address + sizeof(code);
|
||||
retval = target_bulk_write_memory(target, target_buf, size / 4, data);
|
||||
if (retval == ERROR_OK && (size & 3) != 0)
|
||||
retval = target_write_memory(target,
|
||||
target_buf + (size & ~3),
|
||||
1, size & 3, data + (size & ~3));
|
||||
if (retval != ERROR_OK)
|
||||
return retval;
|
||||
|
||||
/* set up algorithm and parameters */
|
||||
algo.common_magic = ARM_COMMON_MAGIC;
|
||||
algo.core_mode = ARM_MODE_SVC;
|
||||
algo.core_state = ARM_STATE_ARM;
|
||||
|
||||
init_reg_param(®_params[0], "r0", 32, PARAM_IN);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_IN);
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_IN);
|
||||
|
||||
buf_set_u32(reg_params[0].value, 0, 32, nand->data);
|
||||
buf_set_u32(reg_params[1].value, 0, 32, target_buf);
|
||||
buf_set_u32(reg_params[2].value, 0, 32, size);
|
||||
|
||||
/* armv4 must exit using a hardware breakpoint */
|
||||
if (armv4_5->is_armv4)
|
||||
exit = nand->copy_area->address + sizeof(code) - 4;
|
||||
|
||||
/* use alg to write data from work area to NAND chip */
|
||||
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
|
||||
nand->copy_area->address, exit, 1000, &algo);
|
||||
if (retval != ERROR_OK)
|
||||
LOG_ERROR("error executing hosted NAND write");
|
||||
|
||||
destroy_reg_param(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
/**
|
||||
* Uses an on-chip algorithm for an ARM device to read from a NAND device and
|
||||
* store the data into the host machine's memory.
|
||||
*
|
||||
* @param nand Pointer to the arm_nand_data struct that defines the I/O
|
||||
* @param data Pointer to the data buffer to store the read data
|
||||
* @param size Amount of data to be stored to the buffer.
|
||||
* @return Success or failure of the operation
|
||||
*/
|
||||
int arm_nandread(struct arm_nand_data *nand, uint8_t *data, uint32_t size)
|
||||
{
|
||||
struct target *target = nand->target;
|
||||
struct arm_algorithm algo;
|
||||
struct arm *armv4_5 = target->arch_info;
|
||||
struct reg_param reg_params[3];
|
||||
uint32_t target_buf;
|
||||
uint32_t exit = 0;
|
||||
int retval;
|
||||
|
||||
/* Inputs:
|
||||
* r0 buffer address
|
||||
* r1 NAND data address (byte wide)
|
||||
* r2 buffer length
|
||||
*/
|
||||
static const uint32_t code[] = {
|
||||
0xe5d13000, /* s: ldrb r3, [r1] */
|
||||
0xe4c03001, /* strb r3, [r0], #1 */
|
||||
0xe2522001, /* subs r2, r2, #1 */
|
||||
0x1afffffb, /* bne s */
|
||||
|
||||
/* exit: ARMv4 needs hardware breakpoint */
|
||||
0xe1200070, /* e: bkpt #0 */
|
||||
};
|
||||
|
||||
/* create the copy area if not yet available */
|
||||
if (nand->op != ARM_NAND_READ || !nand->copy_area) {
|
||||
retval = arm_code_to_working_area(target, code, sizeof(code),
|
||||
nand->chunk_size, &nand->copy_area);
|
||||
if (retval != ERROR_OK) {
|
||||
return retval;
|
||||
}
|
||||
}
|
||||
|
||||
nand->op = ARM_NAND_READ;
|
||||
target_buf = nand->copy_area->address + sizeof(code);
|
||||
|
||||
/* set up algorithm and parameters */
|
||||
algo.common_magic = ARM_COMMON_MAGIC;
|
||||
algo.core_mode = ARM_MODE_SVC;
|
||||
algo.core_state = ARM_STATE_ARM;
|
||||
|
||||
init_reg_param(®_params[0], "r0", 32, PARAM_IN);
|
||||
init_reg_param(®_params[1], "r1", 32, PARAM_IN);
|
||||
init_reg_param(®_params[2], "r2", 32, PARAM_IN);
|
||||
|
||||
buf_set_u32(reg_params[0].value, 0, 32, target_buf);
|
||||
buf_set_u32(reg_params[1].value, 0, 32, nand->data);
|
||||
buf_set_u32(reg_params[2].value, 0, 32, size);
|
||||
|
||||
/* armv4 must exit using a hardware breakpoint */
|
||||
if (armv4_5->is_armv4)
|
||||
exit = nand->copy_area->address + sizeof(code) - 4;
|
||||
|
||||
/* use alg to write data from NAND chip to work area */
|
||||
retval = target_run_algorithm(target, 0, NULL, 3, reg_params,
|
||||
nand->copy_area->address, exit, 1000, &algo);
|
||||
if (retval != ERROR_OK)
|
||||
LOG_ERROR("error executing hosted NAND read");
|
||||
|
||||
destroy_reg_param(®_params[0]);
|
||||
destroy_reg_param(®_params[1]);
|
||||
destroy_reg_param(®_params[2]);
|
||||
|
||||
/* read from work area to the host's memory */
|
||||
retval = target_read_buffer(target, target_buf, size, data);
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
60
src/flash/nand/arm_io.h
Normal file
60
src/flash/nand/arm_io.h
Normal file
@@ -0,0 +1,60 @@
|
||||
/*
|
||||
* Copyright (C) 2009 by David Brownell
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation; either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program; if not, write to the
|
||||
* Free Software Foundation, Inc.,
|
||||
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
||||
*/
|
||||
#ifndef __ARM_NANDIO_H
|
||||
#define __ARM_NANDIO_H
|
||||
|
||||
#include <flash/nand.h>
|
||||
#include <helper/binarybuffer.h>
|
||||
|
||||
/**
|
||||
* Available operational states the arm_nand_data struct can be in.
|
||||
*/
|
||||
enum arm_nand_op {
|
||||
ARM_NAND_NONE, /**< No operation performed. */
|
||||
ARM_NAND_READ, /**< Read operation performed. */
|
||||
ARM_NAND_WRITE, /**< Write operation performed. */
|
||||
};
|
||||
|
||||
/**
|
||||
* The arm_nand_data struct is used for defining NAND I/O operations on an ARM
|
||||
* core.
|
||||
*/
|
||||
struct arm_nand_data {
|
||||
/** Target is proxy for some ARM core. */
|
||||
struct target *target;
|
||||
|
||||
/** The copy area holds code loop and data for I/O operations. */
|
||||
struct working_area *copy_area;
|
||||
|
||||
/** The chunk size is the page size or ECC chunk. */
|
||||
unsigned chunk_size;
|
||||
|
||||
/** Where data is read from or written to. */
|
||||
uint32_t data;
|
||||
|
||||
/** Last operation executed using this struct. */
|
||||
enum arm_nand_op op;
|
||||
|
||||
/* currently implicit: data width == 8 bits (not 16) */
|
||||
};
|
||||
|
||||
int arm_nandwrite(struct arm_nand_data *nand, uint8_t *data, int size);
|
||||
int arm_nandread(struct arm_nand_data *nand, uint8_t *data, uint32_t size);
|
||||
|
||||
#endif /* __ARM_NANDIO_H */
|
||||
@@ -28,7 +28,7 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <flash/arm_nandio.h>
|
||||
#include "arm_io.h"
|
||||
|
||||
|
||||
enum ecc {
|
||||
|
||||
122
src/flash/nand/ecc.c
Normal file
122
src/flash/nand/ecc.c
Normal file
@@ -0,0 +1,122 @@
|
||||
/*
|
||||
* This file contains an ECC algorithm from Toshiba that allows for detection
|
||||
* and correction of 1-bit errors in a 256 byte block of data.
|
||||
*
|
||||
* [ Extracted from the initial code found in some early Linux versions.
|
||||
* The current Linux code is bigger while being faster, but this is of
|
||||
* no real benefit when the bottleneck largely remains the JTAG link. ]
|
||||
*
|
||||
* Copyright (C) 2000-2004 Steven J. Hill (sjhill at realitydiluted.com)
|
||||
* Toshiba America Electronics Components, Inc.
|
||||
*
|
||||
* Copyright (C) 2006 Thomas Gleixner <tglx at linutronix.de>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along
|
||||
* with this file; if not, write to the Free Software Foundation, Inc.,
|
||||
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
|
||||
*
|
||||
* As a special exception, if other files instantiate templates or use
|
||||
* macros or inline functions from these files, or you compile these
|
||||
* files and link them with other works to produce a work based on these
|
||||
* files, these files do not by themselves cause the resulting work to be
|
||||
* covered by the GNU General Public License. However the source code for
|
||||
* these files must still be made available in accordance with section (3)
|
||||
* of the GNU General Public License.
|
||||
*
|
||||
* This exception does not invalidate any other reasons why a work based on
|
||||
* this file might be covered by the GNU General Public License.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <flash/nand.h>
|
||||
|
||||
/*
|
||||
* Pre-calculated 256-way 1 byte column parity
|
||||
*/
|
||||
static const uint8_t nand_ecc_precalc_table[] = {
|
||||
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00,
|
||||
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
|
||||
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
|
||||
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
|
||||
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
|
||||
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
|
||||
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
|
||||
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
|
||||
0x6a, 0x3f, 0x3c, 0x69, 0x33, 0x66, 0x65, 0x30, 0x30, 0x65, 0x66, 0x33, 0x69, 0x3c, 0x3f, 0x6a,
|
||||
0x0f, 0x5a, 0x59, 0x0c, 0x56, 0x03, 0x00, 0x55, 0x55, 0x00, 0x03, 0x56, 0x0c, 0x59, 0x5a, 0x0f,
|
||||
0x0c, 0x59, 0x5a, 0x0f, 0x55, 0x00, 0x03, 0x56, 0x56, 0x03, 0x00, 0x55, 0x0f, 0x5a, 0x59, 0x0c,
|
||||
0x69, 0x3c, 0x3f, 0x6a, 0x30, 0x65, 0x66, 0x33, 0x33, 0x66, 0x65, 0x30, 0x6a, 0x3f, 0x3c, 0x69,
|
||||
0x03, 0x56, 0x55, 0x00, 0x5a, 0x0f, 0x0c, 0x59, 0x59, 0x0c, 0x0f, 0x5a, 0x00, 0x55, 0x56, 0x03,
|
||||
0x66, 0x33, 0x30, 0x65, 0x3f, 0x6a, 0x69, 0x3c, 0x3c, 0x69, 0x6a, 0x3f, 0x65, 0x30, 0x33, 0x66,
|
||||
0x65, 0x30, 0x33, 0x66, 0x3c, 0x69, 0x6a, 0x3f, 0x3f, 0x6a, 0x69, 0x3c, 0x66, 0x33, 0x30, 0x65,
|
||||
0x00, 0x55, 0x56, 0x03, 0x59, 0x0c, 0x0f, 0x5a, 0x5a, 0x0f, 0x0c, 0x59, 0x03, 0x56, 0x55, 0x00
|
||||
};
|
||||
|
||||
/*
|
||||
* nand_calculate_ecc - Calculate 3-byte ECC for 256-byte block
|
||||
*/
|
||||
int nand_calculate_ecc(struct nand_device *nand, const uint8_t *dat, uint8_t *ecc_code)
|
||||
{
|
||||
uint8_t idx, reg1, reg2, reg3, tmp1, tmp2;
|
||||
int i;
|
||||
|
||||
/* Initialize variables */
|
||||
reg1 = reg2 = reg3 = 0;
|
||||
|
||||
/* Build up column parity */
|
||||
for (i = 0; i < 256; i++) {
|
||||
/* Get CP0 - CP5 from table */
|
||||
idx = nand_ecc_precalc_table[*dat++];
|
||||
reg1 ^= (idx & 0x3f);
|
||||
|
||||
/* All bit XOR = 1 ? */
|
||||
if (idx & 0x40) {
|
||||
reg3 ^= (uint8_t) i;
|
||||
reg2 ^= ~((uint8_t) i);
|
||||
}
|
||||
}
|
||||
|
||||
/* Create non-inverted ECC code from line parity */
|
||||
tmp1 = (reg3 & 0x80) >> 0; /* B7 -> B7 */
|
||||
tmp1 |= (reg2 & 0x80) >> 1; /* B7 -> B6 */
|
||||
tmp1 |= (reg3 & 0x40) >> 1; /* B6 -> B5 */
|
||||
tmp1 |= (reg2 & 0x40) >> 2; /* B6 -> B4 */
|
||||
tmp1 |= (reg3 & 0x20) >> 2; /* B5 -> B3 */
|
||||
tmp1 |= (reg2 & 0x20) >> 3; /* B5 -> B2 */
|
||||
tmp1 |= (reg3 & 0x10) >> 3; /* B4 -> B1 */
|
||||
tmp1 |= (reg2 & 0x10) >> 4; /* B4 -> B0 */
|
||||
|
||||
tmp2 = (reg3 & 0x08) << 4; /* B3 -> B7 */
|
||||
tmp2 |= (reg2 & 0x08) << 3; /* B3 -> B6 */
|
||||
tmp2 |= (reg3 & 0x04) << 3; /* B2 -> B5 */
|
||||
tmp2 |= (reg2 & 0x04) << 2; /* B2 -> B4 */
|
||||
tmp2 |= (reg3 & 0x02) << 2; /* B1 -> B3 */
|
||||
tmp2 |= (reg2 & 0x02) << 1; /* B1 -> B2 */
|
||||
tmp2 |= (reg3 & 0x01) << 1; /* B0 -> B1 */
|
||||
tmp2 |= (reg2 & 0x01) << 0; /* B7 -> B0 */
|
||||
|
||||
/* Calculate final ECC code */
|
||||
#ifdef NAND_ECC_SMC
|
||||
ecc_code[0] = ~tmp2;
|
||||
ecc_code[1] = ~tmp1;
|
||||
#else
|
||||
ecc_code[0] = ~tmp1;
|
||||
ecc_code[1] = ~tmp2;
|
||||
#endif
|
||||
ecc_code[2] = ((~reg1) << 2) | 0x03;
|
||||
|
||||
return 0;
|
||||
}
|
||||
172
src/flash/nand/ecc_kw.c
Normal file
172
src/flash/nand/ecc_kw.c
Normal file
@@ -0,0 +1,172 @@
|
||||
/*
|
||||
* Reed-Solomon ECC handling for the Marvell Kirkwood SOC
|
||||
* Copyright (C) 2009 Marvell Semiconductor, Inc.
|
||||
*
|
||||
* Authors: Lennert Buytenhek <buytenh@wantstofly.org>
|
||||
* Nicolas Pitre <nico@fluxnic.net>
|
||||
*
|
||||
* This file is free software; you can redistribute it and/or modify it
|
||||
* under the terms of the GNU General Public License as published by the
|
||||
* Free Software Foundation; either version 2 or (at your option) any
|
||||
* later version.
|
||||
*
|
||||
* This file is distributed in the hope that it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
||||
* for more details.
|
||||
*/
|
||||
|
||||
#ifdef HAVE_CONFIG_H
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <flash/nand.h>
|
||||
|
||||
/*****************************************************************************
|
||||
* Arithmetic in GF(2^10) ("F") modulo x^10 + x^3 + 1.
|
||||
*
|
||||
* For multiplication, a discrete log/exponent table is used, with
|
||||
* primitive element x (F is a primitive field, so x is primitive).
|
||||
*/
|
||||
#define MODPOLY 0x409 /* x^10 + x^3 + 1 in binary */
|
||||
|
||||
/*
|
||||
* Maps an integer a [0..1022] to a polynomial b = gf_exp[a] in
|
||||
* GF(2^10) mod x^10 + x^3 + 1 such that b = x ^ a. There's two
|
||||
* identical copies of this array back-to-back so that we can save
|
||||
* the mod 1023 operation when doing a GF multiplication.
|
||||
*/
|
||||
static uint16_t gf_exp[1023 + 1023];
|
||||
|
||||
/*
|
||||
* Maps a polynomial b in GF(2^10) mod x^10 + x^3 + 1 to an index
|
||||
* a = gf_log[b] in [0..1022] such that b = x ^ a.
|
||||
*/
|
||||
static uint16_t gf_log[1024];
|
||||
|
||||
static void gf_build_log_exp_table(void)
|
||||
{
|
||||
int i;
|
||||
int p_i;
|
||||
|
||||
/*
|
||||
* p_i = x ^ i
|
||||
*
|
||||
* Initialise to 1 for i = 0.
|
||||
*/
|
||||
p_i = 1;
|
||||
|
||||
for (i = 0; i < 1023; i++) {
|
||||
gf_exp[i] = p_i;
|
||||
gf_exp[i + 1023] = p_i;
|
||||
gf_log[p_i] = i;
|
||||
|
||||
/*
|
||||
* p_i = p_i * x
|
||||
*/
|
||||
p_i <<= 1;
|
||||
if (p_i & (1 << 10))
|
||||
p_i ^= MODPOLY;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*****************************************************************************
|
||||
* Reed-Solomon code
|
||||
*
|
||||
* This implements a (1023,1015) Reed-Solomon ECC code over GF(2^10)
|
||||
* mod x^10 + x^3 + 1, shortened to (520,512). The ECC data consists
|
||||
* of 8 10-bit symbols, or 10 8-bit bytes.
|
||||
*
|
||||
* Given 512 bytes of data, computes 10 bytes of ECC.
|
||||
*
|
||||
* This is done by converting the 512 bytes to 512 10-bit symbols
|
||||
* (elements of F), interpreting those symbols as a polynomial in F[X]
|
||||
* by taking symbol 0 as the coefficient of X^8 and symbol 511 as the
|
||||
* coefficient of X^519, and calculating the residue of that polynomial
|
||||
* divided by the generator polynomial, which gives us the 8 ECC symbols
|
||||
* as the remainder. Finally, we convert the 8 10-bit ECC symbols to 10
|
||||
* 8-bit bytes.
|
||||
*
|
||||
* The generator polynomial is hardcoded, as that is faster, but it
|
||||
* can be computed by taking the primitive element a = x (in F), and
|
||||
* constructing a polynomial in F[X] with roots a, a^2, a^3, ..., a^8
|
||||
* by multiplying the minimal polynomials for those roots (which are
|
||||
* just 'x - a^i' for each i).
|
||||
*
|
||||
* Note: due to unfortunate circumstances, the bootrom in the Kirkwood SOC
|
||||
* expects the ECC to be computed backward, i.e. from the last byte down
|
||||
* to the first one.
|
||||
*/
|
||||
int nand_calculate_ecc_kw(struct nand_device *nand, const uint8_t *data, uint8_t *ecc)
|
||||
{
|
||||
unsigned int r7, r6, r5, r4, r3, r2, r1, r0;
|
||||
int i;
|
||||
static int tables_initialized = 0;
|
||||
|
||||
if (!tables_initialized) {
|
||||
gf_build_log_exp_table();
|
||||
tables_initialized = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Load bytes 504..511 of the data into r.
|
||||
*/
|
||||
r0 = data[504];
|
||||
r1 = data[505];
|
||||
r2 = data[506];
|
||||
r3 = data[507];
|
||||
r4 = data[508];
|
||||
r5 = data[509];
|
||||
r6 = data[510];
|
||||
r7 = data[511];
|
||||
|
||||
|
||||
/*
|
||||
* Shift bytes 503..0 (in that order) into r0, followed
|
||||
* by eight zero bytes, while reducing the polynomial by the
|
||||
* generator polynomial in every step.
|
||||
*/
|
||||
for (i = 503; i >= -8; i--) {
|
||||
unsigned int d;
|
||||
|
||||
d = 0;
|
||||
if (i >= 0)
|
||||
d = data[i];
|
||||
|
||||
if (r7) {
|
||||
uint16_t *t = gf_exp + gf_log[r7];
|
||||
|
||||
r7 = r6 ^ t[0x21c];
|
||||
r6 = r5 ^ t[0x181];
|
||||
r5 = r4 ^ t[0x18e];
|
||||
r4 = r3 ^ t[0x25f];
|
||||
r3 = r2 ^ t[0x197];
|
||||
r2 = r1 ^ t[0x193];
|
||||
r1 = r0 ^ t[0x237];
|
||||
r0 = d ^ t[0x024];
|
||||
} else {
|
||||
r7 = r6;
|
||||
r6 = r5;
|
||||
r5 = r4;
|
||||
r4 = r3;
|
||||
r3 = r2;
|
||||
r2 = r1;
|
||||
r1 = r0;
|
||||
r0 = d;
|
||||
}
|
||||
}
|
||||
|
||||
ecc[0] = r0;
|
||||
ecc[1] = (r0 >> 8) | (r1 << 2);
|
||||
ecc[2] = (r1 >> 6) | (r2 << 4);
|
||||
ecc[3] = (r2 >> 4) | (r3 << 6);
|
||||
ecc[4] = (r3 >> 2);
|
||||
ecc[5] = r4;
|
||||
ecc[6] = (r4 >> 8) | (r5 << 2);
|
||||
ecc[7] = (r5 >> 6) | (r6 << 4);
|
||||
ecc[8] = (r6 >> 4) | (r7 << 6);
|
||||
ecc[9] = (r7 >> 2);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -26,7 +26,7 @@
|
||||
#include "config.h"
|
||||
#endif
|
||||
|
||||
#include <flash/arm_nandio.h>
|
||||
#include "arm_io.h"
|
||||
#include <target/armv4_5.h>
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user