|
|
|
@ -0,0 +1,744 @@
|
|
|
|
|
/***************************************************************************
|
|
|
|
|
* 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. *
|
|
|
|
|
***************************************************************************/
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* DaVinci family NAND controller support for OpenOCD.
|
|
|
|
|
*
|
|
|
|
|
* This driver uses hardware ECC (1-bit or 4-bit) unless
|
|
|
|
|
* the chip is accessed in "raw" mode.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#ifdef HAVE_CONFIG_H
|
|
|
|
|
#include "config.h"
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
#include "nand.h"
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
enum ecc {
|
|
|
|
|
HWECC1, /* all controllers support 1-bit ECC */
|
|
|
|
|
HWECC4, /* newer chips also have 4-bit ECC hardware */
|
|
|
|
|
HWECC4_INFIX, /* avoid this layout, except maybe for boot code */
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct davinci_nand {
|
|
|
|
|
target_t *target;
|
|
|
|
|
|
|
|
|
|
u8 chipsel; /* chipselect 0..3 == CS2..CS5 */
|
|
|
|
|
u8 eccmode;
|
|
|
|
|
|
|
|
|
|
/* Async EMIF controller base */
|
|
|
|
|
u32 aemif;
|
|
|
|
|
|
|
|
|
|
/* NAND chip addresses */
|
|
|
|
|
u32 data; /* without CLE or ALE */
|
|
|
|
|
u32 cmd; /* with CLE */
|
|
|
|
|
u32 addr; /* with ALE */
|
|
|
|
|
|
|
|
|
|
/* page i/o for the relevant flavor of hardware ECC */
|
|
|
|
|
int (*read_page)(struct nand_device_s *nand, u32 page,
|
|
|
|
|
u8 *data, u32 data_size, u8 *oob, u32 oob_size);
|
|
|
|
|
int (*write_page)(struct nand_device_s *nand, u32 page,
|
|
|
|
|
u8 *data, u32 data_size, u8 *oob, u32 oob_size);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
#define NANDFCR 0x60 /* flash control register */
|
|
|
|
|
#define NANDFSR 0x64 /* flash status register */
|
|
|
|
|
#define NANDFECC 0x70 /* 1-bit ECC data, CS0, 1st of 4 */
|
|
|
|
|
#define NAND4BITECCLOAD 0xbc /* 4-bit ECC, load saved values */
|
|
|
|
|
#define NAND4BITECC 0xc0 /* 4-bit ECC data, 1st of 4 */
|
|
|
|
|
#define NANDERRADDR 0xd0 /* 4-bit ECC err addr, 1st of 2 */
|
|
|
|
|
#define NANDERRVAL 0xd8 /* 4-bit ECC err value, 1st of 2 */
|
|
|
|
|
|
|
|
|
|
static int halted(target_t *target, const char *label)
|
|
|
|
|
{
|
|
|
|
|
if (target->state == TARGET_HALTED)
|
|
|
|
|
return true;
|
|
|
|
|
|
|
|
|
|
LOG_ERROR("Target must be halted to use NAND controller (%s)", label);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_register_commands(struct command_context_s *cmd_ctx)
|
|
|
|
|
{
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_init(struct nand_device_s *nand)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
u32 nandfcr;
|
|
|
|
|
|
|
|
|
|
if (!halted(target, "init"))
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
/* We require something else to have configured AEMIF to talk
|
|
|
|
|
* to NAND chip in this range (including timings and width).
|
|
|
|
|
*/
|
|
|
|
|
target_read_u32(target, info->aemif + NANDFCR, &nandfcr);
|
|
|
|
|
if (!(nandfcr & (1 << info->chipsel))) {
|
|
|
|
|
LOG_ERROR("chip address %08x not NAND-enabled?", info->data);
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* REVISIT verify: AxCR must be in 8-bit mode, since that's all we
|
|
|
|
|
* tested. 16 bit support should work too; but not with 4-bit ECC.
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_reset(struct nand_device_s *nand)
|
|
|
|
|
{
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_nand_ready(struct nand_device_s *nand, int timeout)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
u32 nandfsr;
|
|
|
|
|
|
|
|
|
|
/* NOTE: return code is zero/error, else success; not ERROR_* */
|
|
|
|
|
|
|
|
|
|
if (!halted(target, "ready"))
|
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
target_read_u32(target, info->aemif + NANDFSR, &nandfsr);
|
|
|
|
|
|
|
|
|
|
if (nandfsr & 0x01)
|
|
|
|
|
return 1;
|
|
|
|
|
|
|
|
|
|
alive_sleep(1);
|
|
|
|
|
} while (timeout-- > 0);
|
|
|
|
|
|
|
|
|
|
return 0;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_command(struct nand_device_s *nand, u8 command)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
|
|
|
|
|
if (!halted(target, "command"))
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
target_write_u8(target, info->cmd, command);
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_address(struct nand_device_s *nand, u8 address)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
|
|
|
|
|
if (!halted(target, "address"))
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
target_write_u8(target, info->addr, address);
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_write_data(struct nand_device_s *nand, u16 data)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
|
|
|
|
|
if (!halted(target, "write_data"))
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
target_write_u8(target, info->data, data);
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_read_data(struct nand_device_s *nand, void *data)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
|
|
|
|
|
if (!halted(target, "read_data"))
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
target_read_u8(target, info->data, data);
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* REVISIT a bit of native code should let block I/O be MUCH faster */
|
|
|
|
|
|
|
|
|
|
static int davinci_read_block_data(struct nand_device_s *nand,
|
|
|
|
|
u8 *data, int data_size)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
u32 nfdata = info->data;
|
|
|
|
|
u32 tmp;
|
|
|
|
|
|
|
|
|
|
if (!halted(target, "read_block"))
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
while (data_size >= 4) {
|
|
|
|
|
target_read_u32(target, nfdata, &tmp);
|
|
|
|
|
|
|
|
|
|
data[0] = tmp;
|
|
|
|
|
data[1] = tmp >> 8;
|
|
|
|
|
data[2] = tmp >> 16;
|
|
|
|
|
data[3] = tmp >> 24;
|
|
|
|
|
|
|
|
|
|
data_size -= 4;
|
|
|
|
|
data += 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (data_size > 0) {
|
|
|
|
|
target_read_u8(target, nfdata, data);
|
|
|
|
|
|
|
|
|
|
data_size -= 1;
|
|
|
|
|
data += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_write_block_data(struct nand_device_s *nand,
|
|
|
|
|
u8 *data, int data_size)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
u32 nfdata = info->data;
|
|
|
|
|
u32 tmp;
|
|
|
|
|
|
|
|
|
|
if (!halted(target, "write_block"))
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
while (data_size >= 4) {
|
|
|
|
|
tmp = le_to_h_u32(data);
|
|
|
|
|
target_write_u32(target, nfdata, tmp);
|
|
|
|
|
|
|
|
|
|
data_size -= 4;
|
|
|
|
|
data += 4;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
while (data_size > 0) {
|
|
|
|
|
target_write_u8(target, nfdata, *data);
|
|
|
|
|
|
|
|
|
|
data_size -= 1;
|
|
|
|
|
data += 1;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_write_page(struct nand_device_s *nand, u32 page,
|
|
|
|
|
u8 *data, u32 data_size, u8 *oob, u32 oob_size)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
u8 *ooballoc = NULL;
|
|
|
|
|
int status;
|
|
|
|
|
|
|
|
|
|
if (!nand->device)
|
|
|
|
|
return ERROR_NAND_DEVICE_NOT_PROBED;
|
|
|
|
|
if (!halted(info->target, "write_page"))
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
/* Always write both data and OOB ... we are not "raw" I/O! */
|
|
|
|
|
if (!data)
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
/* If we're not given OOB, write 0xff where we don't write ECC codes. */
|
|
|
|
|
switch (nand->page_size) {
|
|
|
|
|
case 512:
|
|
|
|
|
oob_size = 16;
|
|
|
|
|
break;
|
|
|
|
|
case 2048:
|
|
|
|
|
oob_size = 64;
|
|
|
|
|
break;
|
|
|
|
|
case 4096:
|
|
|
|
|
oob_size = 128;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
}
|
|
|
|
|
if (!oob) {
|
|
|
|
|
ooballoc = malloc(oob_size);
|
|
|
|
|
if (ooballoc)
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
oob = ooballoc;
|
|
|
|
|
memset(oob, 0x0ff, oob_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
status = info->write_page(nand, page, data, data_size, oob, oob_size);
|
|
|
|
|
free(ooballoc);
|
|
|
|
|
return status;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_read_page(struct nand_device_s *nand, u32 page,
|
|
|
|
|
u8 *data, u32 data_size, u8 *oob, u32 oob_size)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
|
|
|
|
|
if (!nand->device)
|
|
|
|
|
return ERROR_NAND_DEVICE_NOT_PROBED;
|
|
|
|
|
if (!halted(info->target, "read_page"))
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
|
|
|
|
|
return info->read_page(nand, page, data, data_size, oob, oob_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static void davinci_write_pagecmd(struct nand_device_s *nand, u8 cmd, u32 page)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
int page3 = nand->address_cycles - (nand->page_size == 512);
|
|
|
|
|
|
|
|
|
|
/* write command ({page,otp}x{read,program} */
|
|
|
|
|
target_write_u8(target, info->cmd, cmd);
|
|
|
|
|
|
|
|
|
|
/* column address (beginning-of-page) */
|
|
|
|
|
target_write_u8(target, info->addr, 0);
|
|
|
|
|
if (nand->page_size > 512)
|
|
|
|
|
target_write_u8(target, info->addr, 0);
|
|
|
|
|
|
|
|
|
|
/* page address */
|
|
|
|
|
target_write_u8(target, info->addr, page);
|
|
|
|
|
target_write_u8(target, info->addr, page >> 8);
|
|
|
|
|
if (page3)
|
|
|
|
|
target_write_u8(target, info->addr, page >> 16);
|
|
|
|
|
if (page3 == 2)
|
|
|
|
|
target_write_u8(target, info->addr, page >> 24);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_writepage_tail(struct nand_device_s *nand,
|
|
|
|
|
u8 *oob, u32 oob_size)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
u8 status;
|
|
|
|
|
|
|
|
|
|
if (oob_size)
|
|
|
|
|
davinci_write_block_data(nand, oob, oob_size);
|
|
|
|
|
|
|
|
|
|
/* non-cachemode page program */
|
|
|
|
|
target_write_u8(target, info->cmd, NAND_CMD_PAGEPROG);
|
|
|
|
|
|
|
|
|
|
if (!davinci_nand_ready(nand, 100))
|
|
|
|
|
return ERROR_NAND_OPERATION_TIMEOUT;
|
|
|
|
|
|
|
|
|
|
if (nand_read_status(nand, &status) != ERROR_OK) {
|
|
|
|
|
LOG_ERROR("couldn't read status");
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (status & NAND_STATUS_FAIL) {
|
|
|
|
|
LOG_ERROR("write operation failed, status: 0x%02x", status);
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* All DaVinci family chips support 1-bit ECC on a per-chipselect basis.
|
|
|
|
|
*/
|
|
|
|
|
static int davinci_write_page_ecc1(struct nand_device_s *nand, u32 page,
|
|
|
|
|
u8 *data, u32 data_size, u8 *oob, u32 oob_size)
|
|
|
|
|
{
|
|
|
|
|
unsigned oob_offset;
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
const u32 fcr_addr = info->aemif + NANDFCR;
|
|
|
|
|
const u32 ecc1_addr = info->aemif + NANDFECC + info->chipsel;
|
|
|
|
|
u32 fcr, ecc1;
|
|
|
|
|
|
|
|
|
|
/* Write contiguous ECC bytes starting at specified offset.
|
|
|
|
|
* NOTE: Linux reserves twice as many bytes as we need; and
|
|
|
|
|
* for 16-bit OOB, those extra bytes are discontiguous.
|
|
|
|
|
*/
|
|
|
|
|
switch (nand->page_size) {
|
|
|
|
|
case 512:
|
|
|
|
|
oob_offset = 0;
|
|
|
|
|
break;
|
|
|
|
|
case 2048:
|
|
|
|
|
oob_offset = 40;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
oob_offset = 80;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page);
|
|
|
|
|
|
|
|
|
|
/* scrub any old ECC state */
|
|
|
|
|
target_read_u32(target, ecc1_addr, &ecc1);
|
|
|
|
|
|
|
|
|
|
target_read_u32(target, fcr_addr, &fcr);
|
|
|
|
|
fcr |= 1 << (8 + info->chipsel);
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
/* set "start csX 1bit ecc" bit */
|
|
|
|
|
target_write_u32(target, fcr_addr, fcr);
|
|
|
|
|
|
|
|
|
|
/* write 512 bytes */
|
|
|
|
|
davinci_write_block_data(nand, data, 512);
|
|
|
|
|
data += 512;
|
|
|
|
|
data_size -= 512;
|
|
|
|
|
|
|
|
|
|
/* read the ecc, pack to 3 bytes, and invert so the ecc
|
|
|
|
|
* in an erased block is correct
|
|
|
|
|
*/
|
|
|
|
|
target_read_u32(target, ecc1_addr, &ecc1);
|
|
|
|
|
ecc1 = (ecc1 & 0x0fff) | ((ecc1 & 0x0fff0000) >> 4);
|
|
|
|
|
ecc1 = ~ecc1;
|
|
|
|
|
|
|
|
|
|
/* save correct ECC code into oob data */
|
|
|
|
|
oob[oob_offset++] = (u8)(ecc1);
|
|
|
|
|
oob[oob_offset++] = (u8)(ecc1 >> 8);
|
|
|
|
|
oob[oob_offset++] = (u8)(ecc1 >> 16);
|
|
|
|
|
|
|
|
|
|
} while (data_size);
|
|
|
|
|
|
|
|
|
|
/* write OOB into spare area */
|
|
|
|
|
return davinci_writepage_tail(nand, oob, oob_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Preferred "new style" ECC layout for use with 4-bit ECC. This somewhat
|
|
|
|
|
* slows down large page reads done with error correction (since the OOB
|
|
|
|
|
* is read first, so its ECC data can be used incrementally), but the
|
|
|
|
|
* manufacturer bad block markers are safe. Contrast: old "infix" style.
|
|
|
|
|
*/
|
|
|
|
|
static int davinci_write_page_ecc4(struct nand_device_s *nand, u32 page,
|
|
|
|
|
u8 *data, u32 data_size, u8 *oob, u32 oob_size)
|
|
|
|
|
{
|
|
|
|
|
static const u8 ecc512[] = {
|
|
|
|
|
0, 1, 2, 3, 4, /* 5== mfr badblock */
|
|
|
|
|
6, 7, /* 8..12 for BBT or JFFS2 */ 13, 14, 15,
|
|
|
|
|
};
|
|
|
|
|
static const u8 ecc2048[] = {
|
|
|
|
|
24, 25, 26, 27, 28, 29, 30, 31, 32, 33,
|
|
|
|
|
34, 35, 36, 37, 38, 39, 40, 41, 42, 43,
|
|
|
|
|
44, 45, 46, 47, 48, 49, 50, 51, 52, 53,
|
|
|
|
|
54, 55, 56, 57, 58, 59, 60, 61, 62, 63,
|
|
|
|
|
};
|
|
|
|
|
static const u8 ecc4096[] = {
|
|
|
|
|
48, 49, 50, 51, 52, 53, 54, 55, 56, 57,
|
|
|
|
|
58, 59, 60, 61, 62, 63, 64, 65, 66, 67,
|
|
|
|
|
68, 69, 70, 71, 72, 73, 74, 75, 76, 77,
|
|
|
|
|
78, 79, 80, 81, 82, 83, 84, 85, 86, 87,
|
|
|
|
|
88, 89, 90, 91, 92, 93, 94, 95, 96, 97,
|
|
|
|
|
98, 99, 100, 101, 102, 103, 104, 105, 106, 107,
|
|
|
|
|
108, 109, 110, 111, 112, 113, 114, 115, 116, 117,
|
|
|
|
|
118, 119, 120, 121, 122, 123, 124, 125, 126, 127,
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
const u8 *l;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
const u32 fcr_addr = info->aemif + NANDFCR;
|
|
|
|
|
const u32 ecc4_addr = info->aemif + NAND4BITECC;
|
|
|
|
|
u32 fcr, ecc4;
|
|
|
|
|
|
|
|
|
|
/* Use the same ECC layout Linux uses. For small page chips
|
|
|
|
|
* it's a bit cramped.
|
|
|
|
|
*
|
|
|
|
|
* NOTE: at this writing, 4KB pages have issues in Linux
|
|
|
|
|
* because they need more than 64 bytes of ECC data, which
|
|
|
|
|
* the standard ECC logic can't handle.
|
|
|
|
|
*/
|
|
|
|
|
switch (nand->page_size) {
|
|
|
|
|
case 512:
|
|
|
|
|
l = ecc512;
|
|
|
|
|
break;
|
|
|
|
|
case 2048:
|
|
|
|
|
l = ecc2048;
|
|
|
|
|
break;
|
|
|
|
|
default:
|
|
|
|
|
l = ecc4096;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page);
|
|
|
|
|
|
|
|
|
|
/* scrub any old ECC state */
|
|
|
|
|
target_read_u32(target, info->aemif + NANDERRVAL, &ecc4);
|
|
|
|
|
|
|
|
|
|
target_read_u32(target, fcr_addr, &fcr);
|
|
|
|
|
fcr &= ~(0x03 << 4);
|
|
|
|
|
fcr |= (1 << 12) | (info->chipsel << 4);
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
u32 raw_ecc[4], *p;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* start 4bit ecc on csX */
|
|
|
|
|
target_write_u32(target, fcr_addr, fcr);
|
|
|
|
|
|
|
|
|
|
/* write 512 bytes */
|
|
|
|
|
davinci_write_block_data(nand, data, 512);
|
|
|
|
|
data += 512;
|
|
|
|
|
data_size -= 512;
|
|
|
|
|
|
|
|
|
|
/* read the ecc, then save it into 10 bytes in the oob */
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
|
target_read_u32(target, ecc4_addr + 4 * i, &raw_ecc[i]);
|
|
|
|
|
raw_ecc[i] &= 0x03ff03ff;
|
|
|
|
|
}
|
|
|
|
|
for (i = 0, p = raw_ecc; i < 2; i++, p += 2) {
|
|
|
|
|
oob[*l++] = p[0] & 0xff;
|
|
|
|
|
oob[*l++] = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc);
|
|
|
|
|
oob[*l++] = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0);
|
|
|
|
|
oob[*l++] = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0);
|
|
|
|
|
oob[*l++] = (p[1] >> 18) & 0xff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
} while (data_size);
|
|
|
|
|
|
|
|
|
|
/* write OOB into spare area */
|
|
|
|
|
return davinci_writepage_tail(nand, oob, oob_size);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* "Infix" OOB ... like Linux ECC_HW_SYNDROME. Avoided because it trashes
|
|
|
|
|
* manufacturer bad block markers, except on small page chips. Once you
|
|
|
|
|
* write to a page using this scheme, you need specialized code to update
|
|
|
|
|
* it (code which ignores now-invalid bad block markers).
|
|
|
|
|
*
|
|
|
|
|
* This is needed *only* to support older firmware. Older ROM Boot Loaders
|
|
|
|
|
* need it to read their second stage loader (UBL) into SRAM, but from then
|
|
|
|
|
* on the whole system can use the cleaner non-infix layouts. Systems with
|
|
|
|
|
* older second stage loaders (ABL/U-Boot, etc) or other system software
|
|
|
|
|
* (MVL 4.x/5.x kernels, filesystems, etc) may need it more generally.
|
|
|
|
|
*/
|
|
|
|
|
static int davinci_write_page_ecc4infix(struct nand_device_s *nand, u32 page,
|
|
|
|
|
u8 *data, u32 data_size, u8 *oob, u32 oob_size)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info = nand->controller_priv;
|
|
|
|
|
target_t *target = info->target;
|
|
|
|
|
const u32 fcr_addr = info->aemif + NANDFCR;
|
|
|
|
|
const u32 ecc4_addr = info->aemif + NAND4BITECC;
|
|
|
|
|
u32 fcr, ecc4;
|
|
|
|
|
|
|
|
|
|
davinci_write_pagecmd(nand, NAND_CMD_SEQIN, page);
|
|
|
|
|
|
|
|
|
|
/* scrub any old ECC state */
|
|
|
|
|
target_read_u32(target, info->aemif + NANDERRVAL, &ecc4);
|
|
|
|
|
|
|
|
|
|
target_read_u32(target, fcr_addr, &fcr);
|
|
|
|
|
fcr &= ~(0x03 << 4);
|
|
|
|
|
fcr |= (1 << 12) | (info->chipsel << 4);
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
u32 raw_ecc[4], *p;
|
|
|
|
|
u8 *l;
|
|
|
|
|
int i;
|
|
|
|
|
|
|
|
|
|
/* start 4bit ecc on csX */
|
|
|
|
|
target_write_u32(target, fcr_addr, fcr);
|
|
|
|
|
|
|
|
|
|
/* write 512 bytes */
|
|
|
|
|
davinci_write_block_data(nand, data, 512);
|
|
|
|
|
data += 512;
|
|
|
|
|
data_size -= 512;
|
|
|
|
|
|
|
|
|
|
/* read the ecc */
|
|
|
|
|
for (i = 0; i < 4; i++) {
|
|
|
|
|
target_read_u32(target, ecc4_addr + 4 * i, &raw_ecc[i]);
|
|
|
|
|
raw_ecc[i] &= 0x03ff03ff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* skip 6 bytes of prepad, then pack 10 packed ecc bytes */
|
|
|
|
|
for (i = 0, l = oob + 6, p = raw_ecc; i < 2; i++, p += 2) {
|
|
|
|
|
*l++ = p[0] & 0xff;
|
|
|
|
|
*l++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc);
|
|
|
|
|
*l++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0);
|
|
|
|
|
*l++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0);
|
|
|
|
|
*l++ = (p[1] >> 18) & 0xff;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* write this "out-of-band" data -- infix */
|
|
|
|
|
davinci_write_block_data(nand, oob, 16);
|
|
|
|
|
oob += 16;
|
|
|
|
|
oob_size -= 16;
|
|
|
|
|
|
|
|
|
|
} while (data_size);
|
|
|
|
|
|
|
|
|
|
/* the last data and OOB writes included the spare area */
|
|
|
|
|
return davinci_writepage_tail(nand, NULL, 0);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_read_page_ecc4infix(struct nand_device_s *nand, u32 page,
|
|
|
|
|
u8 *data, u32 data_size, u8 *oob, u32 oob_size)
|
|
|
|
|
{
|
|
|
|
|
davinci_write_pagecmd(nand, NAND_CMD_READ0, page);
|
|
|
|
|
|
|
|
|
|
/* large page devices need a start command */
|
|
|
|
|
if (nand->page_size > 512)
|
|
|
|
|
davinci_command(nand, NAND_CMD_READSTART);
|
|
|
|
|
|
|
|
|
|
if (!davinci_nand_ready(nand, 100))
|
|
|
|
|
return ERROR_NAND_OPERATION_TIMEOUT;
|
|
|
|
|
|
|
|
|
|
/* NOTE: not bothering to compute and use ECC data for now */
|
|
|
|
|
|
|
|
|
|
do {
|
|
|
|
|
/* write 512 bytes */
|
|
|
|
|
davinci_read_block_data(nand, data, 512);
|
|
|
|
|
data += 512;
|
|
|
|
|
data_size -= 512;
|
|
|
|
|
|
|
|
|
|
/* read this "out-of-band" data -- infix */
|
|
|
|
|
davinci_read_block_data(nand, oob, 16);
|
|
|
|
|
oob += 16;
|
|
|
|
|
oob_size -= 16;
|
|
|
|
|
} while (data_size);
|
|
|
|
|
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
static int davinci_nand_device_command(struct command_context_s *cmd_ctx,
|
|
|
|
|
char *cmd, char **argv, int argc,
|
|
|
|
|
struct nand_device_s *nand)
|
|
|
|
|
{
|
|
|
|
|
struct davinci_nand *info;
|
|
|
|
|
target_t *target;
|
|
|
|
|
unsigned long chip, aemif;
|
|
|
|
|
enum ecc eccmode;
|
|
|
|
|
int chipsel;
|
|
|
|
|
char *ep;
|
|
|
|
|
|
|
|
|
|
/* arguments:
|
|
|
|
|
* - "davinci"
|
|
|
|
|
* - target
|
|
|
|
|
* - nand chip address
|
|
|
|
|
* - ecc mode
|
|
|
|
|
* - aemif address
|
|
|
|
|
* Plus someday, optionally, ALE and CLE masks.
|
|
|
|
|
*/
|
|
|
|
|
if (argc < 5) {
|
|
|
|
|
LOG_ERROR("parameters: %s target "
|
|
|
|
|
"chip_addr hwecc_mode aemif_addr",
|
|
|
|
|
argv[0]);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
target = get_target(argv[1]);
|
|
|
|
|
if (!target) {
|
|
|
|
|
LOG_ERROR("invalid target %s", argv[1]);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
chip = strtoul(argv[2], &ep, 0);
|
|
|
|
|
if (*ep || chip == 0 || chip == ULONG_MAX) {
|
|
|
|
|
LOG_ERROR("Invalid NAND chip address %s", argv[2]);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (strcmp(argv[3], "hwecc1") == 0)
|
|
|
|
|
eccmode = HWECC1;
|
|
|
|
|
else if (strcmp(argv[3], "hwecc4") == 0)
|
|
|
|
|
eccmode = HWECC4;
|
|
|
|
|
else if (strcmp(argv[3], "hwecc4_infix") == 0)
|
|
|
|
|
eccmode = HWECC4_INFIX;
|
|
|
|
|
else {
|
|
|
|
|
LOG_ERROR("Invalid ecc mode %s", argv[3]);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
aemif = strtoul(argv[4], &ep, 0);
|
|
|
|
|
if (*ep || chip == 0 || chip == ULONG_MAX) {
|
|
|
|
|
LOG_ERROR("Invalid AEMIF controller address %s", argv[4]);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/* REVISIT what we'd *like* to do is look up valid ranges using
|
|
|
|
|
* target-specific declarations, and not even need to pass the
|
|
|
|
|
* AEMIF controller address.
|
|
|
|
|
*/
|
|
|
|
|
if (aemif == 0x01e00000 /* dm6446, dm357 */
|
|
|
|
|
|| aemif == 0x01e10000 /* dm335, dm355 */
|
|
|
|
|
|| aemif == 0x01d10000 /* dm365 */
|
|
|
|
|
) {
|
|
|
|
|
if (chip < 0x0200000 || chip >= 0x0a000000) {
|
|
|
|
|
LOG_ERROR("NAND address %08lx out of range?", chip);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
chipsel = (chip - 0x02000000) >> 21;
|
|
|
|
|
} else {
|
|
|
|
|
LOG_ERROR("unrecognized AEMIF controller address %08lx", aemif);
|
|
|
|
|
goto fail;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
info = calloc(1, sizeof *info);
|
|
|
|
|
if (info == NULL)
|
|
|
|
|
goto fail;
|
|
|
|
|
|
|
|
|
|
info->target = target;
|
|
|
|
|
info->eccmode = eccmode;
|
|
|
|
|
info->chipsel = chipsel;
|
|
|
|
|
info->aemif = aemif;
|
|
|
|
|
info->data = chip;
|
|
|
|
|
info->cmd = chip | 0x10;
|
|
|
|
|
info->addr = chip | 0x08;
|
|
|
|
|
|
|
|
|
|
nand->controller_priv = info;
|
|
|
|
|
|
|
|
|
|
/* NOTE: for now we don't do any error correction on read.
|
|
|
|
|
* Nothing else in OpenOCD currently corrects read errors,
|
|
|
|
|
* and in any case it's *writing* that we care most about.
|
|
|
|
|
*/
|
|
|
|
|
info->read_page = nand_read_page_raw;
|
|
|
|
|
|
|
|
|
|
switch (eccmode) {
|
|
|
|
|
case HWECC1:
|
|
|
|
|
/* ECC_HW, 1-bit corrections, 3 bytes ECC per 512 data bytes */
|
|
|
|
|
info->write_page = davinci_write_page_ecc1;
|
|
|
|
|
break;
|
|
|
|
|
case HWECC4:
|
|
|
|
|
/* ECC_HW, 4-bit corrections, 10 bytes ECC per 512 data bytes */
|
|
|
|
|
info->write_page = davinci_write_page_ecc4;
|
|
|
|
|
break;
|
|
|
|
|
case HWECC4_INFIX:
|
|
|
|
|
/* Same 4-bit ECC HW, with problematic page/ecc layout */
|
|
|
|
|
info->read_page = davinci_read_page_ecc4infix;
|
|
|
|
|
info->write_page = davinci_write_page_ecc4infix;
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return ERROR_OK;
|
|
|
|
|
|
|
|
|
|
fail:
|
|
|
|
|
return ERROR_NAND_OPERATION_FAILED;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
nand_flash_controller_t davinci_nand_controller = {
|
|
|
|
|
.name = "davinci",
|
|
|
|
|
.nand_device_command = davinci_nand_device_command,
|
|
|
|
|
.register_commands = davinci_register_commands,
|
|
|
|
|
.init = davinci_init,
|
|
|
|
|
.reset = davinci_reset,
|
|
|
|
|
.command = davinci_command,
|
|
|
|
|
.address = davinci_address,
|
|
|
|
|
.write_data = davinci_write_data,
|
|
|
|
|
.read_data = davinci_read_data,
|
|
|
|
|
.write_page = davinci_write_page,
|
|
|
|
|
.read_page = davinci_read_page,
|
|
|
|
|
.write_block_data = davinci_write_block_data,
|
|
|
|
|
.read_block_data = davinci_read_block_data,
|
|
|
|
|
.nand_ready = davinci_nand_ready,
|
|
|
|
|
};
|