Update MTD to that of Linux 2.6.22.1

A lot changed in the Linux MTD code, since it was last ported from
Linux to U-Boot. This patch takes U-Boot NAND support to the level
of Linux 2.6.22.1 and will enable support for very large NAND devices
(4KB pages) and ease the compatibility between U-Boot and Linux
filesystems.

This patch is tested on two custom boards with PPC and ARM
processors running YAFFS in U-Boot and Linux using gcc-4.1.2
cross compilers.

MAKEALL ppc/arm has some issues:
 * DOC/OneNand/nand_spl is not building (I have not tried porting
   these parts, and since I do not have any HW and I am not familiar
   with this code/HW I think its best left to someone else.)

Except for the issues mentioned above, I have ported all drivers
necessary to run MAKEALL ppc/arm without errors and warnings. Many
drivers were trivial to port, but some were not so trivial. The
following drivers must be examined carefully and maybe rewritten to
some degree:
 cpu/ppc4xx/ndfc.c
 cpu/arm926ejs/davinci/nand.c
 board/delta/nand.c
 board/zylonite/nand.c

Signed-off-by: William Juul <william.juul@tandberg.com>
Signed-off-by: Stig Olsen <stig.olsen@tandberg.com>
Signed-off-by: Scott Wood <scottwood@freescale.com>
This commit is contained in:
William Juul 2007-10-31 13:53:06 +01:00 committed by Scott Wood
parent cd82919e6c
commit cfa460adfd
37 changed files with 4829 additions and 3436 deletions

View file

@ -37,34 +37,29 @@
/* /*
* hardware specific access to control-lines * hardware specific access to control-lines
*/ */
static void bfin_hwcontrol(struct mtd_info *mtd, int cmd) static void bfin_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
register struct nand_chip *this = mtd->priv; register struct nand_chip *this = mtd->priv;
u32 IO_ADDR_W = (u32) this->IO_ADDR_W;
switch (cmd) { if (ctrl & NAND_CTRL_CHANGE) {
if( ctrl & NAND_CLE )
case NAND_CTL_SETCLE: IO_ADDR_W = CFG_NAND_BASE + BFIN_NAND_CLE;
this->IO_ADDR_W = CFG_NAND_BASE + BFIN_NAND_CLE; else
break; IO_ADDR_W = CFG_NAND_BASE;
case NAND_CTL_CLRCLE: if( ctrl & NAND_ALE )
this->IO_ADDR_W = CFG_NAND_BASE; IO_ADDR_W = CFG_NAND_BASE + BFIN_NAND_ALE;
break; else
IO_ADDR_W = CFG_NAND_BASE;
case NAND_CTL_SETALE: this->IO_ADDR_W = (void __iomem *) IO_ADDR_W;
this->IO_ADDR_W = CFG_NAND_BASE + BFIN_NAND_ALE;
break;
case NAND_CTL_CLRALE:
this->IO_ADDR_W = CFG_NAND_BASE;
break;
case NAND_CTL_SETNCE:
case NAND_CTL_CLRNCE:
break;
} }
this->IO_ADDR_R = this->IO_ADDR_W; this->IO_ADDR_R = this->IO_ADDR_W;
/* Drain the writebuffer */ /* Drain the writebuffer */
SSYNC(); SSYNC();
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
int bfin_device_ready(struct mtd_info *mtd) int bfin_device_ready(struct mtd_info *mtd)
@ -79,11 +74,11 @@ int bfin_device_ready(struct mtd_info *mtd)
* argument are board-specific (per include/linux/mtd/nand.h): * argument are board-specific (per include/linux/mtd/nand.h):
* - IO_ADDR_R?: address to read the 8 I/O lines of the flash device * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
* - IO_ADDR_W?: address to write the 8 I/O lines of the flash device * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
* - hwcontrol: hardwarespecific function for accesing control-lines * - cmd_ctrl: hardwarespecific function for accesing control-lines
* - dev_ready: hardwarespecific function for accesing device ready/busy line * - dev_ready: hardwarespecific function for accesing device ready/busy line
* - enable_hwecc?: function to enable (reset) hardware ecc generator. Must * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
* only be provided if a hardware ECC is available * only be provided if a hardware ECC is available
* - eccmode: mode of ecc, see defines * - ecc.mode: mode of ecc, see defines
* - chip_delay: chip dependent delay for transfering data from array to * - chip_delay: chip dependent delay for transfering data from array to
* read regs (tR) * read regs (tR)
* - options: various chip options. They can partly be set to inform * - options: various chip options. They can partly be set to inform
@ -98,8 +93,8 @@ void board_nand_init(struct nand_chip *nand)
*PORT(CONFIG_NAND_GPIO_PORT, IO_DIR) &= ~BFIN_NAND_READY; *PORT(CONFIG_NAND_GPIO_PORT, IO_DIR) &= ~BFIN_NAND_READY;
*PORT(CONFIG_NAND_GPIO_PORT, IO_INEN) |= BFIN_NAND_READY; *PORT(CONFIG_NAND_GPIO_PORT, IO_INEN) |= BFIN_NAND_READY;
nand->hwcontrol = bfin_hwcontrol; nand->cmd_ctrl = bfin_hwcontrol;
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
nand->dev_ready = bfin_device_ready; nand->dev_ready = bfin_device_ready;
nand->chip_delay = 30; nand->chip_delay = 30;
} }

View file

@ -21,7 +21,7 @@
*/ */
#include <common.h> #include <common.h>
#include <asm/io.h>
#if defined(CONFIG_CMD_NAND) #if defined(CONFIG_CMD_NAND)
@ -31,31 +31,28 @@
* hardware specific access to control-lines * hardware specific access to control-lines
* function borrowed from Linux 2.6 (drivers/mtd/nand/ppchameleonevb.c) * function borrowed from Linux 2.6 (drivers/mtd/nand/ppchameleonevb.c)
*/ */
static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd) static void ppchameleonevb_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
struct nand_chip *this = mtdinfo->priv; struct nand_chip *this = mtd->priv;
ulong base = (ulong) this->IO_ADDR_W; ulong base = (ulong) this->IO_ADDR_W;
switch(cmd) { if (ctrl & NAND_CTRL_CHANGE) {
case NAND_CTL_SETCLE: if ( ctrl & NAND_CLE )
MACRO_NAND_CTL_SETCLE((unsigned long)base); MACRO_NAND_CTL_SETCLE((unsigned long)base);
break; else
case NAND_CTL_CLRCLE:
MACRO_NAND_CTL_CLRCLE((unsigned long)base); MACRO_NAND_CTL_CLRCLE((unsigned long)base);
break; if ( ctrl & NAND_ALE )
case NAND_CTL_SETALE: MACRO_NAND_CTL_CLRCLE((unsigned long)base);
MACRO_NAND_CTL_SETALE((unsigned long)base); else
break;
case NAND_CTL_CLRALE:
MACRO_NAND_CTL_CLRALE((unsigned long)base); MACRO_NAND_CTL_CLRALE((unsigned long)base);
break; if ( ctrl & NAND_NCE )
case NAND_CTL_SETNCE:
MACRO_NAND_ENABLE_CE((unsigned long)base); MACRO_NAND_ENABLE_CE((unsigned long)base);
break; else
case NAND_CTL_CLRNCE:
MACRO_NAND_DISABLE_CE((unsigned long)base); MACRO_NAND_DISABLE_CE((unsigned long)base);
break;
} }
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
@ -92,11 +89,11 @@ static int ppchameleonevb_device_ready(struct mtd_info *mtdinfo)
* argument are board-specific (per include/linux/mtd/nand.h): * argument are board-specific (per include/linux/mtd/nand.h):
* - IO_ADDR_R?: address to read the 8 I/O lines of the flash device * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
* - IO_ADDR_W?: address to write the 8 I/O lines of the flash device * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
* - hwcontrol: hardwarespecific function for accesing control-lines * - cmd_ctrl: hardwarespecific function for accesing control-lines
* - dev_ready: hardwarespecific function for accesing device ready/busy line * - dev_ready: hardwarespecific function for accesing device ready/busy line
* - enable_hwecc?: function to enable (reset) hardware ecc generator. Must * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
* only be provided if a hardware ECC is available * only be provided if a hardware ECC is available
* - eccmode: mode of ecc, see defines * - ecc.mode: mode of ecc, see defines
* - chip_delay: chip dependent delay for transfering data from array to * - chip_delay: chip dependent delay for transfering data from array to
* read regs (tR) * read regs (tR)
* - options: various chip options. They can partly be set to inform * - options: various chip options. They can partly be set to inform
@ -108,9 +105,9 @@ static int ppchameleonevb_device_ready(struct mtd_info *mtdinfo)
int board_nand_init(struct nand_chip *nand) int board_nand_init(struct nand_chip *nand)
{ {
nand->hwcontrol = ppchameleonevb_hwcontrol; nand->cmd_ctrl = ppchameleonevb_hwcontrol;
nand->dev_ready = ppchameleonevb_device_ready; nand->dev_ready = ppchameleonevb_device_ready;
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
nand->chip_delay = NAND_BIG_DELAY_US; nand->chip_delay = NAND_BIG_DELAY_US;
nand->options = NAND_SAMSUNG_LP_OPTIONS; nand->options = NAND_SAMSUNG_LP_OPTIONS;
return 0; return 0;

View file

@ -69,7 +69,7 @@ static struct nand_oobinfo delta_oob = {
/* /*
* not required for Monahans DFC * not required for Monahans DFC
*/ */
static void dfc_hwcontrol(struct mtd_info *mtdinfo, int cmd) static void dfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
return; return;
} }
@ -110,30 +110,6 @@ static void dfc_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
} }
/*
* These functions are quite problematic for the DFC. Luckily they are
* not used in the current nand code, except for nand_command, which
* we've defined our own anyway. The problem is, that we always need
* to write 4 bytes to the DFC Data Buffer, but in these functions we
* don't know if to buffer the bytes/half words until we've gathered 4
* bytes or if to send them straight away.
*
* Solution: Don't use these with Mona's DFC and complain loudly.
*/
static void dfc_write_word(struct mtd_info *mtd, u16 word)
{
printf("dfc_write_word: WARNING, this function does not work with the Monahans DFC!\n");
}
static void dfc_write_byte(struct mtd_info *mtd, u_char byte)
{
printf("dfc_write_byte: WARNING, this function does not work with the Monahans DFC!\n");
}
/* The original:
* static void dfc_read_buf(struct mtd_info *mtd, const u_char *buf, int len)
*
* Shouldn't this be "u_char * const buf" ?
*/
static void dfc_read_buf(struct mtd_info *mtd, u_char* const buf, int len) static void dfc_read_buf(struct mtd_info *mtd, u_char* const buf, int len)
{ {
int i=0, j; int i=0, j;
@ -168,7 +144,7 @@ static void dfc_read_buf(struct mtd_info *mtd, u_char* const buf, int len)
*/ */
static u16 dfc_read_word(struct mtd_info *mtd) static u16 dfc_read_word(struct mtd_info *mtd)
{ {
printf("dfc_write_byte: UNIMPLEMENTED.\n"); printf("dfc_read_word: UNIMPLEMENTED.\n");
return 0; return 0;
} }
@ -289,9 +265,10 @@ static void dfc_new_cmd(void)
/* this function is called after Programm and Erase Operations to /* this function is called after Programm and Erase Operations to
* check for success or failure */ * check for success or failure */
static int dfc_wait(struct mtd_info *mtd, struct nand_chip *this, int state) static int dfc_wait(struct mtd_info *mtd, struct nand_chip *this)
{ {
unsigned long ndsr=0, event=0; unsigned long ndsr=0, event=0;
int state = this->state;
if(state == FL_WRITING) { if(state == FL_WRITING) {
event = NDSR_CS0_CMDD | NDSR_CS0_BBD; event = NDSR_CS0_CMDD | NDSR_CS0_BBD;
@ -439,7 +416,7 @@ static void dfc_gpio_init(void)
* - dev_ready: hardwarespecific function for accesing device ready/busy line * - dev_ready: hardwarespecific function for accesing device ready/busy line
* - enable_hwecc?: function to enable (reset) hardware ecc generator. Must * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
* only be provided if a hardware ECC is available * only be provided if a hardware ECC is available
* - eccmode: mode of ecc, see defines * - ecc.mode: mode of ecc, see defines
* - chip_delay: chip dependent delay for transfering data from array to * - chip_delay: chip dependent delay for transfering data from array to
* read regs (tR) * read regs (tR)
* - options: various chip options. They can partly be set to inform * - options: various chip options. They can partly be set to inform
@ -561,20 +538,18 @@ int board_nand_init(struct nand_chip *nand)
/* wait(10); */ /* wait(10); */
nand->hwcontrol = dfc_hwcontrol; nand->cmd_ctrl = dfc_hwcontrol;
/* nand->dev_ready = dfc_device_ready; */ /* nand->dev_ready = dfc_device_ready; */
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
nand->options = NAND_BUSWIDTH_16; nand->options = NAND_BUSWIDTH_16;
nand->waitfunc = dfc_wait; nand->waitfunc = dfc_wait;
nand->read_byte = dfc_read_byte; nand->read_byte = dfc_read_byte;
nand->write_byte = dfc_write_byte;
nand->read_word = dfc_read_word; nand->read_word = dfc_read_word;
nand->write_word = dfc_write_word;
nand->read_buf = dfc_read_buf; nand->read_buf = dfc_read_buf;
nand->write_buf = dfc_write_buf; nand->write_buf = dfc_write_buf;
nand->cmdfunc = dfc_cmdfunc; nand->cmdfunc = dfc_cmdfunc;
nand->autooob = &delta_oob; // nand->autooob = &delta_oob;
nand->badblock_pattern = &delta_bbt_descr; nand->badblock_pattern = &delta_bbt_descr;
return 0; return 0;
} }

View file

@ -30,28 +30,26 @@
/* /*
* hardware specific access to control-lines * hardware specific access to control-lines
*/ */
static void esd405ep_nand_hwcontrol(struct mtd_info *mtdinfo, int cmd) static void esd405ep_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
switch(cmd) { struct nand_chip *this = mtd->priv;
case NAND_CTL_SETCLE: if (ctrl & NAND_CTRL_CHANGE) {
if ( ctrl & NAND_CLE )
out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) | CFG_NAND_CLE); out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) | CFG_NAND_CLE);
break; else
case NAND_CTL_CLRCLE:
out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) & ~CFG_NAND_CLE); out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) & ~CFG_NAND_CLE);
break; if ( ctrl & NAND_ALE )
case NAND_CTL_SETALE:
out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) | CFG_NAND_ALE); out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) | CFG_NAND_ALE);
break; else
case NAND_CTL_CLRALE:
out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) & ~CFG_NAND_ALE); out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) & ~CFG_NAND_ALE);
break; if ( ctrl & NAND_NCE )
case NAND_CTL_SETNCE:
out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) & ~CFG_NAND_CE); out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) & ~CFG_NAND_CE);
break; else
case NAND_CTL_CLRNCE:
out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) | CFG_NAND_CE); out_be32((void *)GPIO0_OR, in_be32((void *)GPIO0_OR) | CFG_NAND_CE);
break;
} }
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
@ -77,9 +75,9 @@ int board_nand_init(struct nand_chip *nand)
/* /*
* Initialize nand_chip structure * Initialize nand_chip structure
*/ */
nand->hwcontrol = esd405ep_nand_hwcontrol; nand->cmd_ctrl = esd405ep_nand_hwcontrol;
nand->dev_ready = esd405ep_nand_device_ready; nand->dev_ready = esd405ep_nand_device_ready;
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
nand->chip_delay = NAND_BIG_DELAY_US; nand->chip_delay = NAND_BIG_DELAY_US;
nand->options = NAND_SAMSUNG_LP_OPTIONS; nand->options = NAND_SAMSUNG_LP_OPTIONS;
return 0; return 0;

View file

@ -40,36 +40,26 @@ DECLARE_GLOBAL_DATA_PTR;
#define SET_ALE 0x08 #define SET_ALE 0x08
#define CLR_ALE ~SET_ALE #define CLR_ALE ~SET_ALE
static void nand_hwcontrol(struct mtd_info *mtdinfo, int cmd) static void nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
struct nand_chip *this = mtdinfo->priv; struct nand_chip *this = mtdinfo->priv;
volatile fbcs_t *fbcs = (fbcs_t *) MMAP_FBCS; /* volatile fbcs_t *fbcs = (fbcs_t *) MMAP_FBCS; TODO: handle wp */
u32 nand_baseaddr = (u32) this->IO_ADDR_W; u32 nand_baseaddr = (u32) this->IO_ADDR_W;
switch (cmd) { if (ctrl & NAND_CTRL_CHANGE) {
case NAND_CTL_SETNCE: if ( ctrl & NAND_CLE )
case NAND_CTL_CLRNCE:
break;
case NAND_CTL_SETCLE:
nand_baseaddr |= SET_CLE; nand_baseaddr |= SET_CLE;
break; else
case NAND_CTL_CLRCLE:
nand_baseaddr &= CLR_CLE; nand_baseaddr &= CLR_CLE;
break; if ( ctrl & NAND_ALE )
case NAND_CTL_SETALE:
nand_baseaddr |= SET_ALE; nand_baseaddr |= SET_ALE;
break; else
case NAND_CTL_CLRALE: nand_baseaddr &= CLR_ALE;
nand_baseaddr |= CLR_ALE;
break;
case NAND_CTL_SETWP:
fbcs->csmr2 |= FBCS_CSMR_WP;
break;
case NAND_CTL_CLRWP:
fbcs->csmr2 &= ~FBCS_CSMR_WP;
break;
} }
this->IO_ADDR_W = (void __iomem *)(nand_baseaddr); this->IO_ADDR_W = (void __iomem *)(nand_baseaddr);
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
static void nand_write_byte(struct mtd_info *mtdinfo, u_char byte) static void nand_write_byte(struct mtd_info *mtdinfo, u_char byte)
@ -103,8 +93,8 @@ int board_nand_init(struct nand_chip *nand)
gpio->podr_timer = 0; gpio->podr_timer = 0;
nand->chip_delay = 50; nand->chip_delay = 50;
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
nand->hwcontrol = nand_hwcontrol; nand->cmd_ctrl = nand_hwcontrol;
nand->read_byte = nand_read_byte; nand->read_byte = nand_read_byte;
nand->write_byte = nand_write_byte; nand->write_byte = nand_write_byte;
nand->dev_ready = nand_dev_ready; nand->dev_ready = nand_dev_ready;

View file

@ -22,7 +22,7 @@
*/ */
#include <common.h> #include <common.h>
#include <asm/io.h>
#if defined(CONFIG_CMD_NAND) #if defined(CONFIG_CMD_NAND)
@ -32,57 +32,49 @@
/* /*
* hardware specific access to control-lines * hardware specific access to control-lines
*/ */
static void nc650_hwcontrol(struct mtd_info *mtd, int cmd) static void nc650_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
switch(cmd) { if (ctrl & NAND_CTRL_CHANGE) {
case NAND_CTL_SETCLE: if ( ctrl & NAND_CLE )
this->IO_ADDR_W += 2; this->IO_ADDR_W += 2;
break; else
case NAND_CTL_CLRCLE:
this->IO_ADDR_W -= 2; this->IO_ADDR_W -= 2;
break; if ( ctrl & NAND_ALE )
case NAND_CTL_SETALE:
this->IO_ADDR_W += 1; this->IO_ADDR_W += 1;
break; else
case NAND_CTL_CLRALE:
this->IO_ADDR_W -= 1; this->IO_ADDR_W -= 1;
break;
case NAND_CTL_SETNCE:
case NAND_CTL_CLRNCE:
/* nop */
break;
} }
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
#elif defined(CONFIG_IDS852_REV2) #elif defined(CONFIG_IDS852_REV2)
/* /*
* hardware specific access to control-lines * hardware specific access to control-lines
*/ */
static void nc650_hwcontrol(struct mtd_info *mtd, int cmd) static void nc650_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
switch(cmd) { if (ctrl & NAND_CTRL_CHANGE) {
case NAND_CTL_SETCLE: if ( ctrl & NAND_CLE )
*(((volatile __u8 *) this->IO_ADDR_W) + 0xa) = 0; writeb(0, (volatile __u8 *) this->IO_ADDR_W + 0xa);
break; else
case NAND_CTL_CLRCLE: writeb(0, (volatile __u8 *) this->IO_ADDR_W) + 0x8);
*(((volatile __u8 *) this->IO_ADDR_W) + 0x8) = 0; if ( ctrl & NAND_ALE )
break; writeb(0, (volatile __u8 *) this->IO_ADDR_W) + 0x9);
case NAND_CTL_SETALE: else
*(((volatile __u8 *) this->IO_ADDR_W) + 0x9) = 0; writeb(0, (volatile __u8 *) this->IO_ADDR_W) + 0x8);
break; if ( ctrl & NAND_NCE )
case NAND_CTL_CLRALE: writeb(0, (volatile __u8 *) this->IO_ADDR_W) + 0x8);
*(((volatile __u8 *) this->IO_ADDR_W) + 0x8) = 0; else
break; writeb(0, (volatile __u8 *) this->IO_ADDR_W) + 0xc);
case NAND_CTL_SETNCE:
*(((volatile __u8 *) this->IO_ADDR_W) + 0x8) = 0;
break;
case NAND_CTL_CLRNCE:
*(((volatile __u8 *) this->IO_ADDR_W) + 0xc) = 0;
break;
} }
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
#else #else
#error Unknown IDS852 module revision #error Unknown IDS852 module revision
@ -93,11 +85,11 @@ static void nc650_hwcontrol(struct mtd_info *mtd, int cmd)
* argument are board-specific (per include/linux/mtd/nand.h): * argument are board-specific (per include/linux/mtd/nand.h):
* - IO_ADDR_R?: address to read the 8 I/O lines of the flash device * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
* - IO_ADDR_W?: address to write the 8 I/O lines of the flash device * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
* - hwcontrol: hardwarespecific function for accesing control-lines * - cmd_ctrl: hardwarespecific function for accesing control-lines
* - dev_ready: hardwarespecific function for accesing device ready/busy line * - dev_ready: hardwarespecific function for accesing device ready/busy line
* - enable_hwecc?: function to enable (reset) hardware ecc generator. Must * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
* only be provided if a hardware ECC is available * only be provided if a hardware ECC is available
* - eccmode: mode of ecc, see defines * - eccm.ode: mode of ecc, see defines
* - chip_delay: chip dependent delay for transfering data from array to * - chip_delay: chip dependent delay for transfering data from array to
* read regs (tR) * read regs (tR)
* - options: various chip options. They can partly be set to inform * - options: various chip options. They can partly be set to inform
@ -109,8 +101,8 @@ static void nc650_hwcontrol(struct mtd_info *mtd, int cmd)
int board_nand_init(struct nand_chip *nand) int board_nand_init(struct nand_chip *nand)
{ {
nand->hwcontrol = nc650_hwcontrol; nand->cmd_ctrl = nc650_hwcontrol;
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
nand->chip_delay = 12; nand->chip_delay = 12;
/* nand->options = NAND_SAMSUNG_LP_OPTIONS;*/ /* nand->options = NAND_SAMSUNG_LP_OPTIONS;*/
return 0; return 0;

View file

@ -21,6 +21,7 @@
*/ */
#include <common.h> #include <common.h>
#include <asm/io.h>
#if defined(CONFIG_CMD_NAND) #if defined(CONFIG_CMD_NAND)
@ -32,24 +33,29 @@
#define MASK_CLE 0x02 #define MASK_CLE 0x02
#define MASK_ALE 0x04 #define MASK_ALE 0x04
static void netstar_nand_hwcontrol(struct mtd_info *mtd, int cmd) static void netstar_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
ulong IO_ADDR_W = (ulong) this->IO_ADDR_W; ulong IO_ADDR_W = (ulong) this->IO_ADDR_W;
IO_ADDR_W &= ~(MASK_ALE|MASK_CLE); IO_ADDR_W &= ~(MASK_ALE|MASK_CLE);
switch (cmd) { if (ctrl & NAND_CTRL_CHANGE) {
case NAND_CTL_SETCLE: IO_ADDR_W |= MASK_CLE; break; if ( ctrl & NAND_CLE )
case NAND_CTL_SETALE: IO_ADDR_W |= MASK_ALE; break; IO_ADDR_W |= MASK_CLE;
if ( ctrl & NAND_ALE )
IO_ADDR_W |= MASK_ALE;
} }
this->IO_ADDR_W = (void *) IO_ADDR_W; this->IO_ADDR_W = (void __iomem *) IO_ADDR_W;
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
int board_nand_init(struct nand_chip *nand) int board_nand_init(struct nand_chip *nand)
{ {
nand->options = NAND_SAMSUNG_LP_OPTIONS; nand->options = NAND_SAMSUNG_LP_OPTIONS;
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
nand->hwcontrol = netstar_nand_hwcontrol; nand->cmd_ctrl = netstar_nand_hwcontrol;
nand->chip_delay = 400; nand->chip_delay = 400;
return 0; return 0;
} }

View file

@ -56,43 +56,24 @@ static struct alpr_ndfc_regs *alpr_ndfc = NULL;
* *
* There are 2 NAND devices on the board, a Hynix HY27US08561A (1 GByte). * There are 2 NAND devices on the board, a Hynix HY27US08561A (1 GByte).
*/ */
static void alpr_nand_hwcontrol(struct mtd_info *mtd, int cmd) static void alpr_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
switch (cmd) { struct nand_chip *this = mtd->priv;
case NAND_CTL_SETCLE:
if (ctrl & NAND_CTRL_CHANGE) {
if ( ctrl & NAND_CLE )
hwctl |= 0x1; hwctl |= 0x1;
break; else
case NAND_CTL_CLRCLE:
hwctl &= ~0x1; hwctl &= ~0x1;
break; if ( ctrl & NAND_ALE )
case NAND_CTL_SETALE:
hwctl |= 0x2; hwctl |= 0x2;
break; else
case NAND_CTL_CLRALE:
hwctl &= ~0x2; hwctl &= ~0x2;
break; if ( (ctrl & NAND_NCE) != NAND_NCE)
case NAND_CTL_SETNCE:
break;
case NAND_CTL_CLRNCE:
writeb(0x00, &(alpr_ndfc->term)); writeb(0x00, &(alpr_ndfc->term));
break;
} }
} if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
static void alpr_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *nand = mtd->priv;
if (hwctl & 0x1)
/*
* IO_ADDR_W used as CMD[i] reg to support multiple NAND
* chips.
*/
writeb(byte, nand->IO_ADDR_W);
else if (hwctl & 0x2) {
writeb(byte, &(alpr_ndfc->addr_wait));
} else
writeb(byte, &(alpr_ndfc->data));
} }
static u_char alpr_nand_read_byte(struct mtd_info *mtd) static u_char alpr_nand_read_byte(struct mtd_info *mtd)
@ -158,12 +139,10 @@ int board_nand_init(struct nand_chip *nand)
{ {
alpr_ndfc = (struct alpr_ndfc_regs *)CFG_NAND_BASE; alpr_ndfc = (struct alpr_ndfc_regs *)CFG_NAND_BASE;
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
/* Reference hardware control function */ /* Reference hardware control function */
nand->hwcontrol = alpr_nand_hwcontrol; nand->cmd_ctrl = alpr_nand_hwcontrol;
/* Set command delay time */
nand->write_byte = alpr_nand_write_byte;
nand->read_byte = alpr_nand_read_byte; nand->read_byte = alpr_nand_read_byte;
nand->write_buf = alpr_nand_write_buf; nand->write_buf = alpr_nand_write_buf;
nand->read_buf = alpr_nand_read_buf; nand->read_buf = alpr_nand_read_buf;

View file

@ -52,40 +52,26 @@ static struct pdnb3_ndfc_regs *pdnb3_ndfc;
* *
* There is one NAND devices on the board, a Hynix HY27US08561A (32 MByte). * There is one NAND devices on the board, a Hynix HY27US08561A (32 MByte).
*/ */
static void pdnb3_nand_hwcontrol(struct mtd_info *mtd, int cmd) static void pdnb3_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
switch (cmd) { struct nand_chip *this = mtd->priv;
case NAND_CTL_SETCLE:
if (ctrl & NAND_CTRL_CHANGE) {
if ( ctrl & NAND_CLE )
hwctl |= 0x1; hwctl |= 0x1;
break;
case NAND_CTL_CLRCLE:
hwctl &= ~0x1;
break;
case NAND_CTL_SETALE:
hwctl |= 0x2;
break;
case NAND_CTL_CLRALE:
hwctl &= ~0x2;
break;
case NAND_CTL_SETNCE:
break;
case NAND_CTL_CLRNCE:
writeb(0x00, &(pdnb3_ndfc->term));
break;
}
}
static void pdnb3_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
if (hwctl & 0x1)
writeb(byte, &(pdnb3_ndfc->cmd));
else if (hwctl & 0x2)
writeb(byte, &(pdnb3_ndfc->addr));
else else
writeb(byte, &(pdnb3_ndfc->data)); hwctl &= ~0x1;
if ( ctrl & NAND_ALE )
hwctl |= 0x2;
else
hwctl &= ~0x2;
if ( (ctrl & NAND_NCE) != NAND_NCE)
writeb(0x00, &(pdnb3_ndfc->term));
} }
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
}
static u_char pdnb3_nand_read_byte(struct mtd_info *mtd) static u_char pdnb3_nand_read_byte(struct mtd_info *mtd)
{ {
@ -152,16 +138,13 @@ int board_nand_init(struct nand_chip *nand)
{ {
pdnb3_ndfc = (struct pdnb3_ndfc_regs *)CFG_NAND_BASE; pdnb3_ndfc = (struct pdnb3_ndfc_regs *)CFG_NAND_BASE;
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
/* Set address of NAND IO lines (Using Linear Data Access Region) */ /* Set address of NAND IO lines (Using Linear Data Access Region) */
nand->IO_ADDR_R = (void __iomem *) ((ulong) pdnb3_ndfc + 0x4); nand->IO_ADDR_R = (void __iomem *) ((ulong) pdnb3_ndfc + 0x4);
nand->IO_ADDR_W = (void __iomem *) ((ulong) pdnb3_ndfc + 0x4); nand->IO_ADDR_W = (void __iomem *) ((ulong) pdnb3_ndfc + 0x4);
/* Reference hardware control function */ /* Reference hardware control function */
nand->hwcontrol = pdnb3_nand_hwcontrol; nand->cmd_ctrl = pdnb3_nand_hwcontrol;
/* Set command delay time */
nand->hwcontrol = pdnb3_nand_hwcontrol;
nand->write_byte = pdnb3_nand_write_byte;
nand->read_byte = pdnb3_nand_read_byte; nand->read_byte = pdnb3_nand_read_byte;
nand->write_buf = pdnb3_nand_write_buf; nand->write_buf = pdnb3_nand_write_buf;
nand->read_buf = pdnb3_nand_read_buf; nand->read_buf = pdnb3_nand_read_buf;

View file

@ -39,30 +39,26 @@
static void *sc3_io_base; static void *sc3_io_base;
static void *sc3_control_base = (void *)0xEF600700; static void *sc3_control_base = (void *)0xEF600700;
static void sc3_nand_hwcontrol(struct mtd_info *mtd, int cmd) static void sc3_nand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
switch (cmd) { struct nand_chip *this = mtd->priv;
case NAND_CTL_SETCLE: if (ctrl & NAND_CTRL_CHANGE) {
if ( ctrl & NAND_CLE )
set_bit (SC3_NAND_CLE, sc3_control_base); set_bit (SC3_NAND_CLE, sc3_control_base);
break; else
case NAND_CTL_CLRCLE:
clear_bit (SC3_NAND_CLE, sc3_control_base); clear_bit (SC3_NAND_CLE, sc3_control_base);
break; if ( ctrl & NAND_ALE )
case NAND_CTL_SETALE:
set_bit (SC3_NAND_ALE, sc3_control_base); set_bit (SC3_NAND_ALE, sc3_control_base);
break; else
case NAND_CTL_CLRALE:
clear_bit (SC3_NAND_ALE, sc3_control_base); clear_bit (SC3_NAND_ALE, sc3_control_base);
break; if ( ctrl & NAND_NCE )
case NAND_CTL_SETNCE:
set_bit (SC3_NAND_CE, sc3_control_base); set_bit (SC3_NAND_CE, sc3_control_base);
break; else
case NAND_CTL_CLRNCE:
clear_bit (SC3_NAND_CE, sc3_control_base); clear_bit (SC3_NAND_CE, sc3_control_base);
break;
} }
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
static int sc3_nand_dev_ready(struct mtd_info *mtd) static int sc3_nand_dev_ready(struct mtd_info *mtd)
@ -79,14 +75,14 @@ static void sc3_select_chip(struct mtd_info *mtd, int chip)
int board_nand_init(struct nand_chip *nand) int board_nand_init(struct nand_chip *nand)
{ {
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
sc3_io_base = (void *) CFG_NAND_BASE; sc3_io_base = (void *) CFG_NAND_BASE;
/* Set address of NAND IO lines (Using Linear Data Access Region) */ /* Set address of NAND IO lines (Using Linear Data Access Region) */
nand->IO_ADDR_R = (void __iomem *) sc3_io_base; nand->IO_ADDR_R = (void __iomem *) sc3_io_base;
nand->IO_ADDR_W = (void __iomem *) sc3_io_base; nand->IO_ADDR_W = (void __iomem *) sc3_io_base;
/* Reference hardware control function */ /* Reference hardware control function */
nand->hwcontrol = sc3_nand_hwcontrol; nand->cmd_ctrl = sc3_nand_hwcontrol;
nand->dev_ready = sc3_nand_dev_ready; nand->dev_ready = sc3_nand_dev_ready;
nand->select_chip = sc3_select_chip; nand->select_chip = sc3_select_chip;
return 0; return 0;

View file

@ -1068,24 +1068,22 @@ int update_flash_size (int flash_size)
static u8 hwctl = 0; static u8 hwctl = 0;
static void upmnand_hwcontrol(struct mtd_info *mtdinfo, int cmd) static void upmnand_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
switch (cmd) { struct nand_chip *this = mtd->priv;
case NAND_CTL_SETCLE:
if (ctrl & NAND_CTRL_CHANGE) {
if ( ctrl & NAND_CLE )
hwctl |= 0x1; hwctl |= 0x1;
break; else
case NAND_CTL_CLRCLE:
hwctl &= ~0x1; hwctl &= ~0x1;
break; if ( ctrl & NAND_ALE )
case NAND_CTL_SETALE:
hwctl |= 0x2; hwctl |= 0x2;
break; else
case NAND_CTL_CLRALE:
hwctl &= ~0x2; hwctl &= ~0x2;
break;
} }
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
static void upmnand_write_byte(struct mtd_info *mtdinfo, u_char byte) static void upmnand_write_byte(struct mtd_info *mtdinfo, u_char byte)
@ -1188,9 +1186,9 @@ int board_nand_init(struct nand_chip *nand)
memctl->memc_br3 = CFG_NAND_BR; memctl->memc_br3 = CFG_NAND_BR;
memctl->memc_mbmr = (MxMR_OP_NORM); memctl->memc_mbmr = (MxMR_OP_NORM);
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
nand->hwcontrol = upmnand_hwcontrol; nand->cmd_ctrl = upmnand_hwcontrol;
nand->read_byte = upmnand_read_byte; nand->read_byte = upmnand_read_byte;
nand->write_byte = upmnand_write_byte; nand->write_byte = upmnand_write_byte;
nand->dev_ready = tqm8272_dev_ready; nand->dev_ready = tqm8272_dev_ready;

View file

@ -69,7 +69,7 @@ static struct nand_oobinfo delta_oob = {
/* /*
* not required for Monahans DFC * not required for Monahans DFC
*/ */
static void dfc_hwcontrol(struct mtd_info *mtdinfo, int cmd) static void dfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
return; return;
} }
@ -110,25 +110,6 @@ static void dfc_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
} }
/*
* These functions are quite problematic for the DFC. Luckily they are
* not used in the current nand code, except for nand_command, which
* we've defined our own anyway. The problem is, that we always need
* to write 4 bytes to the DFC Data Buffer, but in these functions we
* don't know if to buffer the bytes/half words until we've gathered 4
* bytes or if to send them straight away.
*
* Solution: Don't use these with Mona's DFC and complain loudly.
*/
static void dfc_write_word(struct mtd_info *mtd, u16 word)
{
printf("dfc_write_word: WARNING, this function does not work with the Monahans DFC!\n");
}
static void dfc_write_byte(struct mtd_info *mtd, u_char byte)
{
printf("dfc_write_byte: WARNING, this function does not work with the Monahans DFC!\n");
}
/* The original: /* The original:
* static void dfc_read_buf(struct mtd_info *mtd, const u_char *buf, int len) * static void dfc_read_buf(struct mtd_info *mtd, const u_char *buf, int len)
* *
@ -168,7 +149,7 @@ static void dfc_read_buf(struct mtd_info *mtd, u_char* const buf, int len)
*/ */
static u16 dfc_read_word(struct mtd_info *mtd) static u16 dfc_read_word(struct mtd_info *mtd)
{ {
printf("dfc_write_byte: UNIMPLEMENTED.\n"); printf("dfc_read_word: UNIMPLEMENTED.\n");
return 0; return 0;
} }
@ -289,9 +270,10 @@ static void dfc_new_cmd(void)
/* this function is called after Programm and Erase Operations to /* this function is called after Programm and Erase Operations to
* check for success or failure */ * check for success or failure */
static int dfc_wait(struct mtd_info *mtd, struct nand_chip *this, int state) static int dfc_wait(struct mtd_info *mtd, struct nand_chip *this)
{ {
unsigned long ndsr=0, event=0; unsigned long ndsr=0, event=0;
int state = this->state;
if(state == FL_WRITING) { if(state == FL_WRITING) {
event = NDSR_CS0_CMDD | NDSR_CS0_BBD; event = NDSR_CS0_CMDD | NDSR_CS0_BBD;
@ -435,11 +417,11 @@ static void dfc_gpio_init(void)
* argument are board-specific (per include/linux/mtd/nand_new.h): * argument are board-specific (per include/linux/mtd/nand_new.h):
* - IO_ADDR_R?: address to read the 8 I/O lines of the flash device * - IO_ADDR_R?: address to read the 8 I/O lines of the flash device
* - IO_ADDR_W?: address to write the 8 I/O lines of the flash device * - IO_ADDR_W?: address to write the 8 I/O lines of the flash device
* - hwcontrol: hardwarespecific function for accesing control-lines * - cmd_ctrl: hardwarespecific function for accesing control-lines
* - dev_ready: hardwarespecific function for accesing device ready/busy line * - dev_ready: hardwarespecific function for accesing device ready/busy line
* - enable_hwecc?: function to enable (reset) hardware ecc generator. Must * - enable_hwecc?: function to enable (reset) hardware ecc generator. Must
* only be provided if a hardware ECC is available * only be provided if a hardware ECC is available
* - eccmode: mode of ecc, see defines * - ecc.mode: mode of ecc, see defines
* - chip_delay: chip dependent delay for transfering data from array to * - chip_delay: chip dependent delay for transfering data from array to
* read regs (tR) * read regs (tR)
* - options: various chip options. They can partly be set to inform * - options: various chip options. They can partly be set to inform
@ -560,21 +542,18 @@ int board_nand_init(struct nand_chip *nand)
/* wait 10 us due to cmd buffer clear reset */ /* wait 10 us due to cmd buffer clear reset */
/* wait(10); */ /* wait(10); */
nand->cmd_ctrl = dfc_hwcontrol;
nand->hwcontrol = dfc_hwcontrol;
/* nand->dev_ready = dfc_device_ready; */ /* nand->dev_ready = dfc_device_ready; */
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
nand->options = NAND_BUSWIDTH_16; nand->options = NAND_BUSWIDTH_16;
nand->waitfunc = dfc_wait; nand->waitfunc = dfc_wait;
nand->read_byte = dfc_read_byte; nand->read_byte = dfc_read_byte;
nand->write_byte = dfc_write_byte;
nand->read_word = dfc_read_word; nand->read_word = dfc_read_word;
nand->write_word = dfc_write_word;
nand->read_buf = dfc_read_buf; nand->read_buf = dfc_read_buf;
nand->write_buf = dfc_write_buf; nand->write_buf = dfc_write_buf;
nand->cmdfunc = dfc_cmdfunc; nand->cmdfunc = dfc_cmdfunc;
nand->autooob = &delta_oob; // nand->autooob = &delta_oob;
nand->badblock_pattern = &delta_bbt_descr; nand->badblock_pattern = &delta_bbt_descr;
return 0; return 0;
} }

View file

@ -14,6 +14,7 @@
#include <linux/mtd/nftl.h> #include <linux/mtd/nftl.h>
#include <linux/mtd/doc2000.h> #include <linux/mtd/doc2000.h>
#if 0
#ifdef CFG_DOC_SUPPORT_2000 #ifdef CFG_DOC_SUPPORT_2000
#define DoC_is_2000(doc) (doc->ChipID == DOC_ChipID_Doc2k) #define DoC_is_2000(doc) (doc->ChipID == DOC_ChipID_Doc2k)
#else #else
@ -1629,3 +1630,6 @@ void doc_probe(unsigned long physadr)
puts ("No DiskOnChip found\n"); puts ("No DiskOnChip found\n");
} }
} }
#else
void doc_probe(unsigned long physadr) {}
#endif

View file

@ -18,6 +18,7 @@
* *
*/ */
#include <common.h> #include <common.h>
#include <linux/mtd/mtd.h>
#if defined(CONFIG_CMD_NAND) #if defined(CONFIG_CMD_NAND)
@ -47,20 +48,26 @@ static int nand_dump(nand_info_t *nand, ulong off)
int i; int i;
u_char *buf, *p; u_char *buf, *p;
buf = malloc(nand->oobblock + nand->oobsize); buf = malloc(nand->writesize + nand->oobsize);
if (!buf) { if (!buf) {
puts("No memory for page buffer\n"); puts("No memory for page buffer\n");
return 1; return 1;
} }
off &= ~(nand->oobblock - 1); off &= ~(nand->writesize - 1);
i = nand_read_raw(nand, buf, off, nand->oobblock, nand->oobsize); #if 0
i = nand_read_raw(nand, buf, off, nand->writesize, nand->oobsize);
#else
size_t dummy;
loff_t addr = (loff_t) off;
i = nand->read(nand, addr, nand->writesize, &dummy, buf);
#endif
if (i < 0) { if (i < 0) {
printf("Error (%d) reading page %08lx\n", i, off); printf("Error (%d) reading page %08lx\n", i, off);
free(buf); free(buf);
return 1; return 1;
} }
printf("Page %08lx dump:\n", off); printf("Page %08lx dump:\n", off);
i = nand->oobblock >> 4; p = buf; i = nand->writesize >> 4; p = buf;
while (i--) { while (i--) {
printf("\t%02x %02x %02x %02x %02x %02x %02x %02x" printf("\t%02x %02x %02x %02x %02x %02x %02x %02x"
" %02x %02x %02x %02x %02x %02x %02x %02x\n", " %02x %02x %02x %02x %02x %02x %02x %02x\n",
@ -155,7 +162,7 @@ out:
int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[]) int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
{ {
int i, dev, ret; int i, dev, ret = 0;
ulong addr, off; ulong addr, off;
size_t size; size_t size;
char *cmd, *s; char *cmd, *s;
@ -250,7 +257,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) { if (strcmp(cmd, "erase") == 0 || strcmp(cmd, "scrub") == 0) {
nand_erase_options_t opts; nand_erase_options_t opts;
/* "clean" at index 2 means request to write cleanmarker */ /* "clean" at index 2 means request to write cleanmarker */
int clean = argc > 2 && !strcmp("clean", argv[2]); int clean = !strcmp("clean", argv[2]);
int o = clean ? 3 : 2; int o = clean ? 3 : 2;
int scrub = !strcmp(cmd, "scrub"); int scrub = !strcmp(cmd, "scrub");
@ -260,6 +267,7 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
return 1; return 1;
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
opts.offset = off; opts.offset = off;
opts.length = size; opts.length = size;
opts.jffs2 = clean; opts.jffs2 = clean;
@ -332,28 +340,29 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
opts.length = size; opts.length = size;
opts.offset = off; opts.offset = off;
opts.quiet = quiet; opts.quiet = quiet;
ret = nand_read_opts(nand, &opts); // ret = nand_read_opts(nand, &opts);
} else { } else {
/* write */ /* write */
nand_write_options_t opts; mtd_oob_ops_t opts;
memset(&opts, 0, sizeof(opts)); memset(&opts, 0, sizeof(opts));
opts.buffer = (u_char*) addr; opts.datbuf = (u_char*) addr;
opts.length = size; opts.len = size;
opts.offset = off; opts.ooblen = 64;
/* opts.forcejffs2 = 1; */ opts.mode = MTD_OOB_AUTO;
opts.pad = 1; ret = nand_write_opts(nand, off, &opts);
opts.blockalign = 1;
opts.quiet = quiet;
ret = nand_write_opts(nand, &opts);
} }
} else if (s != NULL && !strcmp(s, ".oob")) { } else if (s != NULL && !strcmp(s, ".oob")) {
/* read out-of-band data */ /* out-of-band data */
mtd_oob_ops_t ops = {
.oobbuf = (u8 *)addr,
.ooblen = size,
.mode = MTD_OOB_RAW
};
if (read) if (read)
ret = nand->read_oob(nand, off, size, &size, ret = nand->read_oob(nand, off, &ops);
(u_char *) addr);
else else
ret = nand->write_oob(nand, off, size, &size, ret = nand->write_oob(nand, off, &ops);
(u_char *) addr);
} else { } else {
if (read) if (read)
ret = nand_read(nand, off, &size, (u_char *)addr); ret = nand_read(nand, off, &size, (u_char *)addr);
@ -397,9 +406,9 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
} }
if (status) { if (status) {
ulong block_start = 0; // ulong block_start = 0;
ulong off; ulong off;
int last_status = -1; // int last_status = -1;
struct nand_chip *nand_chip = nand->priv; struct nand_chip *nand_chip = nand->priv;
/* check the WP bit */ /* check the WP bit */
@ -408,33 +417,33 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
(nand_chip->read_byte(nand) & 0x80 ? (nand_chip->read_byte(nand) & 0x80 ?
"NOT " : "" ) ); "NOT " : "" ) );
for (off = 0; off < nand->size; off += nand->oobblock) { for (off = 0; off < nand->size; off += nand->writesize) {
int s = nand_get_lock_status(nand, off); // int s = nand_get_lock_status(nand, off);
//
/* print message only if status has changed // /* print message only if status has changed
* or at end of chip // * or at end of chip
*/ // */
if (off == nand->size - nand->oobblock // if (off == nand->size - nand->writesize
|| (s != last_status && off != 0)) { // || (s != last_status && off != 0)) {
//
printf("%08lx - %08lx: %8lu pages %s%s%s\n", // printf("%08lx - %08lx: %8d pages %s%s%s\n",
block_start, // block_start,
off-1, // off-1,
(off-block_start)/nand->oobblock, // (off-block_start)/nand->writesize,
((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""), // ((last_status & NAND_LOCK_STATUS_TIGHT) ? "TIGHT " : ""),
((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""), // ((last_status & NAND_LOCK_STATUS_LOCK) ? "LOCK " : ""),
((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : "")); // ((last_status & NAND_LOCK_STATUS_UNLOCK) ? "UNLOCK " : ""));
} // }
//
last_status = s; // last_status = s;
} }
} else { } else {
if (!nand_lock(nand, tight)) { // if (!nand_lock(nand, tight)) {
puts("NAND flash successfully locked\n"); // puts("NAND flash successfully locked\n");
} else { // } else {
puts("Error locking NAND flash\n"); // puts("Error locking NAND flash\n");
return 1; // return 1;
} // }
} }
return 0; return 0;
} }
@ -443,13 +452,13 @@ int do_nand(cmd_tbl_t * cmdtp, int flag, int argc, char *argv[])
if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0) if (arg_off_size(argc - 2, argv + 2, nand, &off, &size) < 0)
return 1; return 1;
if (!nand_unlock(nand, off, size)) { // if (!nand_unlock(nand, off, size)) {
puts("NAND flash successfully unlocked\n"); // puts("NAND flash successfully unlocked\n");
} else { // } else {
puts("Error unlocking NAND flash, " // puts("Error unlocking NAND flash, "
"write and erase will probably fail\n"); // "write and erase will probably fail\n");
return 1; // return 1;
} // }
return 0; return 0;
} }
@ -463,16 +472,18 @@ U_BOOT_CMD(nand, 5, 1, do_nand,
"info - show available NAND devices\n" "info - show available NAND devices\n"
"nand device [dev] - show or set current device\n" "nand device [dev] - show or set current device\n"
"nand read[.jffs2] - addr off|partition size\n" "nand read[.jffs2] - addr off|partition size\n"
"nand write[.jffs2] - addr off|partition size - read/write `size' bytes starting\n" "nand write[.jffs2] - addr off|partition size\n"
" at offset `off' to/from memory address `addr'\n" " read/write 'size' bytes starting at offset 'off'\n"
"nand erase [clean] [off size] - erase `size' bytes from\n" " to/from memory address 'addr'\n"
" offset `off' (entire device if not specified)\n" "nand erase [clean] [off size] - erase 'size' bytes from\n"
" offset 'off' (entire device if not specified)\n"
"nand bad - show bad blocks\n" "nand bad - show bad blocks\n"
"nand dump[.oob] off - dump page\n" "nand dump[.oob] off - dump page\n"
"nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n" "nand scrub - really clean NAND erasing bad blocks (UNSAFE)\n"
"nand markbad off - mark bad block at offset (UNSAFE)\n" "nand markbad off - mark bad block at offset (UNSAFE)\n"
"nand biterr off - make a bit error at offset (UNSAFE)\n" "nand biterr off - make a bit error at offset (UNSAFE)\n"
"nand lock [tight] [status] - bring nand to lock state or display locked pages\n" "nand lock [tight] [status]\n"
" bring nand to lock state or display locked pages\n"
"nand unlock [offset] [size] - unlock section\n"); "nand unlock [offset] [size] - unlock section\n");
static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand, static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
@ -494,19 +505,8 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset); printf("\nLoading from %s, offset 0x%lx\n", nand->name, offset);
cnt = nand->oobblock; cnt = nand->writesize;
if (jffs2) {
nand_read_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.buffer = (u_char*) addr;
opts.length = cnt;
opts.offset = offset;
opts.quiet = 1;
r = nand_read_opts(nand, &opts);
} else {
r = nand_read(nand, offset, &cnt, (u_char *) addr); r = nand_read(nand, offset, &cnt, (u_char *) addr);
}
if (r) { if (r) {
puts("** Read error\n"); puts("** Read error\n");
show_boot_progress (-56); show_boot_progress (-56);
@ -537,18 +537,7 @@ static int nand_load_image(cmd_tbl_t *cmdtp, nand_info_t *nand,
return 1; return 1;
} }
if (jffs2) {
nand_read_options_t opts;
memset(&opts, 0, sizeof(opts));
opts.buffer = (u_char*) addr;
opts.length = cnt;
opts.offset = offset;
opts.quiet = 1;
r = nand_read_opts(nand, &opts);
} else {
r = nand_read(nand, offset, &cnt, (u_char *) addr); r = nand_read(nand, offset, &cnt, (u_char *) addr);
}
if (r) { if (r) {
puts("** Read error\n"); puts("** Read error\n");
show_boot_progress (-58); show_boot_progress (-58);

View file

@ -42,6 +42,7 @@
*/ */
#include <common.h> #include <common.h>
#include <asm/io.h>
#ifdef CFG_USE_NAND #ifdef CFG_USE_NAND
#if !defined(CFG_NAND_LEGACY) #if !defined(CFG_NAND_LEGACY)
@ -52,23 +53,23 @@
extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE]; extern struct nand_chip nand_dev_desc[CFG_MAX_NAND_DEVICE];
static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd) static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
u_int32_t IO_ADDR_W = (u_int32_t)this->IO_ADDR_W; u_int32_t IO_ADDR_W = (u_int32_t)this->IO_ADDR_W;
IO_ADDR_W &= ~(MASK_ALE|MASK_CLE); IO_ADDR_W &= ~(MASK_ALE|MASK_CLE);
switch (cmd) { if (ctrl & NAND_CTRL_CHANGE) {
case NAND_CTL_SETCLE: if ( ctrl & NAND_CLE )
IO_ADDR_W |= MASK_CLE; IO_ADDR_W |= MASK_CLE;
break; if ( ctrl & NAND_ALE )
case NAND_CTL_SETALE:
IO_ADDR_W |= MASK_ALE; IO_ADDR_W |= MASK_ALE;
break; this->IO_ADDR_W = (void __iomem *) IO_ADDR_W;
} }
this->IO_ADDR_W = (void *)IO_ADDR_W; if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
/* Set WP on deselect, write enable on select */ /* Set WP on deselect, write enable on select */
@ -145,7 +146,7 @@ static int nand_davinci_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u
int region, n; int region, n;
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
n = (this->eccmode == NAND_ECC_HW12_2048) ? 4 : 1; n = (this->ecc.size/512);
region = 1; region = 1;
while (n--) { while (n--) {
@ -281,7 +282,7 @@ static int nand_davinci_correct_data(struct mtd_info *mtd, u_char *dat, u_char *
int block_count = 0, i, rc; int block_count = 0, i, rc;
this = mtd->priv; this = mtd->priv;
block_count = (this->eccmode == NAND_ECC_HW12_2048) ? 4 : 1; block_count = (this->ecc.size/512);
for (i = 0; i < block_count; i++) { for (i = 0; i < block_count; i++) {
if (memcmp(read_ecc, calc_ecc, 3) != 0) { if (memcmp(read_ecc, calc_ecc, 3) != 0) {
rc = nand_davinci_compare_ecc(read_ecc, calc_ecc, dat); rc = nand_davinci_compare_ecc(read_ecc, calc_ecc, dat);
@ -306,7 +307,7 @@ static int nand_davinci_dev_ready(struct mtd_info *mtd)
return(emif_addr->NANDFSR & 0x1); return(emif_addr->NANDFSR & 0x1);
} }
static int nand_davinci_waitfunc(struct mtd_info *mtd, struct nand_chip *this, int state) static int nand_davinci_waitfunc(struct mtd_info *mtd, struct nand_chip *this)
{ {
while(!nand_davinci_dev_ready(mtd)) {;} while(!nand_davinci_dev_ready(mtd)) {;}
*NAND_CE0CLE = NAND_STATUS; *NAND_CE0CLE = NAND_STATUS;
@ -362,22 +363,26 @@ int board_nand_init(struct nand_chip *nand)
#endif #endif
#ifdef CFG_NAND_HW_ECC #ifdef CFG_NAND_HW_ECC
#ifdef CFG_NAND_LARGEPAGE #ifdef CFG_NAND_LARGEPAGE
nand->eccmode = NAND_ECC_HW12_2048; nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = 2048;
nand->ecc.bytes = 12;
#elif defined(CFG_NAND_SMALLPAGE) #elif defined(CFG_NAND_SMALLPAGE)
nand->eccmode = NAND_ECC_HW3_512; nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = 512;
nand->ecc.bytes = 3;
#else #else
#error "Either CFG_NAND_LARGEPAGE or CFG_NAND_SMALLPAGE must be defined!" #error "Either CFG_NAND_LARGEPAGE or CFG_NAND_SMALLPAGE must be defined!"
#endif #endif
nand->autooob = &davinci_nand_oobinfo; // nand->autooob = &davinci_nand_oobinfo;
nand->calculate_ecc = nand_davinci_calculate_ecc; nand->ecc.calculate = nand_davinci_calculate_ecc;
nand->correct_data = nand_davinci_correct_data; nand->ecc.correct = nand_davinci_correct_data;
nand->enable_hwecc = nand_davinci_enable_hwecc; nand->ecc.hwctl = nand_davinci_enable_hwecc;
#else #else
nand->eccmode = NAND_ECC_SOFT; nand->ecc.mode = NAND_ECC_SOFT;
#endif #endif
/* Set address of hardware control function */ /* Set address of hardware control function */
nand->hwcontrol = nand_davinci_hwcontrol; nand->cmd_ctrl = nand_davinci_hwcontrol;
nand->dev_ready = nand_davinci_dev_ready; nand->dev_ready = nand_davinci_dev_ready;
nand->waitfunc = nand_davinci_waitfunc; nand->waitfunc = nand_davinci_waitfunc;

View file

@ -46,38 +46,22 @@
static u8 hwctl = 0; static u8 hwctl = 0;
static void ndfc_hwcontrol(struct mtd_info *mtdinfo, int cmd) static void ndfc_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl)
{ {
switch (cmd) { struct nand_chip *this = mtd->priv;
case NAND_CTL_SETCLE:
if (ctrl & NAND_CTRL_CHANGE) {
if ( ctrl & NAND_CLE )
hwctl |= 0x1; hwctl |= 0x1;
break;
case NAND_CTL_CLRCLE:
hwctl &= ~0x1;
break;
case NAND_CTL_SETALE:
hwctl |= 0x2;
break;
case NAND_CTL_CLRALE:
hwctl &= ~0x2;
break;
}
}
static void ndfc_write_byte(struct mtd_info *mtdinfo, u_char byte)
{
struct nand_chip *this = mtdinfo->priv;
ulong base = (ulong) this->IO_ADDR_W & 0xfffffffc;
if (hwctl & 0x1)
out_8((u8 *)(base + NDFC_CMD), byte);
else if (hwctl & 0x2)
out_8((u8 *)(base + NDFC_ALE), byte);
else else
out_8((u8 *)(base + NDFC_DATA), byte); hwctl &= ~0x1;
if ( ctrl & NAND_ALE )
hwctl |= 0x2;
else
hwctl &= ~0x2;
}
if (cmd != NAND_CMD_NONE)
writeb(cmd, this->IO_ADDR_W);
} }
static u_char ndfc_read_byte(struct mtd_info *mtdinfo) static u_char ndfc_read_byte(struct mtd_info *mtdinfo)
@ -194,16 +178,17 @@ int board_nand_init(struct nand_chip *nand)
int cs = (ulong)nand->IO_ADDR_W & 0x00000003; int cs = (ulong)nand->IO_ADDR_W & 0x00000003;
ulong base = (ulong)nand->IO_ADDR_W & 0xfffffffc; ulong base = (ulong)nand->IO_ADDR_W & 0xfffffffc;
nand->hwcontrol = ndfc_hwcontrol; nand->cmd_ctrl = ndfc_hwcontrol;
nand->read_byte = ndfc_read_byte; nand->read_byte = ndfc_read_byte;
nand->read_buf = ndfc_read_buf; nand->read_buf = ndfc_read_buf;
nand->write_byte = ndfc_write_byte;
nand->dev_ready = ndfc_dev_ready; nand->dev_ready = ndfc_dev_ready;
nand->eccmode = NAND_ECC_HW3_256; nand->ecc.correct = nand_correct_data;
nand->enable_hwecc = ndfc_enable_hwecc; nand->ecc.hwctl = ndfc_enable_hwecc;
nand->calculate_ecc = ndfc_calculate_ecc; nand->ecc.calculate = ndfc_calculate_ecc;
nand->correct_data = nand_correct_data; nand->ecc.mode = NAND_ECC_HW;
nand->ecc.size = 256;
nand->ecc.bytes = 3;
#ifndef CONFIG_NAND_SPL #ifndef CONFIG_NAND_SPL
nand->write_buf = ndfc_write_buf; nand->write_buf = ndfc_write_buf;

View file

@ -16,7 +16,7 @@
* *
* Interface to generic NAND code for M-Systems DiskOnChip devices * Interface to generic NAND code for M-Systems DiskOnChip devices
* *
* $Id: diskonchip.c,v 1.45 2005/01/05 18:05:14 dwmw2 Exp $ * $Id: diskonchip.c,v 1.55 2005/11/07 11:14:30 gleixner Exp $
*/ */
#include <common.h> #include <common.h>
@ -39,13 +39,13 @@
#include <linux/mtd/inftl.h> #include <linux/mtd/inftl.h>
/* Where to look for the devices? */ /* Where to look for the devices? */
#ifndef CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS #ifndef CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS
#define CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS 0 #define CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS 0
#endif #endif
static unsigned long __initdata doc_locations[] = { static unsigned long __initdata doc_locations[] = {
#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
#ifdef CONFIG_MTD_DISKONCHIP_PROBE_HIGH #ifdef CONFIG_MTD_NAND_DISKONCHIP_PROBE_HIGH
0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000, 0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000, 0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000, 0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
@ -65,7 +65,7 @@ static unsigned long __initdata doc_locations[] = {
0xff000000, 0xff000000,
#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C) #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
0xff000000, 0xff000000,
##else #else
#warning Unknown architecture for DiskOnChip. No default probe locations defined #warning Unknown architecture for DiskOnChip. No default probe locations defined
#endif #endif
0xffffffff }; 0xffffffff };
@ -85,14 +85,10 @@ struct doc_priv {
struct mtd_info *nextdoc; struct mtd_info *nextdoc;
}; };
/* Max number of eraseblocks to scan (from start of device) for the (I)NFTL
MediaHeader. The spec says to just keep going, I think, but that's just
silly. */
#define MAX_MEDIAHEADER_SCAN 8
/* This is the syndrome computed by the HW ecc generator upon reading an empty /* This is the syndrome computed by the HW ecc generator upon reading an empty
page, one with all 0xff for data and stored ecc code. */ page, one with all 0xff for data and stored ecc code. */
static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a }; static u_char empty_read_syndrome[6] = { 0x26, 0xff, 0x6d, 0x47, 0x73, 0x7a };
/* This is the ecc value computed by the HW ecc generator upon writing an empty /* This is the ecc value computed by the HW ecc generator upon writing an empty
page, one with all 0xff for data. */ page, one with all 0xff for data. */
static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 }; static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
@ -103,7 +99,8 @@ static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) #define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) #define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd); static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int bitmask);
static void doc200x_select_chip(struct mtd_info *mtd, int chip); static void doc200x_select_chip(struct mtd_info *mtd, int chip);
static int debug = 0; static int debug = 0;
@ -115,23 +112,23 @@ module_param(try_dword, int, 0);
static int no_ecc_failures = 0; static int no_ecc_failures = 0;
module_param(no_ecc_failures, int, 0); module_param(no_ecc_failures, int, 0);
#ifdef CONFIG_MTD_PARTITIONS
static int no_autopart = 0; static int no_autopart = 0;
module_param(no_autopart, int, 0); module_param(no_autopart, int, 0);
#endif
#ifdef MTD_NAND_DISKONCHIP_BBTWRITE static int show_firmware_partition = 0;
module_param(show_firmware_partition, int, 0);
#ifdef CONFIG_MTD_NAND_DISKONCHIP_BBTWRITE
static int inftl_bbt_write = 1; static int inftl_bbt_write = 1;
#else #else
static int inftl_bbt_write = 0; static int inftl_bbt_write = 0;
#endif #endif
module_param(inftl_bbt_write, int, 0); module_param(inftl_bbt_write, int, 0);
static unsigned long doc_config_location = CONFIG_MTD_DISKONCHIP_PROBE_ADDRESS; static unsigned long doc_config_location = CONFIG_MTD_NAND_DISKONCHIP_PROBE_ADDRESS;
module_param(doc_config_location, ulong, 0); module_param(doc_config_location, ulong, 0);
MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
/* Sector size for HW ECC */ /* Sector size for HW ECC */
#define SECTOR_SIZE 512 #define SECTOR_SIZE 512
/* The sector bytes are packed into NB_DATA 10 bit words */ /* The sector bytes are packed into NB_DATA 10 bit words */
@ -213,8 +210,7 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
can be modified since pos is even */ can be modified since pos is even */
index = (pos >> 3) ^ 1; index = (pos >> 3) ^ 1;
bitpos = pos & 7; bitpos = pos & 7;
if ((index >= 0 && index < SECTOR_SIZE) || if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
index == (SECTOR_SIZE + 1)) {
val = (uint8_t) (errval[i] >> (2 + bitpos)); val = (uint8_t) (errval[i] >> (2 + bitpos));
parity ^= val; parity ^= val;
if (index < SECTOR_SIZE) if (index < SECTOR_SIZE)
@ -224,8 +220,7 @@ static int doc_ecc_decode (struct rs_control *rs, uint8_t *data, uint8_t *ecc)
bitpos = (bitpos + 10) & 7; bitpos = (bitpos + 10) & 7;
if (bitpos == 0) if (bitpos == 0)
bitpos = 8; bitpos = 8;
if ((index >= 0 && index < SECTOR_SIZE) || if ((index >= 0 && index < SECTOR_SIZE) || index == (SECTOR_SIZE + 1)) {
index == (SECTOR_SIZE + 1)) {
val = (uint8_t) (errval[i] << (8 - bitpos)); val = (uint8_t) (errval[i] << (8 - bitpos));
parity ^= val; parity ^= val;
if (index < SECTOR_SIZE) if (index < SECTOR_SIZE)
@ -261,7 +256,8 @@ static int _DoC_WaitReady(struct doc_priv *doc)
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
unsigned long timeo = jiffies + (HZ * 10); unsigned long timeo = jiffies + (HZ * 10);
if(debug) printk("_DoC_WaitReady...\n"); if (debug)
printk("_DoC_WaitReady...\n");
/* Out-of-line routine to wait for chip response */ /* Out-of-line routine to wait for chip response */
if (DoC_is_MillenniumPlus(doc)) { if (DoC_is_MillenniumPlus(doc)) {
while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) { while ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) {
@ -306,7 +302,8 @@ static inline int DoC_WaitReady(struct doc_priv *doc)
DoC_Delay(doc, 2); DoC_Delay(doc, 2);
} }
if(debug) printk("DoC_WaitReady OK\n"); if (debug)
printk("DoC_WaitReady OK\n");
return ret; return ret;
} }
@ -316,7 +313,8 @@ static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
if(debug)printk("write_byte %02x\n", datum); if (debug)
printk("write_byte %02x\n", datum);
WriteDOC(datum, docptr, CDSNSlowIO); WriteDOC(datum, docptr, CDSNSlowIO);
WriteDOC(datum, docptr, 2k_CDSN_IO); WriteDOC(datum, docptr, 2k_CDSN_IO);
} }
@ -331,35 +329,37 @@ static u_char doc2000_read_byte(struct mtd_info *mtd)
ReadDOC(docptr, CDSNSlowIO); ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(doc, 2); DoC_Delay(doc, 2);
ret = ReadDOC(docptr, 2k_CDSN_IO); ret = ReadDOC(docptr, 2k_CDSN_IO);
if (debug) printk("read_byte returns %02x\n", ret); if (debug)
printk("read_byte returns %02x\n", ret);
return ret; return ret;
} }
static void doc2000_writebuf(struct mtd_info *mtd, static void doc2000_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
const u_char *buf, int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
int i; int i;
if (debug)printk("writebuf of %d bytes: ", len); if (debug)
printk("writebuf of %d bytes: ", len);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
if (debug && i < 16) if (debug && i < 16)
printk("%02x ", buf[i]); printk("%02x ", buf[i]);
} }
if (debug) printk("\n"); if (debug)
printk("\n");
} }
static void doc2000_readbuf(struct mtd_info *mtd, static void doc2000_readbuf(struct mtd_info *mtd, u_char *buf, int len)
u_char *buf, int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
int i; int i;
if (debug)printk("readbuf of %d bytes: ", len); if (debug)
printk("readbuf of %d bytes: ", len);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
@ -374,7 +374,8 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd,
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
int i; int i;
if (debug) printk("readbuf_dword of %d bytes: ", len); if (debug)
printk("readbuf_dword of %d bytes: ", len);
if (unlikely((((unsigned long)buf) | len) & 3)) { if (unlikely((((unsigned long)buf) | len) & 3)) {
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
@ -387,8 +388,7 @@ static void doc2000_readbuf_dword(struct mtd_info *mtd,
} }
} }
static int doc2000_verifybuf(struct mtd_info *mtd, static int doc2000_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
const u_char *buf, int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
@ -408,12 +408,15 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
uint16_t ret; uint16_t ret;
doc200x_select_chip(mtd, nr); doc200x_select_chip(mtd, nr);
doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); doc200x_hwcontrol(mtd, NAND_CMD_READID,
this->write_byte(mtd, NAND_CMD_READID); NAND_CTRL_CLE | NAND_CTRL_CHANGE);
doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
doc200x_hwcontrol(mtd, NAND_CTL_SETALE); doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
this->write_byte(mtd, 0);
doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); /* We cant' use dev_ready here, but at least we wait for the
* command to complete
*/
udelay(50);
ret = this->read_byte(mtd) << 8; ret = this->read_byte(mtd) << 8;
ret |= this->read_byte(mtd); ret |= this->read_byte(mtd);
@ -426,12 +429,13 @@ static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
} ident; } ident;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); doc200x_hwcontrol(mtd, NAND_CMD_READID,
doc2000_write_byte(mtd, NAND_CMD_READID); NAND_CTRL_CLE | NAND_CTRL_CHANGE);
doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); doc200x_hwcontrol(mtd, 0, NAND_CTRL_ALE | NAND_CTRL_CHANGE);
doc200x_hwcontrol(mtd, NAND_CTL_SETALE); doc200x_hwcontrol(mtd, NAND_CMD_NONE,
doc2000_write_byte(mtd, 0); NAND_NCE | NAND_CTRL_CHANGE);
doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
udelay(50);
ident.dword = readl(docptr + DoC_2k_CDSN_IO); ident.dword = readl(docptr + DoC_2k_CDSN_IO);
if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
@ -465,7 +469,7 @@ static void __init doc2000_count_chips(struct mtd_info *mtd)
printk(KERN_DEBUG "Detected %d chips per floor.\n", i); printk(KERN_DEBUG "Detected %d chips per floor.\n", i);
} }
static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this)
{ {
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
@ -496,16 +500,15 @@ static u_char doc2001_read_byte(struct mtd_info *mtd)
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
/*ReadDOC(docptr, CDSNSlowIO); */ //ReadDOC(docptr, CDSNSlowIO);
/* 11.4.5 -- delay twice to allow extended length cycle */ /* 11.4.5 -- delay twice to allow extended length cycle */
DoC_Delay(doc, 2); DoC_Delay(doc, 2);
ReadDOC(docptr, ReadPipeInit); ReadDOC(docptr, ReadPipeInit);
/*return ReadDOC(docptr, Mil_CDSN_IO); */ //return ReadDOC(docptr, Mil_CDSN_IO);
return ReadDOC(docptr, LastDataRead); return ReadDOC(docptr, LastDataRead);
} }
static void doc2001_writebuf(struct mtd_info *mtd, static void doc2001_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
const u_char *buf, int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
@ -518,8 +521,7 @@ static void doc2001_writebuf(struct mtd_info *mtd,
WriteDOC(0x00, docptr, WritePipeTerm); WriteDOC(0x00, docptr, WritePipeTerm);
} }
static void doc2001_readbuf(struct mtd_info *mtd, static void doc2001_readbuf(struct mtd_info *mtd, u_char *buf, int len)
u_char *buf, int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
@ -536,8 +538,7 @@ static void doc2001_readbuf(struct mtd_info *mtd,
buf[i] = ReadDOC(docptr, LastDataRead); buf[i] = ReadDOC(docptr, LastDataRead);
} }
static int doc2001_verifybuf(struct mtd_info *mtd, static int doc2001_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
const u_char *buf, int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
@ -567,36 +568,38 @@ static u_char doc2001plus_read_byte(struct mtd_info *mtd)
ReadDOC(docptr, Mplus_ReadPipeInit); ReadDOC(docptr, Mplus_ReadPipeInit);
ReadDOC(docptr, Mplus_ReadPipeInit); ReadDOC(docptr, Mplus_ReadPipeInit);
ret = ReadDOC(docptr, Mplus_LastDataRead); ret = ReadDOC(docptr, Mplus_LastDataRead);
if (debug) printk("read_byte returns %02x\n", ret); if (debug)
printk("read_byte returns %02x\n", ret);
return ret; return ret;
} }
static void doc2001plus_writebuf(struct mtd_info *mtd, static void doc2001plus_writebuf(struct mtd_info *mtd, const u_char *buf, int len)
const u_char *buf, int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
int i; int i;
if (debug)printk("writebuf of %d bytes: ", len); if (debug)
printk("writebuf of %d bytes: ", len);
for (i = 0; i < len; i++) { for (i = 0; i < len; i++) {
WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
if (debug && i < 16) if (debug && i < 16)
printk("%02x ", buf[i]); printk("%02x ", buf[i]);
} }
if (debug) printk("\n"); if (debug)
printk("\n");
} }
static void doc2001plus_readbuf(struct mtd_info *mtd, static void doc2001plus_readbuf(struct mtd_info *mtd, u_char *buf, int len)
u_char *buf, int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
int i; int i;
if (debug)printk("readbuf of %d bytes: ", len); if (debug)
printk("readbuf of %d bytes: ", len);
/* Start read pipeline */ /* Start read pipeline */
ReadDOC(docptr, Mplus_ReadPipeInit); ReadDOC(docptr, Mplus_ReadPipeInit);
@ -615,18 +618,19 @@ static void doc2001plus_readbuf(struct mtd_info *mtd,
buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead); buf[len - 1] = ReadDOC(docptr, Mplus_LastDataRead);
if (debug && i < 16) if (debug && i < 16)
printk("%02x ", buf[len - 1]); printk("%02x ", buf[len - 1]);
if (debug) printk("\n"); if (debug)
printk("\n");
} }
static int doc2001plus_verifybuf(struct mtd_info *mtd, static int doc2001plus_verifybuf(struct mtd_info *mtd, const u_char *buf, int len)
const u_char *buf, int len)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
int i; int i;
if (debug)printk("verifybuf of %d bytes: ", len); if (debug)
printk("verifybuf of %d bytes: ", len);
/* Start read pipeline */ /* Start read pipeline */
ReadDOC(docptr, Mplus_ReadPipeInit); ReadDOC(docptr, Mplus_ReadPipeInit);
@ -652,7 +656,8 @@ static void doc2001plus_select_chip(struct mtd_info *mtd, int chip)
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
int floor = 0; int floor = 0;
if(debug)printk("select chip (%d)\n", chip); if (debug)
printk("select chip (%d)\n", chip);
if (chip == -1) { if (chip == -1) {
/* Disable flash internally */ /* Disable flash internally */
@ -678,7 +683,8 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip)
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
int floor = 0; int floor = 0;
if(debug)printk("select chip (%d)\n", chip); if (debug)
printk("select chip (%d)\n", chip);
if (chip == -1) if (chip == -1)
return; return;
@ -687,54 +693,42 @@ static void doc200x_select_chip(struct mtd_info *mtd, int chip)
chip -= (floor * doc->chips_per_floor); chip -= (floor * doc->chips_per_floor);
/* 11.4.4 -- deassert CE before changing chip */ /* 11.4.4 -- deassert CE before changing chip */
doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE); doc200x_hwcontrol(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
WriteDOC(floor, docptr, FloorSelect); WriteDOC(floor, docptr, FloorSelect);
WriteDOC(chip, docptr, CDSNDeviceSelect); WriteDOC(chip, docptr, CDSNDeviceSelect);
doc200x_hwcontrol(mtd, NAND_CTL_SETNCE); doc200x_hwcontrol(mtd, NAND_CMD_NONE, NAND_NCE | NAND_CTRL_CHANGE);
doc->curchip = chip; doc->curchip = chip;
doc->curfloor = floor; doc->curfloor = floor;
} }
static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) #define CDSN_CTRL_MSK (CDSN_CTRL_CE | CDSN_CTRL_CLE | CDSN_CTRL_ALE)
static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd,
unsigned int ctrl)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
switch(cmd) { if (ctrl & NAND_CTRL_CHANGE) {
case NAND_CTL_SETNCE: doc->CDSNControl &= ~CDSN_CTRL_MSK;
doc->CDSNControl |= CDSN_CTRL_CE; doc->CDSNControl |= ctrl & CDSN_CTRL_MSK;
break; if (debug)
case NAND_CTL_CLRNCE: printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
doc->CDSNControl &= ~CDSN_CTRL_CE;
break;
case NAND_CTL_SETCLE:
doc->CDSNControl |= CDSN_CTRL_CLE;
break;
case NAND_CTL_CLRCLE:
doc->CDSNControl &= ~CDSN_CTRL_CLE;
break;
case NAND_CTL_SETALE:
doc->CDSNControl |= CDSN_CTRL_ALE;
break;
case NAND_CTL_CLRALE:
doc->CDSNControl &= ~CDSN_CTRL_ALE;
break;
case NAND_CTL_SETWP:
doc->CDSNControl |= CDSN_CTRL_WP;
break;
case NAND_CTL_CLRWP:
doc->CDSNControl &= ~CDSN_CTRL_WP;
break;
}
if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl);
WriteDOC(doc->CDSNControl, docptr, CDSNControl); WriteDOC(doc->CDSNControl, docptr, CDSNControl);
/* 11.4.3 -- 4 NOPs after CSDNControl write */ /* 11.4.3 -- 4 NOPs after CSDNControl write */
DoC_Delay(doc, 4); DoC_Delay(doc, 4);
} }
if (cmd != NAND_CMD_NONE) {
if (DoC_is_2000(doc))
doc2000_write_byte(mtd, cmd);
else
doc2001_write_byte(mtd, cmd);
}
}
static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr) static void doc2001plus_command(struct mtd_info *mtd, unsigned command, int column, int page_addr)
{ {
@ -757,9 +751,9 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
if (command == NAND_CMD_SEQIN) { if (command == NAND_CMD_SEQIN) {
int readcmd; int readcmd;
if (column >= mtd->oobblock) { if (column >= mtd->writesize) {
/* OOB area */ /* OOB area */
column -= mtd->oobblock; column -= mtd->writesize;
readcmd = NAND_CMD_READOOB; readcmd = NAND_CMD_READOOB;
} else if (column < 256) { } else if (column < 256) {
/* First 256 bytes --> READ0 */ /* First 256 bytes --> READ0 */
@ -794,7 +788,8 @@ static void doc2001plus_command (struct mtd_info *mtd, unsigned command, int col
WriteDOC(0, docptr, Mplus_WritePipeTerm); WriteDOC(0, docptr, Mplus_WritePipeTerm);
WriteDOC(0, docptr, Mplus_WritePipeTerm); WriteDOC(0, docptr, Mplus_WritePipeTerm);
/* deassert ALE */ /* deassert ALE */
if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 || command == NAND_CMD_READOOB || command == NAND_CMD_READID) if (command == NAND_CMD_READ0 || command == NAND_CMD_READ1 ||
command == NAND_CMD_READOOB || command == NAND_CMD_READID)
WriteDOC(0, docptr, Mplus_FlashControl); WriteDOC(0, docptr, Mplus_FlashControl);
} }
@ -854,7 +849,8 @@ static int doc200x_dev_ready(struct mtd_info *mtd)
printk("not ready\n"); printk("not ready\n");
return 0; return 0;
} }
if (debug)printk("was ready\n"); if (debug)
printk("was ready\n");
return 1; return 1;
} else { } else {
/* 11.4.2 -- must NOP four times before checking FR/B# */ /* 11.4.2 -- must NOP four times before checking FR/B# */
@ -866,7 +862,8 @@ static int doc200x_dev_ready(struct mtd_info *mtd)
} }
/* 11.4.2 -- Must NOP twice if it's ready */ /* 11.4.2 -- Must NOP twice if it's ready */
DoC_Delay(doc, 2); DoC_Delay(doc, 2);
if (debug)printk("was ready\n"); if (debug)
printk("was ready\n");
return 1; return 1;
} }
} }
@ -917,8 +914,7 @@ static void doc2001plus_enable_hwecc(struct mtd_info *mtd, int mode)
} }
/* This code is only called on write */ /* This code is only called on write */
static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat, unsigned char *ecc_code)
unsigned char *ecc_code)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
@ -962,7 +958,8 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
often. It could be optimized away by examining the data in often. It could be optimized away by examining the data in
the writebuf routine, and remembering the result. */ the writebuf routine, and remembering the result. */
for (i = 0; i < 512; i++) { for (i = 0; i < 512; i++) {
if (dat[i] == 0xff) continue; if (dat[i] == 0xff)
continue;
emptymatch = 0; emptymatch = 0;
break; break;
} }
@ -970,17 +967,20 @@ static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
/* If emptymatch still =1, we do have an all-0xff data buffer. /* If emptymatch still =1, we do have an all-0xff data buffer.
Return all-0xff ecc value instead of the computed one, so Return all-0xff ecc value instead of the computed one, so
it'll look just like a freshly-erased page. */ it'll look just like a freshly-erased page. */
if (emptymatch) memset(ecc_code, 0xff, 6); if (emptymatch)
memset(ecc_code, 0xff, 6);
#endif #endif
return 0; return 0;
} }
static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat,
u_char *read_ecc, u_char *isnull)
{ {
int i, ret = 0; int i, ret = 0;
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
void __iomem *docptr = doc->virtadr; void __iomem *docptr = doc->virtadr;
uint8_t calc_ecc[6];
volatile u_char dummy; volatile u_char dummy;
int emptymatch = 1; int emptymatch = 1;
@ -1013,7 +1013,8 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
all-0xff data and stored ecc block. Check the stored ecc. */ all-0xff data and stored ecc block. Check the stored ecc. */
if (emptymatch) { if (emptymatch) {
for (i = 0; i < 6; i++) { for (i = 0; i < 6; i++) {
if (read_ecc[i] == 0xff) continue; if (read_ecc[i] == 0xff)
continue;
emptymatch = 0; emptymatch = 0;
break; break;
} }
@ -1024,7 +1025,8 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
often. It could be optimized away by examining the data in often. It could be optimized away by examining the data in
the readbuf routine, and remembering the result. */ the readbuf routine, and remembering the result. */
for (i = 0; i < 512; i++) { for (i = 0; i < 512; i++) {
if (dat[i] == 0xff) continue; if (dat[i] == 0xff)
continue;
emptymatch = 0; emptymatch = 0;
break; break;
} }
@ -1033,7 +1035,8 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
erased block, in which case the ECC will not come out right. erased block, in which case the ECC will not come out right.
We'll suppress the error and tell the caller everything's We'll suppress the error and tell the caller everything's
OK. Because it is. */ OK. Because it is. */
if (!emptymatch) ret = doc_ecc_decode (rs_decoder, dat, calc_ecc); if (!emptymatch)
ret = doc_ecc_decode(rs_decoder, dat, calc_ecc);
if (ret > 0) if (ret > 0)
printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret); printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
} }
@ -1048,13 +1051,22 @@ static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_
return ret; return ret;
} }
/*u_char mydatabuf[528]; */ //u_char mydatabuf[528];
static struct nand_oobinfo doc200x_oobinfo = { /* The strange out-of-order .oobfree list below is a (possibly unneeded)
.useecc = MTD_NANDECC_AUTOPLACE, * attempt to retain compatibility. It used to read:
* .oobfree = { {8, 8} }
* Since that leaves two bytes unusable, it was changed. But the following
* scheme might affect existing jffs2 installs by moving the cleanmarker:
* .oobfree = { {6, 10} }
* jffs2 seems to handle the above gracefully, but the current scheme seems
* safer. The only problem with it is that any code that parses oobfree must
* be able to handle out-of-order segments.
*/
static struct nand_ecclayout doc200x_oobinfo = {
.eccbytes = 6, .eccbytes = 6,
.eccpos = {0, 1, 2, 3, 4, 5}, .eccpos = {0, 1, 2, 3, 4, 5},
.oobfree = { {8, 8} } .oobfree = {{8, 8}, {6, 2}}
}; };
/* Find the (I)NFTL Media Header, and optionally also the mirror media header. /* Find the (I)NFTL Media Header, and optionally also the mirror media header.
@ -1063,28 +1075,28 @@ static struct nand_oobinfo doc200x_oobinfo = {
either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media either "ANAND" or "BNAND". If findmirror=1, also look for the mirror media
header. The page #s of the found media headers are placed in mh0_page and header. The page #s of the found media headers are placed in mh0_page and
mh1_page in the DOC private structure. */ mh1_page in the DOC private structure. */
static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, static int __init find_media_headers(struct mtd_info *mtd, u_char *buf, const char *id, int findmirror)
const char *id, int findmirror)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
unsigned offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift); unsigned offs;
int ret; int ret;
size_t retlen; size_t retlen;
end = min(end, mtd->size); /* paranoia */ for (offs = 0; offs < mtd->size; offs += mtd->erasesize) {
for (offs = 0; offs < end; offs += mtd->erasesize) { ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf);
ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); if (retlen != mtd->writesize)
if (retlen != mtd->oobblock) continue; continue;
if (ret) { if (ret) {
printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n", offs);
offs);
} }
if (memcmp(buf, id, 6)) continue; if (memcmp(buf, id, 6))
continue;
printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs); printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
if (doc->mh0_page == -1) { if (doc->mh0_page == -1) {
doc->mh0_page = offs >> this->page_shift; doc->mh0_page = offs >> this->page_shift;
if (!findmirror) return 1; if (!findmirror)
return 1;
continue; continue;
} }
doc->mh1_page = offs >> this->page_shift; doc->mh1_page = offs >> this->page_shift;
@ -1097,8 +1109,8 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
/* Only one mediaheader was found. We want buf to contain a /* Only one mediaheader was found. We want buf to contain a
mediaheader on return, so we'll have to re-read the one we found. */ mediaheader on return, so we'll have to re-read the one we found. */
offs = doc->mh0_page << this->page_shift; offs = doc->mh0_page << this->page_shift;
ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf); ret = mtd->read(mtd, offs, mtd->writesize, &retlen, buf);
if (retlen != mtd->oobblock) { if (retlen != mtd->writesize) {
/* Insanity. Give up. */ /* Insanity. Give up. */
printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n"); printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
return 0; return 0;
@ -1106,8 +1118,7 @@ static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
return 1; return 1;
} }
static inline int __init nftl_partscan(struct mtd_info *mtd, static inline int __init nftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
struct mtd_partition *parts)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
@ -1115,19 +1126,23 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
u_char *buf; u_char *buf;
struct NFTLMediaHeader *mh; struct NFTLMediaHeader *mh;
const unsigned psize = 1 << this->page_shift; const unsigned psize = 1 << this->page_shift;
int numparts = 0;
unsigned blocks, maxblocks; unsigned blocks, maxblocks;
int offs, numheaders; int offs, numheaders;
buf = kmalloc(mtd->oobblock, GFP_KERNEL); buf = kmalloc(mtd->writesize, GFP_KERNEL);
if (!buf) { if (!buf) {
printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
return 0; return 0;
} }
if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) goto out; if (!(numheaders = find_media_headers(mtd, buf, "ANAND", 1)))
goto out;
mh = (struct NFTLMediaHeader *)buf; mh = (struct NFTLMediaHeader *)buf;
/*#ifdef CONFIG_MTD_DEBUG_VERBOSE */ mh->NumEraseUnits = le16_to_cpu(mh->NumEraseUnits);
/* if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */ mh->FirstPhysicalEUN = le16_to_cpu(mh->FirstPhysicalEUN);
mh->FormattedSize = le32_to_cpu(mh->FormattedSize);
printk(KERN_INFO " DataOrgID = %s\n" printk(KERN_INFO " DataOrgID = %s\n"
" NumEraseUnits = %d\n" " NumEraseUnits = %d\n"
" FirstPhysicalEUN = %d\n" " FirstPhysicalEUN = %d\n"
@ -1136,7 +1151,6 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
mh->DataOrgID, mh->NumEraseUnits, mh->DataOrgID, mh->NumEraseUnits,
mh->FirstPhysicalEUN, mh->FormattedSize, mh->FirstPhysicalEUN, mh->FormattedSize,
mh->UnitSizeFactor); mh->UnitSizeFactor);
/*#endif */
blocks = mtd->size >> this->phys_erase_shift; blocks = mtd->size >> this->phys_erase_shift;
maxblocks = min(32768U, mtd->erasesize - psize); maxblocks = min(32768U, mtd->erasesize - psize);
@ -1179,31 +1193,35 @@ static inline int __init nftl_partscan(struct mtd_info *mtd,
offs <<= this->page_shift; offs <<= this->page_shift;
offs += mtd->erasesize; offs += mtd->erasesize;
/*parts[0].name = " DiskOnChip Boot / Media Header partition"; */ if (show_firmware_partition == 1) {
/*parts[0].offset = 0; */ parts[0].name = " DiskOnChip Firmware / Media Header partition";
/*parts[0].size = offs; */ parts[0].offset = 0;
parts[0].size = offs;
parts[0].name = " DiskOnChip BDTL partition"; numparts = 1;
parts[0].offset = offs;
parts[0].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
offs += parts[0].size;
if (offs < mtd->size) {
parts[1].name = " DiskOnChip Remainder partition";
parts[1].offset = offs;
parts[1].size = mtd->size - offs;
ret = 2;
goto out;
} }
ret = 1;
parts[numparts].name = " DiskOnChip BDTL partition";
parts[numparts].offset = offs;
parts[numparts].size = (mh->NumEraseUnits - numheaders) << this->bbt_erase_shift;
offs += parts[numparts].size;
numparts++;
if (offs < mtd->size) {
parts[numparts].name = " DiskOnChip Remainder partition";
parts[numparts].offset = offs;
parts[numparts].size = mtd->size - offs;
numparts++;
}
ret = numparts;
out: out:
kfree(buf); kfree(buf);
return ret; return ret;
} }
/* This is a stripped-down copy of the code in inftlmount.c */ /* This is a stripped-down copy of the code in inftlmount.c */
static inline int __init inftl_partscan(struct mtd_info *mtd, static inline int __init inftl_partscan(struct mtd_info *mtd, struct mtd_partition *parts)
struct mtd_partition *parts)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
@ -1220,13 +1238,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
if (inftl_bbt_write) if (inftl_bbt_write)
end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift); end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
buf = kmalloc(mtd->oobblock, GFP_KERNEL); buf = kmalloc(mtd->writesize, GFP_KERNEL);
if (!buf) { if (!buf) {
printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n"); printk(KERN_ERR "DiskOnChip mediaheader kmalloc failed!\n");
return 0; return 0;
} }
if (!find_media_headers(mtd, buf, "BNAND", 0)) goto out; if (!find_media_headers(mtd, buf, "BNAND", 0))
goto out;
doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift); doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
mh = (struct INFTLMediaHeader *)buf; mh = (struct INFTLMediaHeader *)buf;
@ -1237,8 +1256,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
mh->FormatFlags = le32_to_cpu(mh->FormatFlags); mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
mh->PercentUsed = le32_to_cpu(mh->PercentUsed); mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
/*#ifdef CONFIG_MTD_DEBUG_VERBOSE */
/* if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */
printk(KERN_INFO " bootRecordID = %s\n" printk(KERN_INFO " bootRecordID = %s\n"
" NoOfBootImageBlocks = %d\n" " NoOfBootImageBlocks = %d\n"
" NoOfBinaryPartitions = %d\n" " NoOfBinaryPartitions = %d\n"
@ -1256,7 +1273,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
((unsigned char *) &mh->OsakVersion)[2] & 0xf, ((unsigned char *) &mh->OsakVersion)[2] & 0xf,
((unsigned char *) &mh->OsakVersion)[3] & 0xf, ((unsigned char *) &mh->OsakVersion)[3] & 0xf,
mh->PercentUsed); mh->PercentUsed);
/*#endif */
vshift = this->phys_erase_shift + mh->BlockMultiplierBits; vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
@ -1282,8 +1298,6 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
ip->spareUnits = le32_to_cpu(ip->spareUnits); ip->spareUnits = le32_to_cpu(ip->spareUnits);
ip->Reserved0 = le32_to_cpu(ip->Reserved0); ip->Reserved0 = le32_to_cpu(ip->Reserved0);
/*#ifdef CONFIG_MTD_DEBUG_VERBOSE */
/* if (CONFIG_MTD_DEBUG_VERBOSE >= 2) */
printk(KERN_INFO " PARTITION[%d] ->\n" printk(KERN_INFO " PARTITION[%d] ->\n"
" virtualUnits = %d\n" " virtualUnits = %d\n"
" firstUnit = %d\n" " firstUnit = %d\n"
@ -1293,16 +1307,14 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
i, ip->virtualUnits, ip->firstUnit, i, ip->virtualUnits, ip->firstUnit,
ip->lastUnit, ip->flags, ip->lastUnit, ip->flags,
ip->spareUnits); ip->spareUnits);
/*#endif */
/* if ((show_firmware_partition == 1) &&
if ((i == 0) && (ip->firstUnit > 0)) { (i == 0) && (ip->firstUnit > 0)) {
parts[0].name = " DiskOnChip IPL / Media Header partition"; parts[0].name = " DiskOnChip IPL / Media Header partition";
parts[0].offset = 0; parts[0].offset = 0;
parts[0].size = mtd->erasesize * ip->firstUnit; parts[0].size = mtd->erasesize * ip->firstUnit;
numparts = 1; numparts = 1;
} }
*/
if (ip->flags & INFTL_BINARY) if (ip->flags & INFTL_BINARY)
parts[numparts].name = " DiskOnChip BDK partition"; parts[numparts].name = " DiskOnChip BDK partition";
@ -1311,8 +1323,10 @@ static inline int __init inftl_partscan(struct mtd_info *mtd,
parts[numparts].offset = ip->firstUnit << vshift; parts[numparts].offset = ip->firstUnit << vshift;
parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift; parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
numparts++; numparts++;
if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit; if (ip->lastUnit > lastvunit)
if (ip->flags & INFTL_LAST) break; lastvunit = ip->lastUnit;
if (ip->flags & INFTL_LAST)
break;
} }
lastvunit++; lastvunit++;
if ((lastvunit << vshift) < end) { if ((lastvunit << vshift) < end) {
@ -1338,7 +1352,8 @@ static int __init nftl_scan_bbt(struct mtd_info *mtd)
/* On NFTL, we have to find the media headers before we can read the /* On NFTL, we have to find the media headers before we can read the
BBTs, since they're stored in the media header eraseblocks. */ BBTs, since they're stored in the media header eraseblocks. */
numparts = nftl_partscan(mtd, parts); numparts = nftl_partscan(mtd, parts);
if (!numparts) return -EIO; if (!numparts)
return -EIO;
this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT | this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
NAND_BBT_SAVECONTENT | NAND_BBT_WRITE | NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
NAND_BBT_VERSION; NAND_BBT_VERSION;
@ -1385,8 +1400,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
this->bbt_td->pages[0] = 2; this->bbt_td->pages[0] = 2;
this->bbt_md = NULL; this->bbt_md = NULL;
} else { } else {
this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;
NAND_BBT_VERSION;
if (inftl_bbt_write) if (inftl_bbt_write)
this->bbt_td->options |= NAND_BBT_WRITE; this->bbt_td->options |= NAND_BBT_WRITE;
this->bbt_td->offs = 8; this->bbt_td->offs = 8;
@ -1396,8 +1410,7 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
this->bbt_td->reserved_block_code = 0x01; this->bbt_td->reserved_block_code = 0x01;
this->bbt_td->pattern = "MSYS_BBT"; this->bbt_td->pattern = "MSYS_BBT";
this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT | NAND_BBT_VERSION;
NAND_BBT_VERSION;
if (inftl_bbt_write) if (inftl_bbt_write)
this->bbt_md->options |= NAND_BBT_WRITE; this->bbt_md->options |= NAND_BBT_WRITE;
this->bbt_md->offs = 8; this->bbt_md->offs = 8;
@ -1417,7 +1430,8 @@ static int __init inftl_scan_bbt(struct mtd_info *mtd)
/* At least for now, require the INFTL Media Header. We could probably /* At least for now, require the INFTL Media Header. We could probably
do without it for non-INFTL use, since all it gives us is do without it for non-INFTL use, since all it gives us is
autopartitioning, but I want to give it more thought. */ autopartitioning, but I want to give it more thought. */
if (!numparts) return -EIO; if (!numparts)
return -EIO;
add_mtd_device(mtd); add_mtd_device(mtd);
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
if (!no_autopart) if (!no_autopart)
@ -1431,7 +1445,6 @@ static inline int __init doc2000_init(struct mtd_info *mtd)
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
this->write_byte = doc2000_write_byte;
this->read_byte = doc2000_read_byte; this->read_byte = doc2000_read_byte;
this->write_buf = doc2000_writebuf; this->write_buf = doc2000_writebuf;
this->read_buf = doc2000_readbuf; this->read_buf = doc2000_readbuf;
@ -1449,7 +1462,6 @@ static inline int __init doc2001_init(struct mtd_info *mtd)
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
this->write_byte = doc2001_write_byte;
this->read_byte = doc2001_read_byte; this->read_byte = doc2001_read_byte;
this->write_buf = doc2001_writebuf; this->write_buf = doc2001_writebuf;
this->read_buf = doc2001_readbuf; this->read_buf = doc2001_readbuf;
@ -1481,16 +1493,15 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct doc_priv *doc = this->priv; struct doc_priv *doc = this->priv;
this->write_byte = NULL;
this->read_byte = doc2001plus_read_byte; this->read_byte = doc2001plus_read_byte;
this->write_buf = doc2001plus_writebuf; this->write_buf = doc2001plus_writebuf;
this->read_buf = doc2001plus_readbuf; this->read_buf = doc2001plus_readbuf;
this->verify_buf = doc2001plus_verifybuf; this->verify_buf = doc2001plus_verifybuf;
this->scan_bbt = inftl_scan_bbt; this->scan_bbt = inftl_scan_bbt;
this->hwcontrol = NULL; this->cmd_ctrl = NULL;
this->select_chip = doc2001plus_select_chip; this->select_chip = doc2001plus_select_chip;
this->cmdfunc = doc2001plus_command; this->cmdfunc = doc2001plus_command;
this->enable_hwecc = doc2001plus_enable_hwecc; this->ecc.hwctl = doc2001plus_enable_hwecc;
doc->chips_per_floor = 1; doc->chips_per_floor = 1;
mtd->name = "DiskOnChip Millennium Plus"; mtd->name = "DiskOnChip Millennium Plus";
@ -1498,7 +1509,7 @@ static inline int __init doc2001plus_init(struct mtd_info *mtd)
return 1; return 1;
} }
static inline int __init doc_probe(unsigned long physadr) static int __init doc_probe(unsigned long physadr)
{ {
unsigned char ChipID; unsigned char ChipID;
struct mtd_info *mtd; struct mtd_info *mtd;
@ -1527,16 +1538,12 @@ static inline int __init doc_probe(unsigned long physadr)
save_control = ReadDOC(virtadr, DOCControl); save_control = ReadDOC(virtadr, DOCControl);
/* Reset the DiskOnChip ASIC */ /* Reset the DiskOnChip ASIC */
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl);
virtadr, DOCControl); WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, virtadr, DOCControl);
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET,
virtadr, DOCControl);
/* Enable the DiskOnChip ASIC */ /* Enable the DiskOnChip ASIC */
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl);
virtadr, DOCControl); WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, virtadr, DOCControl);
WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL,
virtadr, DOCControl);
ChipID = ReadDOC(virtadr, ChipID); ChipID = ReadDOC(virtadr, ChipID);
@ -1556,15 +1563,13 @@ static inline int __init doc_probe(unsigned long physadr)
ReadDOC(virtadr, Mplus_Power); ReadDOC(virtadr, Mplus_Power);
/* Reset the Millennium Plus ASIC */ /* Reset the Millennium Plus ASIC */
tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;
DOC_MODE_BDECT;
WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(tmp, virtadr, Mplus_DOCControl);
WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
mdelay(1); mdelay(1);
/* Enable the Millennium Plus ASIC */ /* Enable the Millennium Plus ASIC */
tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | DOC_MODE_BDECT;
DOC_MODE_BDECT;
WriteDOC(tmp, virtadr, Mplus_DOCControl); WriteDOC(tmp, virtadr, Mplus_DOCControl);
WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm); WriteDOC(~tmp, virtadr, Mplus_CtrlConfirm);
mdelay(1); mdelay(1);
@ -1618,11 +1623,11 @@ static inline int __init doc_probe(unsigned long physadr)
if (ChipID == DOC_ChipID_DocMilPlus16) { if (ChipID == DOC_ChipID_DocMilPlus16) {
WriteDOC(~newval, virtadr, Mplus_AliasResolution); WriteDOC(~newval, virtadr, Mplus_AliasResolution);
oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution); oldval = ReadDOC(doc->virtadr, Mplus_AliasResolution);
WriteDOC(newval, virtadr, Mplus_AliasResolution); /* restore it */ WriteDOC(newval, virtadr, Mplus_AliasResolution); // restore it
} else { } else {
WriteDOC(~newval, virtadr, AliasResolution); WriteDOC(~newval, virtadr, AliasResolution);
oldval = ReadDOC(doc->virtadr, AliasResolution); oldval = ReadDOC(doc->virtadr, AliasResolution);
WriteDOC(newval, virtadr, AliasResolution); /* restore it */ WriteDOC(newval, virtadr, AliasResolution); // restore it
} }
newval = ~newval; newval = ~newval;
if (oldval == newval) { if (oldval == newval) {
@ -1634,16 +1639,13 @@ static inline int __init doc_probe(unsigned long physadr)
printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr); printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
len = sizeof(struct mtd_info) + len = sizeof(struct mtd_info) +
sizeof(struct nand_chip) + sizeof(struct nand_chip) + sizeof(struct doc_priv) + (2 * sizeof(struct nand_bbt_descr));
sizeof(struct doc_priv) + mtd = kzalloc(len, GFP_KERNEL);
(2 * sizeof(struct nand_bbt_descr));
mtd = kmalloc(len, GFP_KERNEL);
if (!mtd) { if (!mtd) {
printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len); printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
ret = -ENOMEM; ret = -ENOMEM;
goto fail; goto fail;
} }
memset(mtd, 0, len);
nand = (struct nand_chip *) (mtd + 1); nand = (struct nand_chip *) (mtd + 1);
doc = (struct doc_priv *) (nand + 1); doc = (struct doc_priv *) (nand + 1);
@ -1655,17 +1657,19 @@ static inline int __init doc_probe(unsigned long physadr)
nand->priv = doc; nand->priv = doc;
nand->select_chip = doc200x_select_chip; nand->select_chip = doc200x_select_chip;
nand->hwcontrol = doc200x_hwcontrol; nand->cmd_ctrl = doc200x_hwcontrol;
nand->dev_ready = doc200x_dev_ready; nand->dev_ready = doc200x_dev_ready;
nand->waitfunc = doc200x_wait; nand->waitfunc = doc200x_wait;
nand->block_bad = doc200x_block_bad; nand->block_bad = doc200x_block_bad;
nand->enable_hwecc = doc200x_enable_hwecc; nand->ecc.hwctl = doc200x_enable_hwecc;
nand->calculate_ecc = doc200x_calculate_ecc; nand->ecc.calculate = doc200x_calculate_ecc;
nand->correct_data = doc200x_correct_data; nand->ecc.correct = doc200x_correct_data;
nand->autooob = &doc200x_oobinfo; nand->ecc.layout = &doc200x_oobinfo;
nand->eccmode = NAND_ECC_HW6_512; nand->ecc.mode = NAND_ECC_HW_SYNDROME;
nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME; nand->ecc.size = 512;
nand->ecc.bytes = 6;
nand->options = NAND_USE_FLASH_BBT;
doc->physadr = physadr; doc->physadr = physadr;
doc->virtadr = virtadr; doc->virtadr = virtadr;

File diff suppressed because it is too large Load diff

View file

@ -6,7 +6,7 @@
* *
* Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de) * Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
* *
* $Id: nand_bbt.c,v 1.28 2004/11/13 10:19:09 gleixner Exp $ * $Id: nand_bbt.c,v 1.36 2005/11/07 11:14:30 gleixner Exp $
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
@ -48,7 +48,7 @@
* *
* Following assumptions are made: * Following assumptions are made:
* - bbts start at a page boundary, if autolocated on a block boundary * - bbts start at a page boundary, if autolocated on a block boundary
* - the space neccecary for a bbt in FLASH does not exceed a block boundary * - the space necessary for a bbt in FLASH does not exceed a block boundary
* *
*/ */
@ -63,6 +63,19 @@
#include <asm/errno.h> #include <asm/errno.h>
/* XXX U-BOOT XXX */
#if 0
#include <linux/slab.h>
#include <linux/types.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/compatmac.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/vmalloc.h>
#endif
/** /**
* check_pattern - [GENERIC] check if a pattern is in the buffer * check_pattern - [GENERIC] check if a pattern is in the buffer
* @buf: the buffer to search * @buf: the buffer to search
@ -78,7 +91,7 @@
*/ */
static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td) static int check_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
{ {
int i, end; int i, end = 0;
uint8_t *p = buf; uint8_t *p = buf;
end = paglen + td->offs; end = paglen + td->offs;
@ -96,9 +109,9 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
return -1; return -1;
} }
if (td->options & NAND_BBT_SCANEMPTY) {
p += td->len; p += td->len;
end += td->len; end += td->len;
if (td->options & NAND_BBT_SCANEMPTY) {
for (i = end; i < len; i++) { for (i = end; i < len; i++) {
if (*p++ != 0xff) if (*p++ != 0xff)
return -1; return -1;
@ -107,6 +120,29 @@ static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_des
return 0; return 0;
} }
/**
* check_short_pattern - [GENERIC] check if a pattern is in the buffer
* @buf: the buffer to search
* @td: search pattern descriptor
*
* Check for a pattern at the given place. Used to search bad block
* tables and good / bad block identifiers. Same as check_pattern, but
* no optional empty check
*
*/
static int check_short_pattern(uint8_t *buf, struct nand_bbt_descr *td)
{
int i;
uint8_t *p = buf;
/* Compare the pattern */
for (i = 0; i < td->len; i++) {
if (p[td->offs + i] != td->pattern[i])
return -1;
}
return 0;
}
/** /**
* read_bbt - [GENERIC] Read the bad block table starting from page * read_bbt - [GENERIC] Read the bad block table starting from page
* @mtd: MTD device structure * @mtd: MTD device structure
@ -134,7 +170,7 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
while (totlen) { while (totlen) {
len = min(totlen, (size_t) (1 << this->bbt_erase_shift)); len = min(totlen, (size_t) (1 << this->bbt_erase_shift));
res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob); res = mtd->read(mtd, from, len, &retlen, buf);
if (res < 0) { if (res < 0) {
if (retlen != len) { if (retlen != len) {
printk(KERN_INFO "nand_bbt: Error reading bad block table\n"); printk(KERN_INFO "nand_bbt: Error reading bad block table\n");
@ -150,11 +186,11 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
uint8_t tmp = (dat >> j) & msk; uint8_t tmp = (dat >> j) & msk;
if (tmp == msk) if (tmp == msk)
continue; continue;
if (reserved_block_code && if (reserved_block_code && (tmp == reserved_block_code)) {
(tmp == reserved_block_code)) {
printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n", printk(KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
((offs << 2) + (act >> 1)) << this->bbt_erase_shift); ((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06); this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
mtd->ecc_stats.bbtblocks++;
continue; continue;
} }
/* Leave it for now, if its matured we can move this /* Leave it for now, if its matured we can move this
@ -166,6 +202,7 @@ static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06); this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
else else
this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06); this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
mtd->ecc_stats.badblocks++;
} }
} }
totlen -= len; totlen -= len;
@ -209,6 +246,42 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des
return 0; return 0;
} }
/*
* Scan read raw data from flash
*/
static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs,
size_t len)
{
struct mtd_oob_ops ops;
ops.mode = MTD_OOB_RAW;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
ops.oobbuf = buf;
ops.datbuf = buf;
ops.len = len;
return mtd->read_oob(mtd, offs, &ops);
}
/*
* Scan write data with oob to flash
*/
static int scan_write_bbt(struct mtd_info *mtd, loff_t offs, size_t len,
uint8_t *buf, uint8_t *oob)
{
struct mtd_oob_ops ops;
ops.mode = MTD_OOB_PLACE;
ops.ooboffs = 0;
ops.ooblen = mtd->oobsize;
ops.datbuf = buf;
ops.oobbuf = oob;
ops.len = len;
return mtd->write_oob(mtd, offs, &ops);
}
/** /**
* read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page * read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
* @mtd: MTD device structure * @mtd: MTD device structure
@ -220,27 +293,83 @@ static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_des
* We assume that the bbt bits are in consecutive order. * We assume that the bbt bits are in consecutive order.
* *
*/ */
static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, static int read_abs_bbts(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *md) struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
/* Read the primary version, if available */ /* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) { if (td->options & NAND_BBT_VERSION) {
nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); scan_read_raw(mtd, buf, td->pages[0] << this->page_shift,
td->version[0] = buf[mtd->oobblock + td->veroffs]; mtd->writesize);
printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", td->pages[0], td->version[0]); td->version[0] = buf[mtd->writesize + td->veroffs];
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
td->pages[0], td->version[0]);
} }
/* Read the mirror version, if available */ /* Read the mirror version, if available */
if (md && (md->options & NAND_BBT_VERSION)) { if (md && (md->options & NAND_BBT_VERSION)) {
nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize); scan_read_raw(mtd, buf, md->pages[0] << this->page_shift,
md->version[0] = buf[mtd->oobblock + md->veroffs]; mtd->writesize);
printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]); md->version[0] = buf[mtd->writesize + md->veroffs];
printk(KERN_DEBUG "Bad block table at page %d, version 0x%02X\n",
md->pages[0], md->version[0]);
}
return 1;
} }
/*
* Scan a given block full
*/
static int scan_block_full(struct mtd_info *mtd, struct nand_bbt_descr *bd,
loff_t offs, uint8_t *buf, size_t readlen,
int scanlen, int len)
{
int ret, j;
ret = scan_read_raw(mtd, buf, offs, readlen);
if (ret)
return ret;
for (j = 0; j < len; j++, buf += scanlen) {
if (check_pattern(buf, scanlen, mtd->writesize, bd))
return 1; return 1;
} }
return 0;
}
/*
* Scan a given block partially
*/
static int scan_block_fast(struct mtd_info *mtd, struct nand_bbt_descr *bd,
loff_t offs, uint8_t *buf, int len)
{
struct mtd_oob_ops ops;
int j, ret;
ops.ooblen = mtd->oobsize;
ops.oobbuf = buf;
ops.ooboffs = 0;
ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
for (j = 0; j < len; j++) {
/*
* Read the full oob until read_oob is fixed to
* handle single byte reads for 16 bit
* buswidth
*/
ret = mtd->read_oob(mtd, offs, &ops);
if (ret)
return ret;
if (check_short_pattern(buf, bd))
return 1;
offs += mtd->writesize;
}
return 0;
}
/** /**
* create_bbt - [GENERIC] Create a bad block table by scanning the device * create_bbt - [GENERIC] Create a bad block table by scanning the device
@ -253,13 +382,16 @@ static int read_abs_bbts (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_de
* Create a bad block table by scanning the device * Create a bad block table by scanning the device
* for the given good/bad block identify pattern * for the given good/bad block identify pattern
*/ */
static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip) static int create_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *bd, int chip)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
int i, j, numblocks, len, scanlen; int i, numblocks, len, scanlen;
int startblock; int startblock;
loff_t from; loff_t from;
size_t readlen, ooblen; size_t readlen;
printk(KERN_INFO "Scanning device for bad blocks\n");
if (bd->options & NAND_BBT_SCANALLPAGES) if (bd->options & NAND_BBT_SCANALLPAGES)
len = 1 << (this->bbt_erase_shift - this->page_shift); len = 1 << (this->bbt_erase_shift - this->page_shift);
@ -269,13 +401,20 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
else else
len = 1; len = 1;
} }
scanlen = mtd->oobblock + mtd->oobsize;
readlen = len * mtd->oobblock; if (!(bd->options & NAND_BBT_SCANEMPTY)) {
ooblen = len * mtd->oobsize; /* We need only read few bytes from the OOB area */
scanlen = 0;
readlen = bd->len;
} else {
/* Full page content should be read */
scanlen = mtd->writesize + mtd->oobsize;
readlen = len * mtd->writesize;
}
if (chip == -1) { if (chip == -1) {
/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it /* Note that numblocks is 2 * (real numblocks) here, see i+=2
* makes shifting and masking less painful */ * below as it makes shifting and masking less painful */
numblocks = mtd->size >> (this->bbt_erase_shift - 1); numblocks = mtd->size >> (this->bbt_erase_shift - 1);
startblock = 0; startblock = 0;
from = 0; from = 0;
@ -283,7 +422,7 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
if (chip >= this->numchips) { if (chip >= this->numchips) {
printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n", printk(KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
chip + 1, this->numchips); chip + 1, this->numchips);
return; return -EINVAL;
} }
numblocks = this->chipsize >> (this->bbt_erase_shift - 1); numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
startblock = chip * numblocks; startblock = chip * numblocks;
@ -292,16 +431,28 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
} }
for (i = startblock; i < numblocks;) { for (i = startblock; i < numblocks;) {
nand_read_raw (mtd, buf, from, readlen, ooblen); int ret;
for (j = 0; j < len; j++) {
if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) { if (bd->options & NAND_BBT_SCANALLPAGES)
ret = scan_block_full(mtd, bd, from, buf, readlen,
scanlen, len);
else
ret = scan_block_fast(mtd, bd, from, buf, len);
if (ret < 0)
return ret;
if (ret) {
this->bbt[i >> 3] |= 0x03 << (i & 0x6); this->bbt[i >> 3] |= 0x03 << (i & 0x6);
break; printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
} i >> 1, (unsigned int)from);
mtd->ecc_stats.badblocks++;
} }
i += 2; i += 2;
from += (1 << this->bbt_erase_shift); from += (1 << this->bbt_erase_shift);
} }
return 0;
} }
/** /**
@ -316,7 +467,7 @@ static void create_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_desc
* block. * block.
* If the option NAND_BBT_PERCHIP is given, each chip is searched * If the option NAND_BBT_PERCHIP is given, each chip is searched
* for a bbt, which contains the bad block information of this chip. * for a bbt, which contains the bad block information of this chip.
* This is neccecary to provide support for certain DOC devices. * This is necessary to provide support for certain DOC devices.
* *
* The bbt ident pattern resides in the oob area of the first page * The bbt ident pattern resides in the oob area of the first page
* in a block. * in a block.
@ -326,8 +477,9 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
int i, chips; int i, chips;
int bits, startblock, block, dir; int bits, startblock, block, dir;
int scanlen = mtd->oobblock + mtd->oobsize; int scanlen = mtd->writesize + mtd->oobsize;
int bbtblocks; int bbtblocks;
int blocktopage = this->bbt_erase_shift - this->page_shift;
/* Search direction top -> down ? */ /* Search direction top -> down ? */
if (td->options & NAND_BBT_LASTBLOCK) { if (td->options & NAND_BBT_LASTBLOCK) {
@ -357,13 +509,16 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
td->pages[i] = -1; td->pages[i] = -1;
/* Scan the maximum number of blocks */ /* Scan the maximum number of blocks */
for (block = 0; block < td->maxblocks; block++) { for (block = 0; block < td->maxblocks; block++) {
int actblock = startblock + dir * block; int actblock = startblock + dir * block;
loff_t offs = actblock << this->bbt_erase_shift;
/* Read first page */ /* Read first page */
nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize); scan_read_raw(mtd, buf, offs, mtd->writesize);
if (!check_pattern(buf, scanlen, mtd->oobblock, td)) { if (!check_pattern(buf, scanlen, mtd->writesize, td)) {
td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift); td->pages[i] = actblock << blocktopage;
if (td->options & NAND_BBT_VERSION) { if (td->options & NAND_BBT_VERSION) {
td->version[i] = buf[mtd->oobblock + td->veroffs]; td->version[i] = buf[mtd->writesize + td->veroffs];
} }
break; break;
} }
@ -375,7 +530,8 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
if (td->pages[i] == -1) if (td->pages[i] == -1)
printk(KERN_WARNING "Bad block table not found for chip %d\n", i); printk(KERN_WARNING "Bad block table not found for chip %d\n", i);
else else
printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]); printk(KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i],
td->version[i]);
} }
return 0; return 0;
} }
@ -389,8 +545,7 @@ static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr
* *
* Search and read the bad block table(s) * Search and read the bad block table(s)
*/ */
static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf, static int search_read_bbts(struct mtd_info *mtd, uint8_t * buf, struct nand_bbt_descr *td, struct nand_bbt_descr *md)
struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{ {
/* Search the primary table */ /* Search the primary table */
search_bbt(mtd, buf, td); search_bbt(mtd, buf, td);
@ -403,7 +558,6 @@ static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
return 1; return 1;
} }
/** /**
* write_bbt - [GENERIC] (Re)write the bad block table * write_bbt - [GENERIC] (Re)write the bad block table
* *
@ -417,18 +571,24 @@ static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
* *
*/ */
static int write_bbt(struct mtd_info *mtd, uint8_t *buf, static int write_bbt(struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md, int chipsel) struct nand_bbt_descr *td, struct nand_bbt_descr *md,
int chipsel)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
struct nand_oobinfo oobinfo;
struct erase_info einfo; struct erase_info einfo;
int i, j, res, chip = 0; int i, j, res, chip = 0;
int bits, startblock, dir, page, offs, numblocks, sft, sftmsk; int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
int nrchips, bbtoffs, pageoffs; int nrchips, bbtoffs, pageoffs, ooboffs;
uint8_t msk[4]; uint8_t msk[4];
uint8_t rcode = td->reserved_block_code; uint8_t rcode = td->reserved_block_code;
size_t retlen, len = 0; size_t retlen, len = 0;
loff_t to; loff_t to;
struct mtd_oob_ops ops;
ops.ooblen = mtd->oobsize;
ops.ooboffs = 0;
ops.datbuf = NULL;
ops.mode = MTD_OOB_PLACE;
if (!rcode) if (!rcode)
rcode = 0xff; rcode = 0xff;
@ -472,12 +632,14 @@ static int write_bbt (struct mtd_info *mtd, uint8_t *buf,
for (i = 0; i < td->maxblocks; i++) { for (i = 0; i < td->maxblocks; i++) {
int block = startblock + dir * i; int block = startblock + dir * i;
/* Check, if the block is bad */ /* Check, if the block is bad */
switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) { switch ((this->bbt[block >> 2] >>
(2 * (block & 0x03))) & 0x03) {
case 0x01: case 0x01:
case 0x03: case 0x03:
continue; continue;
} }
page = block << (this->bbt_erase_shift - this->page_shift); page = block <<
(this->bbt_erase_shift - this->page_shift);
/* Check, if the block is used by the mirror table */ /* Check, if the block is used by the mirror table */
if (!md || md->pages[chip] != page) if (!md || md->pages[chip] != page)
goto write; goto write;
@ -488,11 +650,20 @@ write:
/* Set up shift count and masks for the flash table */ /* Set up shift count and masks for the flash table */
bits = td->options & NAND_BBT_NRBITS_MSK; bits = td->options & NAND_BBT_NRBITS_MSK;
msk[2] = ~rcode;
switch (bits) { switch (bits) {
case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break; case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01;
case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break; msk[3] = 0x01;
case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break; break;
case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break; case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01;
msk[3] = 0x03;
break;
case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C;
msk[3] = 0x0f;
break;
case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F;
msk[3] = 0xff;
break;
default: return -EINVAL; default: return -EINVAL;
} }
@ -500,48 +671,55 @@ write:
to = ((loff_t) page) << this->page_shift; to = ((loff_t) page) << this->page_shift;
memcpy (&oobinfo, this->autooob, sizeof(oobinfo));
oobinfo.useecc = MTD_NANDECC_PLACEONLY;
/* Must we save the block contents ? */ /* Must we save the block contents ? */
if (td->options & NAND_BBT_SAVECONTENT) { if (td->options & NAND_BBT_SAVECONTENT) {
/* Make it block aligned */ /* Make it block aligned */
to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1)); to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
len = 1 << this->bbt_erase_shift; len = 1 << this->bbt_erase_shift;
res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); res = mtd->read(mtd, to, len, &retlen, buf);
if (res < 0) { if (res < 0) {
if (retlen != len) { if (retlen != len) {
printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n"); printk(KERN_INFO "nand_bbt: Error "
"reading block for writing "
"the bad block table\n");
return res; return res;
} }
printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n"); printk(KERN_WARNING "nand_bbt: ECC error "
"while reading block for writing "
"bad block table\n");
} }
/* Read oob data */
ops.ooblen = (len >> this->page_shift) * mtd->oobsize;
ops.oobbuf = &buf[len];
res = mtd->read_oob(mtd, to + mtd->writesize, &ops);
if (res < 0 || ops.oobretlen != ops.ooblen)
goto outerr;
/* Calc the byte offset in the buffer */ /* Calc the byte offset in the buffer */
pageoffs = page - (int)(to >> this->page_shift); pageoffs = page - (int)(to >> this->page_shift);
offs = pageoffs << this->page_shift; offs = pageoffs << this->page_shift;
/* Preset the bbt area with 0xff */ /* Preset the bbt area with 0xff */
memset(&buf[offs], 0xff, (size_t) (numblocks >> sft)); memset(&buf[offs], 0xff, (size_t) (numblocks >> sft));
/* Preset the bbt's oob area with 0xff */ ooboffs = len + (pageoffs * mtd->oobsize);
memset (&buf[len + pageoffs * mtd->oobsize], 0xff,
((len >> this->page_shift) - pageoffs) * mtd->oobsize);
if (td->options & NAND_BBT_VERSION) {
buf[len + (pageoffs * mtd->oobsize) + td->veroffs] = td->version[chip];
}
} else { } else {
/* Calc length */ /* Calc length */
len = (size_t) (numblocks >> sft); len = (size_t) (numblocks >> sft);
/* Make it page aligned ! */ /* Make it page aligned ! */
len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1); len = (len + (mtd->writesize - 1)) &
~(mtd->writesize - 1);
/* Preset the buffer with 0xff */ /* Preset the buffer with 0xff */
memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize); memset(buf, 0xff, len +
(len >> this->page_shift)* mtd->oobsize);
offs = 0; offs = 0;
ooboffs = len;
/* Pattern is located in oob area of first page */ /* Pattern is located in oob area of first page */
memcpy (&buf[len + td->offs], td->pattern, td->len); memcpy(&buf[ooboffs + td->offs], td->pattern, td->len);
if (td->options & NAND_BBT_VERSION) {
buf[len + td->veroffs] = td->version[chip];
}
} }
if (td->options & NAND_BBT_VERSION)
buf[ooboffs + td->veroffs] = td->version[chip];
/* walk through the memory table */ /* walk through the memory table */
for (i = 0; i < numblocks;) { for (i = 0; i < numblocks;) {
uint8_t dat; uint8_t dat;
@ -549,7 +727,8 @@ write:
for (j = 0; j < 4; j++, i++) { for (j = 0; j < 4; j++, i++) {
int sftcnt = (i << (3 - sft)) & sftmsk; int sftcnt = (i << (3 - sft)) & sftmsk;
/* Do not store the reserved bbt blocks ! */ /* Do not store the reserved bbt blocks ! */
buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt); buf[offs + (i >> sft)] &=
~(msk[dat & 0x03] << sftcnt);
dat >>= 2; dat >>= 2;
} }
} }
@ -559,23 +738,25 @@ write:
einfo.addr = (unsigned long)to; einfo.addr = (unsigned long)to;
einfo.len = 1 << this->bbt_erase_shift; einfo.len = 1 << this->bbt_erase_shift;
res = nand_erase_nand(mtd, &einfo, 1); res = nand_erase_nand(mtd, &einfo, 1);
if (res < 0) { if (res < 0)
printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res); goto outerr;
return res;
}
res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo); res = scan_write_bbt(mtd, to, len, buf, &buf[len]);
if (res < 0) { if (res < 0)
printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res); goto outerr;
return res;
} printk(KERN_DEBUG "Bad block table written to 0x%08x, version "
printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n", "0x%02X\n", (unsigned int)to, td->version[chip]);
(unsigned int) to, td->version[chip]);
/* Mark it as used */ /* Mark it as used */
td->pages[chip] = page; td->pages[chip] = page;
} }
return 0; return 0;
outerr:
printk(KERN_WARNING
"nand_bbt: Error while writing bad block table %d\n", res);
return res;
} }
/** /**
@ -586,26 +767,24 @@ write:
* The function creates a memory based bbt by scanning the device * The function creates a memory based bbt by scanning the device
* for manufacturer / software marked good / bad blocks * for manufacturer / software marked good / bad blocks
*/ */
static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd) static inline int nand_memory_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
{ {
struct nand_chip *this = mtd->priv; struct nand_chip *this = mtd->priv;
/* Ensure that we only scan for the pattern and nothing else */ bd->options &= ~NAND_BBT_SCANEMPTY;
bd->options = 0; return create_bbt(mtd, this->buffers->databuf, bd, -1);
create_bbt (mtd, this->data_buf, bd, -1);
return 0;
} }
/** /**
* check_create - [GENERIC] create and write bbt(s) if neccecary * check_create - [GENERIC] create and write bbt(s) if necessary
* @mtd: MTD device structure * @mtd: MTD device structure
* @buf: temporary buffer * @buf: temporary buffer
* @bd: descriptor for the good/bad block search pattern * @bd: descriptor for the good/bad block search pattern
* *
* The function checks the results of the previous call to read_bbt * The function checks the results of the previous call to read_bbt
* and creates / updates the bbt(s) if neccecary * and creates / updates the bbt(s) if necessary
* Creation is neccecary if no bbt was found for the chip/device * Creation is necessary if no bbt was found for the chip/device
* Update is neccecary if one of the tables is missing or the * Update is necessary if one of the tables is missing or the
* version nr. of one table is less than the other * version nr. of one table is less than the other
*/ */
static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd) static int check_create(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd)
@ -739,7 +918,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
for (i = 0; i < chips; i++) { for (i = 0; i < chips; i++) {
if ((td->options & NAND_BBT_ABSPAGE) || if ((td->options & NAND_BBT_ABSPAGE) ||
!(td->options & NAND_BBT_WRITE)) { !(td->options & NAND_BBT_WRITE)) {
if (td->pages[i] == -1) continue; if (td->pages[i] == -1)
continue;
block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift); block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
block <<= 1; block <<= 1;
oldval = this->bbt[(block >> 3)]; oldval = this->bbt[(block >> 3)];
@ -759,7 +939,8 @@ static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
oldval = this->bbt[(block >> 3)]; oldval = this->bbt[(block >> 3)];
newval = oldval | (0x2 << (block & 0x06)); newval = oldval | (0x2 << (block & 0x06));
this->bbt[(block >> 3)] = newval; this->bbt[(block >> 3)] = newval;
if (oldval != newval) update = 1; if (oldval != newval)
update = 1;
block += 2; block += 2;
} }
/* If we want reserved blocks to be recorded to flash, and some /* If we want reserved blocks to be recorded to flash, and some
@ -793,25 +974,29 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
struct nand_bbt_descr *md = this->bbt_md; struct nand_bbt_descr *md = this->bbt_md;
len = mtd->size >> (this->bbt_erase_shift + 2); len = mtd->size >> (this->bbt_erase_shift + 2);
/* Allocate memory (2bit per block) */ /* Allocate memory (2bit per block) and clear the memory bad block table */
this->bbt = kmalloc (len, GFP_KERNEL); this->bbt = kzalloc(len, GFP_KERNEL);
if (!this->bbt) { if (!this->bbt) {
printk(KERN_ERR "nand_scan_bbt: Out of memory\n"); printk(KERN_ERR "nand_scan_bbt: Out of memory\n");
return -ENOMEM; return -ENOMEM;
} }
/* Clear the memory bad block table */
memset (this->bbt, 0x00, len);
/* If no primary table decriptor is given, scan the device /* If no primary table decriptor is given, scan the device
* to build a memory based bad block table * to build a memory based bad block table
*/ */
if (!td) if (!td) {
return nand_memory_bbt(mtd, bd); if ((res = nand_memory_bbt(mtd, bd))) {
printk(KERN_ERR "nand_bbt: Can't scan flash and build the RAM-based BBT\n");
kfree(this->bbt);
this->bbt = NULL;
}
return res;
}
/* Allocate a temporary buffer for one eraseblock incl. oob */ /* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift); len = (1 << this->bbt_erase_shift);
len += (len >> this->page_shift) * mtd->oobsize; len += (len >> this->page_shift) * mtd->oobsize;
buf = kmalloc (len, GFP_KERNEL); buf = vmalloc(len);
if (!buf) { if (!buf) {
printk(KERN_ERR "nand_bbt: Out of memory\n"); printk(KERN_ERR "nand_bbt: Out of memory\n");
kfree(this->bbt); kfree(this->bbt);
@ -835,11 +1020,10 @@ int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
if (md) if (md)
mark_bbt_region(mtd, md); mark_bbt_region(mtd, md);
kfree (buf); vfree(buf);
return res; return res;
} }
/** /**
* nand_update_bbt - [NAND Interface] update bad block table(s) * nand_update_bbt - [NAND Interface] update bad block table(s)
* @mtd: MTD device structure * @mtd: MTD device structure
@ -901,14 +1085,11 @@ out:
} }
/* Define some generic bad / good block scan pattern which are used /* Define some generic bad / good block scan pattern which are used
* while scanning a device for factory marked good / bad blocks * while scanning a device for factory marked good / bad blocks. */
*
* The memory based patterns just
*/
static uint8_t scan_ff_pattern[] = { 0xff, 0xff }; static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static struct nand_bbt_descr smallpage_memorybased = { static struct nand_bbt_descr smallpage_memorybased = {
.options = 0, .options = NAND_BBT_SCAN2NDPAGE,
.offs = 5, .offs = 5,
.len = 1, .len = 1,
.pattern = scan_ff_pattern .pattern = scan_ff_pattern
@ -922,14 +1103,14 @@ static struct nand_bbt_descr largepage_memorybased = {
}; };
static struct nand_bbt_descr smallpage_flashbased = { static struct nand_bbt_descr smallpage_flashbased = {
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, .options = NAND_BBT_SCAN2NDPAGE,
.offs = 5, .offs = 5,
.len = 1, .len = 1,
.pattern = scan_ff_pattern .pattern = scan_ff_pattern
}; };
static struct nand_bbt_descr largepage_flashbased = { static struct nand_bbt_descr largepage_flashbased = {
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES, .options = NAND_BBT_SCAN2NDPAGE,
.offs = 0, .offs = 0,
.len = 2, .len = 2,
.pattern = scan_ff_pattern .pattern = scan_ff_pattern
@ -998,7 +1179,6 @@ int nand_default_bbt (struct mtd_info *mtd)
return nand_scan_bbt(mtd, &agand_flashbased); return nand_scan_bbt(mtd, &agand_flashbased);
} }
/* Is a flash based bad block table requested ? */ /* Is a flash based bad block table requested ? */
if (this->options & NAND_USE_FLASH_BBT) { if (this->options & NAND_USE_FLASH_BBT) {
/* Use the default pattern descriptors */ /* Use the default pattern descriptors */
@ -1007,14 +1187,13 @@ int nand_default_bbt (struct mtd_info *mtd)
this->bbt_md = &bbt_mirror_descr; this->bbt_md = &bbt_mirror_descr;
} }
if (!this->badblock_pattern) { if (!this->badblock_pattern) {
this->badblock_pattern = (mtd->oobblock > 512) ? this->badblock_pattern = (mtd->writesize > 512) ? &largepage_flashbased : &smallpage_flashbased;
&largepage_flashbased : &smallpage_flashbased;
} }
} else { } else {
this->bbt_td = NULL; this->bbt_td = NULL;
this->bbt_md = NULL; this->bbt_md = NULL;
if (!this->badblock_pattern) { if (!this->badblock_pattern) {
this->badblock_pattern = (mtd->oobblock > 512) ? this->badblock_pattern = (mtd->writesize > 512) ?
&largepage_memorybased : &smallpage_memorybased; &largepage_memorybased : &smallpage_memorybased;
} }
} }
@ -1042,11 +1221,20 @@ int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
"(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1); "(block %d) 0x%02x\n", (unsigned int)offs, res, block >> 1);
switch ((int)res) { switch ((int)res) {
case 0x00: return 0; case 0x00:
case 0x01: return 1; return 0;
case 0x02: return allowbbt ? 0 : 1; case 0x01:
return 1;
case 0x02:
return allowbbt ? 0 : 1;
} }
return 1; return 1;
} }
/* XXX U-BOOT XXX */
#if 0
EXPORT_SYMBOL(nand_scan_bbt);
EXPORT_SYMBOL(nand_default_bbt);
#endif
#endif #endif

View file

@ -7,7 +7,9 @@
* Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com) * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
* Toshiba America Electronics Components, Inc. * Toshiba America Electronics Components, Inc.
* *
* $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $ * Copyright (C) 2006 Thomas Gleixner <tglx@linutronix.de>
*
* $Id: nand_ecc.c,v 1.15 2005/11/07 11:14:30 gleixner Exp $
* *
* This file is free software; you can redistribute it and/or modify it * 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 * under the terms of the GNU General Public License as published by the
@ -39,6 +41,14 @@
#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) #if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
/* XXX U-BOOT XXX */
#if 0
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/nand_ecc.h>
#endif
#include<linux/mtd/mtd.h> #include<linux/mtd/mtd.h>
/* /*
@ -128,6 +138,10 @@ int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
return 0; return 0;
} }
/* XXX U-BOOT XXX */
#if 0
EXPORT_SYMBOL(nand_calculate_ecc);
#endif
#endif /* CONFIG_NAND_SPL */ #endif /* CONFIG_NAND_SPL */
static inline int countbits(uint32_t byte) static inline int countbits(uint32_t byte)
@ -197,4 +211,9 @@ int nand_correct_data(struct mtd_info *mtd, u_char *dat,
return -1; return -1;
} }
/* XXX U-BOOT XXX */
#if 0
EXPORT_SYMBOL(nand_correct_data);
#endif
#endif #endif

View file

@ -3,7 +3,7 @@
* *
* Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de)
* *
* $Id: nand_ids.c,v 1.10 2004/05/26 13:40:12 gleixner Exp $ * $Id: nand_ids.c,v 1.16 2005/11/07 11:14:31 gleixner Exp $
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
@ -16,7 +16,6 @@
#if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY) #if defined(CONFIG_CMD_NAND) && !defined(CFG_NAND_LEGACY)
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
/* /*
* Chip ID list * Chip ID list
* *
@ -29,6 +28,8 @@
* 512 512 Byte page size * 512 512 Byte page size
*/ */
struct nand_flash_dev nand_flash_ids[] = { struct nand_flash_dev nand_flash_ids[] = {
#ifdef CONFIG_MTD_NAND_MUSEUM_IDS
{"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0}, {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
{"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0}, {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
{"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0}, {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
@ -44,6 +45,7 @@ struct nand_flash_dev nand_flash_ids[] = {
{"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0}, {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
{"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16}, {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
{"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16}, {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
#endif
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0}, {"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0}, {"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
@ -61,52 +63,72 @@ struct nand_flash_dev nand_flash_ids[] = {
{"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16}, {"NAND 64MiB 3,3V 16-bit", 0x56, 512, 64, 0x4000, NAND_BUSWIDTH_16},
{"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0}, {"NAND 128MiB 1,8V 8-bit", 0x78, 512, 128, 0x4000, 0},
{"NAND 128MiB 1,8V 8-bit", 0x39, 512, 128, 0x4000, 0},
{"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0}, {"NAND 128MiB 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},
{"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 128MiB 1,8V 16-bit", 0x49, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16}, {"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 128MiB 3,3V 16-bit", 0x59, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0}, {"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
/* These are the new chips with large page size. The pagesize /*
* and the erasesize is determined from the extended id bytes * These are the new chips with large page size. The pagesize and the
* erasesize is determined from the extended id bytes
*/ */
#define LP_OPTIONS (NAND_SAMSUNG_LP_OPTIONS | NAND_NO_READRDY | NAND_NO_AUTOINCR)
#define LP_OPTIONS16 (LP_OPTIONS | NAND_BUSWIDTH_16)
/*512 Megabit */
{"NAND 64MiB 1,8V 8-bit", 0xA2, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 3,3V 8-bit", 0xF2, 0, 64, 0, LP_OPTIONS},
{"NAND 64MiB 1,8V 16-bit", 0xB2, 0, 64, 0, LP_OPTIONS16},
{"NAND 64MiB 3,3V 16-bit", 0xC2, 0, 64, 0, LP_OPTIONS16},
/* 1 Gigabit */ /* 1 Gigabit */
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS},
{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS},
{"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16},
{"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16},
/* 2 Gigabit */ /* 2 Gigabit */
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, LP_OPTIONS},
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, LP_OPTIONS},
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, LP_OPTIONS16},
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, LP_OPTIONS16},
/* 4 Gigabit */ /* 4 Gigabit */
{"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, LP_OPTIONS},
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, LP_OPTIONS},
{"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, LP_OPTIONS16},
{"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, LP_OPTIONS16},
/* 8 Gigabit */ /* 8 Gigabit */
{"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, LP_OPTIONS},
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, LP_OPTIONS},
{"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, LP_OPTIONS16},
{"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, LP_OPTIONS16},
/* 16 Gigabit */ /* 16 Gigabit */
{"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, LP_OPTIONS},
{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR}, {"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, LP_OPTIONS},
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, LP_OPTIONS16},
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR}, {"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, LP_OPTIONS16},
/* Renesas AND 1 Gigabit. Those chips do not support extended id and have a strange page/block layout ! /*
* The chosen minimum erasesize is 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page planes * Renesas AND 1 Gigabit. Those chips do not support extended id and
* 1 block = 2 pages, but due to plane arrangement the blocks 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 * have a strange page/block layout ! The chosen minimum erasesize is
* Anyway JFFS2 would increase the eraseblock size so we chose a combined one which can be erased in one go * 4 * 2 * 2048 = 16384 Byte, as those chips have an array of 4 page
* There are more speed improvements for reads and writes possible, but not implemented now * planes 1 block = 2 pages, but due to plane arrangement the blocks
* 0-3 consists of page 0 + 4,1 + 5, 2 + 6, 3 + 7 Anyway JFFS2 would
* increase the eraseblock size so we chose a combined one which can be
* erased in one go There are more speed improvements for reads and
* writes possible, but not implemented now
*/ */
{"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000, NAND_IS_AND | NAND_NO_AUTOINCR | NAND_4PAGE_ARRAY}, {"AND 128MiB 3,3V 8-bit", 0x01, 2048, 128, 0x4000,
NAND_IS_AND | NAND_NO_AUTOINCR |NAND_NO_READRDY | NAND_4PAGE_ARRAY |
BBT_AUTO_REFRESH
},
{NULL,} {NULL,}
}; };
@ -121,6 +143,7 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_NATIONAL, "National"}, {NAND_MFR_NATIONAL, "National"},
{NAND_MFR_RENESAS, "Renesas"}, {NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"}, {NAND_MFR_STMICRO, "ST Micro"},
{NAND_MFR_HYNIX, "Hynix"},
{NAND_MFR_MICRON, "Micron"}, {NAND_MFR_MICRON, "Micron"},
{0x0, "Unknown"} {0x0, "Unknown"}
}; };

View file

@ -39,6 +39,9 @@
#include <malloc.h> #include <malloc.h>
#include <div64.h> #include <div64.h>
#include <asm/errno.h>
#include <linux/mtd/mtd.h>
#include <nand.h> #include <nand.h>
#include <jffs2/jffs2.h> #include <jffs2/jffs2.h>
@ -69,71 +72,33 @@ static int nand_block_bad_scrub(struct mtd_info *mtd, loff_t ofs, int getchip)
int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts) int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
{ {
struct jffs2_unknown_node cleanmarker; struct jffs2_unknown_node cleanmarker;
int clmpos = 0;
int clmlen = 8;
erase_info_t erase; erase_info_t erase;
ulong erase_length; ulong erase_length;
int isNAND;
int bbtest = 1; int bbtest = 1;
int result; int result;
int percent_complete = -1; int percent_complete = -1;
int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL; int (*nand_block_bad_old)(struct mtd_info *, loff_t, int) = NULL;
const char *mtd_device = meminfo->name; const char *mtd_device = meminfo->name;
struct mtd_oob_ops oob_opts;
struct nand_chip *chip = meminfo->priv;
uint8_t buf[64];
memset(buf, 0, sizeof(buf));
memset(&erase, 0, sizeof(erase)); memset(&erase, 0, sizeof(erase));
memset(&oob_opts, 0, sizeof(oob_opts));
erase.mtd = meminfo; erase.mtd = meminfo;
erase.len = meminfo->erasesize; erase.len = meminfo->erasesize;
erase.addr = opts->offset; erase.addr = opts->offset;
erase_length = opts->length; erase_length = opts->length;
isNAND = meminfo->type == MTD_NANDFLASH ? 1 : 0;
if (opts->jffs2) {
cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); cleanmarker.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK);
cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); cleanmarker.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER);
if (isNAND) {
struct nand_oobinfo *oobinfo = &meminfo->oobinfo;
/* check for autoplacement */
if (oobinfo->useecc == MTD_NANDECC_AUTOPLACE) {
/* get the position of the free bytes */
if (!oobinfo->oobfree[0][1]) {
printf(" Eeep. Autoplacement selected "
"and no empty space in oob\n");
return -1;
}
clmpos = oobinfo->oobfree[0][0];
clmlen = oobinfo->oobfree[0][1];
if (clmlen > 8)
clmlen = 8;
} else {
/* legacy mode */
switch (meminfo->oobsize) {
case 8:
clmpos = 6;
clmlen = 2;
break;
case 16:
clmpos = 8;
clmlen = 8;
break;
case 64:
clmpos = 16;
clmlen = 8;
break;
}
}
cleanmarker.totlen = cpu_to_je32(8); cleanmarker.totlen = cpu_to_je32(8);
} else {
cleanmarker.totlen =
cpu_to_je32(sizeof(struct jffs2_unknown_node));
}
cleanmarker.hdr_crc = cpu_to_je32( cleanmarker.hdr_crc = cpu_to_je32(
crc32_no_comp(0, (unsigned char *) &cleanmarker, crc32_no_comp(0, (unsigned char *) &cleanmarker,
sizeof(struct jffs2_unknown_node) - 4)); sizeof(struct jffs2_unknown_node) - 4));
}
/* scrub option allows to erase badblock. To prevent internal /* scrub option allows to erase badblock. To prevent internal
* check from erase() method, set block check method to dummy * check from erase() method, set block check method to dummy
@ -194,25 +159,21 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
/* format for JFFS2 ? */ /* format for JFFS2 ? */
if (opts->jffs2) { if (opts->jffs2) {
/* write cleanmarker */ chip->ops.len = chip->ops.ooblen = 64;
if (isNAND) { chip->ops.datbuf = NULL;
size_t written; chip->ops.oobbuf = buf;
chip->ops.ooboffs = chip->badblockpos & ~0x01;
result = meminfo->write_oob(meminfo, result = meminfo->write_oob(meminfo,
erase.addr + clmpos, erase.addr + meminfo->oobsize,
clmlen, &chip->ops);
&written,
(unsigned char *)
&cleanmarker);
if (result != 0) { if (result != 0) {
printf("\n%s: MTD writeoob failure: %d\n", printf("\n%s: MTD writeoob failure: %d\n",
mtd_device, result); mtd_device, result);
continue; continue;
} }
} else { else
printf("\n%s: this erase routine only supports" printf("%s: MTD writeoob at 0x%08x\n",mtd_device, erase.addr + meminfo->oobsize );
" NAND devices!\n",
mtd_device);
}
} }
if (!opts->quiet) { if (!opts->quiet) {
@ -253,6 +214,9 @@ int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts)
return 0; return 0;
} }
/* XXX U-BOOT XXX */
#if 0
#define MAX_PAGE_SIZE 2048 #define MAX_PAGE_SIZE 2048
#define MAX_OOB_SIZE 64 #define MAX_OOB_SIZE 64
@ -263,26 +227,189 @@ static unsigned char data_buf[MAX_PAGE_SIZE];
static unsigned char oob_buf[MAX_OOB_SIZE]; static unsigned char oob_buf[MAX_OOB_SIZE];
/* OOB layouts to pass into the kernel as default */ /* OOB layouts to pass into the kernel as default */
static struct nand_oobinfo none_oobinfo = { static struct nand_ecclayout none_ecclayout = {
.useecc = MTD_NANDECC_OFF, .useecc = MTD_NANDECC_OFF,
}; };
static struct nand_oobinfo jffs2_oobinfo = { static struct nand_ecclayout jffs2_ecclayout = {
.useecc = MTD_NANDECC_PLACE, .useecc = MTD_NANDECC_PLACE,
.eccbytes = 6, .eccbytes = 6,
.eccpos = { 0, 1, 2, 3, 6, 7 } .eccpos = { 0, 1, 2, 3, 6, 7 }
}; };
static struct nand_oobinfo yaffs_oobinfo = { static struct nand_ecclayout yaffs_ecclayout = {
.useecc = MTD_NANDECC_PLACE, .useecc = MTD_NANDECC_PLACE,
.eccbytes = 6, .eccbytes = 6,
.eccpos = { 8, 9, 10, 13, 14, 15} .eccpos = { 8, 9, 10, 13, 14, 15}
}; };
static struct nand_oobinfo autoplace_oobinfo = { static struct nand_ecclayout autoplace_ecclayout = {
.useecc = MTD_NANDECC_AUTOPLACE .useecc = MTD_NANDECC_AUTOPLACE
}; };
#endif
/**
* nand_fill_oob - [Internal] Transfer client buffer to oob
* @chip: nand chip structure
* @oob: oob data buffer
* @ops: oob ops structure
*
* Copied from nand_base.c
*/
static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob,
struct mtd_oob_ops *ops)
{
size_t len = ops->ooblen;
switch(ops->mode) {
case MTD_OOB_PLACE:
case MTD_OOB_RAW:
memcpy(chip->oob_poi + ops->ooboffs, oob, len);
return oob + len;
case MTD_OOB_AUTO: {
struct nand_oobfree *free = chip->ecc.layout->oobfree;
uint32_t boffs = 0, woffs = ops->ooboffs;
size_t bytes = 0;
for(; free->length && len; free++, len -= bytes) {
/* Write request not from offset 0 ? */
if (unlikely(woffs)) {
if (woffs >= free->length) {
woffs -= free->length;
continue;
}
boffs = free->offset + woffs;
bytes = min_t(size_t, len,
(free->length - woffs));
woffs = 0;
} else {
bytes = min_t(size_t, len, free->length);
boffs = free->offset;
}
memcpy(chip->oob_poi + boffs, oob, bytes);
oob += bytes;
}
return oob;
}
default:
BUG();
}
return NULL;
}
#define NOTALIGNED(x) (x & (chip->subpagesize - 1)) != 0
/* copied from nand_base.c: nand_do_write_ops()
* Only very small changes
*/
int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops)
{
int chipnr, realpage, page, blockmask, column;
struct nand_chip *chip = mtd->priv;
uint32_t writelen = ops->len;
uint8_t *oob = ops->oobbuf;
uint8_t *buf = ops->datbuf;
int ret, subpage;
ops->retlen = 0;
if (!writelen)
return 0;
printk("nand_write_opts: to: 0x%08x, ops->len: 0x%08x\n", to, ops->len);
/* reject writes, which are not page aligned */
if (NOTALIGNED(to) || NOTALIGNED(ops->len)) {
printk(KERN_NOTICE "nand_write: "
"Attempt to write not page aligned data\n");
return -EINVAL;
}
column = to & (mtd->writesize - 1);
subpage = column || (writelen & (mtd->writesize - 1));
if (subpage && oob) {
printk(KERN_NOTICE "nand_write: "
"Attempt to write oob to subpage\n");
return -EINVAL;
}
chipnr = (int)(to >> chip->chip_shift);
chip->select_chip(mtd, chipnr);
/* XXX U-BOOT XXX */
#if 0
/* Check, if it is write protected */
if (nand_check_wp(mtd))
return -EIO;
#endif
realpage = (int)(to >> chip->page_shift);
page = realpage & chip->pagemask;
blockmask = (1 << (chip->phys_erase_shift - chip->page_shift)) - 1;
/* Invalidate the page cache, when we write to the cached page */
if (to <= (chip->pagebuf << chip->page_shift) &&
(chip->pagebuf << chip->page_shift) < (to + ops->len))
chip->pagebuf = -1;
/* If we're not given explicit OOB data, let it be 0xFF */
if (likely(!oob)) {
printf("!oob, writing %d bytes with 0xff to chip->oob_poi (0x%08x)\n", mtd->oobsize, chip->oob_poi);
memset(chip->oob_poi, 0xff, mtd->oobsize);
}
while(1) {
int bytes = mtd->writesize;
int cached = writelen > bytes && page != blockmask;
uint8_t *wbuf = buf;
/* Partial page write ? */
if (unlikely(column || writelen < (mtd->writesize - 1))) {
cached = 0;
bytes = min_t(int, bytes - column, (int) writelen);
chip->pagebuf = -1;
memset(chip->buffers->databuf, 0xff, mtd->writesize);
memcpy(&chip->buffers->databuf[column], buf, bytes);
wbuf = chip->buffers->databuf;
}
if (unlikely(oob))
oob = nand_fill_oob(chip, oob, ops);
ret = chip->write_page(mtd, chip, wbuf, page, cached,
(ops->mode == MTD_OOB_RAW));
if (ret)
break;
writelen -= bytes;
if (!writelen)
break;
column = 0;
buf += bytes;
realpage++;
page = realpage & chip->pagemask;
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
chip->select_chip(mtd, -1);
chip->select_chip(mtd, chipnr);
}
}
ops->retlen = ops->len - writelen;
if (unlikely(oob))
ops->oobretlen = ops->ooblen;
return ret;
}
/* XXX U-BOOT XXX */
#if 0
/** /**
* nand_write_opts: - write image to NAND flash with support for various options * nand_write_opts: - write image to NAND flash with support for various options
* *
@ -301,9 +428,9 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
int blockstart = -1; int blockstart = -1;
loff_t offs; loff_t offs;
int readlen; int readlen;
int oobinfochanged = 0; int ecclayoutchanged = 0;
int percent_complete = -1; int percent_complete = -1;
struct nand_oobinfo old_oobinfo; struct nand_ecclayout old_ecclayout;
ulong mtdoffset = opts->offset; ulong mtdoffset = opts->offset;
ulong erasesize_blockalign; ulong erasesize_blockalign;
u_char *buffer = opts->buffer; u_char *buffer = opts->buffer;
@ -324,35 +451,35 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
} }
/* make sure device page sizes are valid */ /* make sure device page sizes are valid */
if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) if (!(meminfo->oobsize == 16 && meminfo->writesize == 512)
&& !(meminfo->oobsize == 8 && meminfo->oobblock == 256) && !(meminfo->oobsize == 8 && meminfo->writesize == 256)
&& !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) {
printf("Unknown flash (not normal NAND)\n"); printf("Unknown flash (not normal NAND)\n");
return -1; return -1;
} }
/* read the current oob info */ /* read the current oob info */
memcpy(&old_oobinfo, &meminfo->oobinfo, sizeof(old_oobinfo)); memcpy(&old_ecclayout, &meminfo->ecclayout, sizeof(old_ecclayout));
/* write without ecc? */ /* write without ecc? */
if (opts->noecc) { if (opts->noecc) {
memcpy(&meminfo->oobinfo, &none_oobinfo, memcpy(&meminfo->ecclayout, &none_ecclayout,
sizeof(meminfo->oobinfo)); sizeof(meminfo->ecclayout));
oobinfochanged = 1; ecclayoutchanged = 1;
} }
/* autoplace ECC? */ /* autoplace ECC? */
if (opts->autoplace && (old_oobinfo.useecc != MTD_NANDECC_AUTOPLACE)) { if (opts->autoplace && (old_ecclayout.useecc != MTD_NANDECC_AUTOPLACE)) {
memcpy(&meminfo->oobinfo, &autoplace_oobinfo, memcpy(&meminfo->ecclayout, &autoplace_ecclayout,
sizeof(meminfo->oobinfo)); sizeof(meminfo->ecclayout));
oobinfochanged = 1; ecclayoutchanged = 1;
} }
/* force OOB layout for jffs2 or yaffs? */ /* force OOB layout for jffs2 or yaffs? */
if (opts->forcejffs2 || opts->forceyaffs) { if (opts->forcejffs2 || opts->forceyaffs) {
struct nand_oobinfo *oobsel = struct nand_ecclayout *oobsel =
opts->forcejffs2 ? &jffs2_oobinfo : &yaffs_oobinfo; opts->forcejffs2 ? &jffs2_ecclayout : &yaffs_ecclayout;
if (meminfo->oobsize == 8) { if (meminfo->oobsize == 8) {
if (opts->forceyaffs) { if (opts->forceyaffs) {
@ -361,15 +488,15 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
goto restoreoob; goto restoreoob;
} }
/* Adjust number of ecc bytes */ /* Adjust number of ecc bytes */
jffs2_oobinfo.eccbytes = 3; jffs2_ecclayout.eccbytes = 3;
} }
memcpy(&meminfo->oobinfo, oobsel, sizeof(meminfo->oobinfo)); memcpy(&meminfo->ecclayout, oobsel, sizeof(meminfo->ecclayout));
} }
/* get image length */ /* get image length */
imglen = opts->length; imglen = opts->length;
pagelen = meminfo->oobblock pagelen = meminfo->writesize
+ ((opts->writeoob != 0) ? meminfo->oobsize : 0); + ((opts->writeoob != 0) ? meminfo->oobsize : 0);
/* check, if file is pagealigned */ /* check, if file is pagealigned */
@ -379,11 +506,11 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
} }
/* check, if length fits into device */ /* check, if length fits into device */
if (((imglen / pagelen) * meminfo->oobblock) if (((imglen / pagelen) * meminfo->writesize)
> (meminfo->size - opts->offset)) { > (meminfo->size - opts->offset)) {
printf("Image %d bytes, NAND page %d bytes, " printf("Image %d bytes, NAND page %d bytes, "
"OOB area %u bytes, device size %u bytes\n", "OOB area %u bytes, device size %u bytes\n",
imglen, pagelen, meminfo->oobblock, meminfo->size); imglen, pagelen, meminfo->writesize, meminfo->size);
printf("Input block does not fit into device\n"); printf("Input block does not fit into device\n");
goto restoreoob; goto restoreoob;
} }
@ -437,11 +564,11 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
} while (offs < blockstart + erasesize_blockalign); } while (offs < blockstart + erasesize_blockalign);
} }
readlen = meminfo->oobblock; readlen = meminfo->writesize;
if (opts->pad && (imglen < readlen)) { if (opts->pad && (imglen < readlen)) {
readlen = imglen; readlen = imglen;
memset(data_buf + readlen, 0xff, memset(data_buf + readlen, 0xff,
meminfo->oobblock - readlen); meminfo->writesize - readlen);
} }
/* read page data from input memory buffer */ /* read page data from input memory buffer */
@ -474,7 +601,7 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
/* write out the page data */ /* write out the page data */
result = meminfo->write(meminfo, result = meminfo->write(meminfo,
mtdoffset, mtdoffset,
meminfo->oobblock, meminfo->writesize,
&written, &written,
(unsigned char *) &data_buf); (unsigned char *) &data_buf);
@ -505,16 +632,16 @@ int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts)
} }
} }
mtdoffset += meminfo->oobblock; mtdoffset += meminfo->writesize;
} }
if (!opts->quiet) if (!opts->quiet)
printf("\n"); printf("\n");
restoreoob: restoreoob:
if (oobinfochanged) { if (ecclayoutchanged) {
memcpy(&meminfo->oobinfo, &old_oobinfo, memcpy(&meminfo->ecclayout, &old_ecclayout,
sizeof(meminfo->oobinfo)); sizeof(meminfo->ecclayout));
} }
if (imglen > 0) { if (imglen > 0) {
@ -548,22 +675,22 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)
int result; int result;
/* make sure device page sizes are valid */ /* make sure device page sizes are valid */
if (!(meminfo->oobsize == 16 && meminfo->oobblock == 512) if (!(meminfo->oobsize == 16 && meminfo->writesize == 512)
&& !(meminfo->oobsize == 8 && meminfo->oobblock == 256) && !(meminfo->oobsize == 8 && meminfo->writesize == 256)
&& !(meminfo->oobsize == 64 && meminfo->oobblock == 2048)) { && !(meminfo->oobsize == 64 && meminfo->writesize == 2048)) {
printf("Unknown flash (not normal NAND)\n"); printf("Unknown flash (not normal NAND)\n");
return -1; return -1;
} }
pagelen = meminfo->oobblock pagelen = meminfo->writesize
+ ((opts->readoob != 0) ? meminfo->oobsize : 0); + ((opts->readoob != 0) ? meminfo->oobsize : 0);
/* check, if length is not larger than device */ /* check, if length is not larger than device */
if (((imglen / pagelen) * meminfo->oobblock) if (((imglen / pagelen) * meminfo->writesize)
> (meminfo->size - opts->offset)) { > (meminfo->size - opts->offset)) {
printf("Image %d bytes, NAND page %d bytes, " printf("Image %d bytes, NAND page %d bytes, "
"OOB area %u bytes, device size %u bytes\n", "OOB area %u bytes, device size %u bytes\n",
imglen, pagelen, meminfo->oobblock, meminfo->size); imglen, pagelen, meminfo->writesize, meminfo->size);
printf("Input block is larger than device\n"); printf("Input block is larger than device\n");
return -1; return -1;
} }
@ -621,7 +748,7 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)
/* read page data to memory buffer */ /* read page data to memory buffer */
result = meminfo->read(meminfo, result = meminfo->read(meminfo,
mtdoffset, mtdoffset,
meminfo->oobblock, meminfo->writesize,
&readlen, &readlen,
(unsigned char *) &data_buf); (unsigned char *) &data_buf);
@ -685,7 +812,7 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)
} }
} }
mtdoffset += meminfo->oobblock; mtdoffset += meminfo->writesize;
} }
if (!opts->quiet) if (!opts->quiet)
@ -699,7 +826,10 @@ int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts)
/* return happy */ /* return happy */
return 0; return 0;
} }
#endif
/* XXX U-BOOT XXX */
#if 0
/****************************************************************************** /******************************************************************************
* Support for locking / unlocking operations of some NAND devices * Support for locking / unlocking operations of some NAND devices
*****************************************************************************/ *****************************************************************************/
@ -784,7 +914,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset)
this->select_chip(meminfo, chipnr); this->select_chip(meminfo, chipnr);
if ((offset & (meminfo->oobblock - 1)) != 0) { if ((offset & (meminfo->writesize - 1)) != 0) {
printf ("nand_get_lock_status: " printf ("nand_get_lock_status: "
"Start address must be beginning of " "Start address must be beginning of "
"nand page!\n"); "nand page!\n");
@ -813,7 +943,7 @@ int nand_get_lock_status(nand_info_t *meminfo, ulong offset)
* @param meminfo nand mtd instance * @param meminfo nand mtd instance
* @param start start byte address * @param start start byte address
* @param length number of bytes to unlock (must be a multiple of * @param length number of bytes to unlock (must be a multiple of
* page size nand->oobblock) * page size nand->writesize)
* *
* @return 0 on success, -1 in case of error * @return 0 on success, -1 in case of error
*/ */
@ -839,14 +969,14 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)
goto out; goto out;
} }
if ((start & (meminfo->oobblock - 1)) != 0) { if ((start & (meminfo->writesize - 1)) != 0) {
printf ("nand_unlock: Start address must be beginning of " printf ("nand_unlock: Start address must be beginning of "
"nand page!\n"); "nand page!\n");
ret = -1; ret = -1;
goto out; goto out;
} }
if (length == 0 || (length & (meminfo->oobblock - 1)) != 0) { if (length == 0 || (length & (meminfo->writesize - 1)) != 0) {
printf ("nand_unlock: Length must be a multiple of nand page " printf ("nand_unlock: Length must be a multiple of nand page "
"size!\n"); "size!\n");
ret = -1; ret = -1;
@ -875,5 +1005,6 @@ int nand_unlock(nand_info_t *meminfo, ulong start, ulong length)
this->select_chip(meminfo, -1); this->select_chip(meminfo, -1);
return ret; return ret;
} }
#endif
#endif #endif

View file

@ -119,11 +119,13 @@ typedef volatile unsigned char vu_char;
#define debugX(level,fmt,args...) #define debugX(level,fmt,args...)
#endif /* DEBUG */ #endif /* DEBUG */
#ifndef BUG
#define BUG() do { \ #define BUG() do { \
printf("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \ printf("BUG: failure at %s:%d/%s()!\n", __FILE__, __LINE__, __FUNCTION__); \
panic("BUG!"); \ panic("BUG!"); \
} while (0) } while (0)
#define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0) #define BUG_ON(condition) do { if (unlikely((condition)!=0)) BUG(); } while(0)
#endif /* BUG */
typedef void (interrupt_handler_t)(void *); typedef void (interrupt_handler_t)(void *);

45
include/linux/err.h Normal file
View file

@ -0,0 +1,45 @@
#ifndef _LINUX_ERR_H
#define _LINUX_ERR_H
/* XXX U-BOOT XXX */
#if 0
#include <linux/compiler.h>
#else
#include <linux/mtd/compat.h>
#endif
#include <asm/errno.h>
/*
* Kernel pointers have redundant information, so we can use a
* scheme where we can return either an error code or a dentry
* pointer with the same return value.
*
* This should be a per-architecture thing, to allow different
* error and pointer decisions.
*/
#define MAX_ERRNO 4095
#ifndef __ASSEMBLY__
#define IS_ERR_VALUE(x) unlikely((x) >= (unsigned long)-MAX_ERRNO)
static inline void *ERR_PTR(long error)
{
return (void *) error;
}
static inline long PTR_ERR(const void *ptr)
{
return (long) ptr;
}
static inline long IS_ERR(const void *ptr)
{
return IS_ERR_VALUE((unsigned long)ptr);
}
#endif
#endif /* _LINUX_ERR_H */

View file

@ -0,0 +1,81 @@
/*
* $Id: blktrans.h,v 1.6 2005/11/07 11:14:54 gleixner Exp $
*
* (C) 2003 David Woodhouse <dwmw2@infradead.org>
*
* Interface to Linux block layer for MTD 'translation layers'.
*
*/
#ifndef __MTD_TRANS_H__
#define __MTD_TRANS_H__
/* XXX U-BOOT XXX */
#if 0
#include <linux/mutex.h>
#else
#include <linux/list.h>
#endif
struct hd_geometry;
struct mtd_info;
struct mtd_blktrans_ops;
struct file;
struct inode;
struct mtd_blktrans_dev {
struct mtd_blktrans_ops *tr;
struct list_head list;
struct mtd_info *mtd;
/* XXX U-BOOT XXX */
#if 0
struct mutex lock;
#endif
int devnum;
unsigned long size;
int readonly;
void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */
};
struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */
struct mtd_blktrans_ops {
char *name;
int major;
int part_bits;
int blksize;
int blkshift;
/* Access functions */
int (*readsect)(struct mtd_blktrans_dev *dev,
unsigned long block, char *buffer);
int (*writesect)(struct mtd_blktrans_dev *dev,
unsigned long block, char *buffer);
/* Block layer ioctls */
int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo);
int (*flush)(struct mtd_blktrans_dev *dev);
/* Called with mtd_table_mutex held; no race with add/remove */
int (*open)(struct mtd_blktrans_dev *dev);
int (*release)(struct mtd_blktrans_dev *dev);
/* Called on {de,}registration and on subsequent addition/removal
of devices, with mtd_table_mutex held. */
void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd);
void (*remove_dev)(struct mtd_blktrans_dev *dev);
struct list_head devs;
struct list_head list;
struct module *owner;
struct mtd_blkcore_priv *blkcore_priv;
};
extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr);
extern int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr);
extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev);
#endif /* __MTD_TRANS_H__ */

View file

@ -18,7 +18,12 @@
#define KERN_DEBUG #define KERN_DEBUG
#define kmalloc(size, flags) malloc(size) #define kmalloc(size, flags) malloc(size)
#define kzalloc(size, flags) calloc(size, 1)
#define vmalloc(size) malloc(size)
#define kfree(ptr) free(ptr) #define kfree(ptr) free(ptr)
#define vfree(ptr) free(ptr)
#define KERNEL_VERSION(a,b,c) (((a) << 16) + ((b) << 8) + (c))
/* /*
* ..and if you can't take the strict * ..and if you can't take the strict

View file

@ -1,15 +1,23 @@
/*
/* Linux driver for Disk-On-Chip 2000 */ * Linux driver for Disk-On-Chip devices
/* (c) 1999 Machine Vision Holdings, Inc. */ *
/* Author: David Woodhouse <dwmw2@mvhi.com> */ * Copyright (C) 1999 Machine Vision Holdings, Inc.
/* $Id: doc2000.h,v 1.15 2001/09/19 00:22:15 dwmw2 Exp $ */ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org>
* Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com>
* Copyright (C) 2002-2003 SnapGear Inc
*
* $Id: doc2000.h,v 1.25 2005/11/07 11:14:54 gleixner Exp $
*
* Released under GPL
*/
#ifndef __MTD_DOC2000_H__ #ifndef __MTD_DOC2000_H__
#define __MTD_DOC2000_H__ #define __MTD_DOC2000_H__
struct DiskOnChip; #include <linux/mtd/mtd.h>
#if 0
#include <linux/mtd/nftl.h> #include <linux/mutex.h>
#endif
#define DoC_Sig1 0 #define DoC_Sig1 0
#define DoC_Sig2 1 #define DoC_Sig2 1
@ -40,10 +48,58 @@ struct DiskOnChip;
#define DoC_Mil_CDSN_IO 0x0800 #define DoC_Mil_CDSN_IO 0x0800
#define DoC_2k_CDSN_IO 0x1800 #define DoC_2k_CDSN_IO 0x1800
#define ReadDOC_(adr, reg) ((volatile unsigned char)(*(volatile __u8 *)(((unsigned long)adr)+((reg))))) #define DoC_Mplus_NOP 0x1002
#define WriteDOC_(d, adr, reg) do{ *(volatile __u8 *)(((unsigned long)adr)+((reg))) = (__u8)d; eieio();} while(0) #define DoC_Mplus_AliasResolution 0x1004
#define DoC_Mplus_DOCControl 0x1006
#define DoC_Mplus_AccessStatus 0x1008
#define DoC_Mplus_DeviceSelect 0x1008
#define DoC_Mplus_Configuration 0x100a
#define DoC_Mplus_OutputControl 0x100c
#define DoC_Mplus_FlashControl 0x1020
#define DoC_Mplus_FlashSelect 0x1022
#define DoC_Mplus_FlashCmd 0x1024
#define DoC_Mplus_FlashAddress 0x1026
#define DoC_Mplus_FlashData0 0x1028
#define DoC_Mplus_FlashData1 0x1029
#define DoC_Mplus_ReadPipeInit 0x102a
#define DoC_Mplus_LastDataRead 0x102c
#define DoC_Mplus_LastDataRead1 0x102d
#define DoC_Mplus_WritePipeTerm 0x102e
#define DoC_Mplus_ECCSyndrome0 0x1040
#define DoC_Mplus_ECCSyndrome1 0x1041
#define DoC_Mplus_ECCSyndrome2 0x1042
#define DoC_Mplus_ECCSyndrome3 0x1043
#define DoC_Mplus_ECCSyndrome4 0x1044
#define DoC_Mplus_ECCSyndrome5 0x1045
#define DoC_Mplus_ECCConf 0x1046
#define DoC_Mplus_Toggle 0x1046
#define DoC_Mplus_DownloadStatus 0x1074
#define DoC_Mplus_CtrlConfirm 0x1076
#define DoC_Mplus_Power 0x1fff
/* How to access the device?
* On ARM, it'll be mmap'd directly with 32-bit wide accesses.
* On PPC, it's mmap'd and 16-bit wide.
* Others use readb/writeb
*/
#if defined(__arm__)
#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2))))
#define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0)
#define DOC_IOREMAP_LEN 0x8000
#elif defined(__ppc__)
#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1))))
#define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0)
#define DOC_IOREMAP_LEN 0x4000 #define DOC_IOREMAP_LEN 0x4000
#else
#define ReadDOC_(adr, reg) readb((void __iomem *)(adr) + (reg))
#define WriteDOC_(d, adr, reg) writeb(d, (void __iomem *)(adr) + (reg))
#define DOC_IOREMAP_LEN 0x2000
#endif
#if defined(__i386__) || defined(__x86_64__)
#define USE_MEMCPY
#endif
/* These are provided to directly use the DoC_xxx defines */ /* These are provided to directly use the DoC_xxx defines */
#define ReadDOC(adr, reg) ReadDOC_(adr,DoC_##reg) #define ReadDOC(adr, reg) ReadDOC_(adr,DoC_##reg)
@ -54,14 +110,21 @@ struct DiskOnChip;
#define DOC_MODE_RESERVED1 2 #define DOC_MODE_RESERVED1 2
#define DOC_MODE_RESERVED2 3 #define DOC_MODE_RESERVED2 3
#define DOC_MODE_MDWREN 4
#define DOC_MODE_CLR_ERR 0x80 #define DOC_MODE_CLR_ERR 0x80
#define DOC_MODE_RST_LAT 0x10
#define DOC_MODE_BDECT 0x08
#define DOC_MODE_MDWREN 0x04
#define DOC_ChipID_UNKNOWN 0x00
#define DOC_ChipID_Doc2k 0x20 #define DOC_ChipID_Doc2k 0x20
#define DOC_ChipID_Doc2kTSOP 0x21 /* internal number for MTD */
#define DOC_ChipID_DocMil 0x30 #define DOC_ChipID_DocMil 0x30
#define DOC_ChipID_DocMilPlus32 0x40
#define DOC_ChipID_DocMilPlus16 0x41
#define CDSN_CTRL_FR_B 0x80 #define CDSN_CTRL_FR_B 0x80
#define CDSN_CTRL_FR_B0 0x40
#define CDSN_CTRL_FR_B1 0x80
#define CDSN_CTRL_ECC_IO 0x20 #define CDSN_CTRL_ECC_IO 0x20
#define CDSN_CTRL_FLASH_IO 0x10 #define CDSN_CTRL_FLASH_IO 0x10
#define CDSN_CTRL_WP 0x08 #define CDSN_CTRL_WP 0x08
@ -77,20 +140,14 @@ struct DiskOnChip;
#define DOC_ECC_RESV 0x02 #define DOC_ECC_RESV 0x02
#define DOC_ECC_IGNORE 0x01 #define DOC_ECC_IGNORE 0x01
#define DOC_FLASH_CE 0x80
#define DOC_FLASH_WP 0x40
#define DOC_FLASH_BANK 0x02
/* We have to also set the reserved bit 1 for enable */ /* We have to also set the reserved bit 1 for enable */
#define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV) #define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV)
#define DOC_ECC_DIS (DOC_ECC_RESV) #define DOC_ECC_DIS (DOC_ECC_RESV)
#define MAX_FLOORS 4
#define MAX_CHIPS 4
#define MAX_FLOORS_MIL 4
#define MAX_CHIPS_MIL 1
#define ADDR_COLUMN 1
#define ADDR_PAGE 2
#define ADDR_COLUMN_PAGE 3
struct Nand { struct Nand {
char floor, chip; char floor, chip;
unsigned long curadr; unsigned long curadr;
@ -98,20 +155,32 @@ struct Nand {
/* Also some erase/write/pipeline info when we get that far */ /* Also some erase/write/pipeline info when we get that far */
}; };
#define MAX_FLOORS 4
#define MAX_CHIPS 4
#define MAX_FLOORS_MIL 1
#define MAX_CHIPS_MIL 1
#define MAX_FLOORS_MPLUS 2
#define MAX_CHIPS_MPLUS 1
#define ADDR_COLUMN 1
#define ADDR_PAGE 2
#define ADDR_COLUMN_PAGE 3
struct DiskOnChip { struct DiskOnChip {
unsigned long physadr; unsigned long physadr;
unsigned long virtadr; void __iomem *virtadr;
unsigned long totlen; unsigned long totlen;
char* name; unsigned char ChipID; /* Type of DiskOnChip */
char ChipID; /* Type of DiskOnChip */
int ioreg; int ioreg;
char* chips_name;
unsigned long mfr; /* Flash IDs - only one type of flash per device */ unsigned long mfr; /* Flash IDs - only one type of flash per device */
unsigned long id; unsigned long id;
int chipshift; int chipshift;
char page256; char page256;
char pageadrlen; char pageadrlen;
char interleave; /* Internal interleaving - Millennium Plus style */
unsigned long erasesize; unsigned long erasesize;
int curfloor; int curfloor;
@ -119,98 +188,22 @@ struct DiskOnChip {
int numchips; int numchips;
struct Nand *chips; struct Nand *chips;
struct mtd_info *nextdoc;
int nftl_found; /* XXX U-BOOT XXX */
struct NFTLrecord nftl; #if 0
struct mutex lock;
#endif
}; };
#define SECTORSIZE 512
/* Return codes from doc_write(), doc_read(), and doc_erase().
*/
#define DOC_OK 0
#define DOC_EIO 1
#define DOC_EINVAL 2
#define DOC_EECC 3
#define DOC_ETIMEOUT 4
/*
* Function Prototypes
*/
int doc_decode_ecc(unsigned char sector[512], unsigned char ecc1[6]); int doc_decode_ecc(unsigned char sector[512], unsigned char ecc1[6]);
int doc_rw(struct DiskOnChip* this, int cmd, loff_t from, size_t len, /* XXX U-BOOT XXX */
size_t *retlen, u_char *buf); #if 1
int doc_read_ecc(struct DiskOnChip* this, loff_t from, size_t len,
size_t *retlen, u_char *buf, u_char *eccbuf);
int doc_write_ecc(struct DiskOnChip* this, loff_t to, size_t len,
size_t *retlen, const u_char *buf, u_char *eccbuf);
int doc_read_oob(struct DiskOnChip* this, loff_t ofs, size_t len,
size_t *retlen, u_char *buf);
int doc_write_oob(struct DiskOnChip* this, loff_t ofs, size_t len,
size_t *retlen, const u_char *buf);
int doc_erase (struct DiskOnChip* this, loff_t ofs, size_t len);
void doc_probe(unsigned long physadr);
void doc_print(struct DiskOnChip*);
/*
* Standard NAND flash commands
*/
#define NAND_CMD_READ0 0
#define NAND_CMD_READ1 1
#define NAND_CMD_PAGEPROG 0x10
#define NAND_CMD_READOOB 0x50
#define NAND_CMD_ERASE1 0x60
#define NAND_CMD_STATUS 0x70
#define NAND_CMD_SEQIN 0x80
#define NAND_CMD_READID 0x90
#define NAND_CMD_ERASE2 0xd0
#define NAND_CMD_RESET 0xff
/* /*
* NAND Flash Manufacturer ID Codes * NAND Flash Manufacturer ID Codes
*/ */
#define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_TOSHIBA 0x98
#define NAND_MFR_SAMSUNG 0xec #define NAND_MFR_SAMSUNG 0xec
#endif
/*
* NAND Flash Device ID Structure
*
* Structure overview:
*
* name - Complete name of device
*
* manufacture_id - manufacturer ID code of device.
*
* model_id - model ID code of device.
*
* chipshift - total number of address bits for the device which
* is used to calculate address offsets and the total
* number of bytes the device is capable of.
*
* page256 - denotes if flash device has 256 byte pages or not.
*
* pageadrlen - number of bytes minus one needed to hold the
* complete address into the flash array. Keep in
* mind that when a read or write is done to a
* specific address, the address is input serially
* 8 bits at a time. This structure member is used
* by the read/write routines as a loop index for
* shifting the address out 8 bits at a time.
*
* erasesize - size of an erase block in the flash device.
*/
struct nand_flash_dev {
char * name;
int manufacture_id;
int model_id;
int chipshift;
char page256;
char pageadrlen;
unsigned long erasesize;
int bus16;
};
#endif /* __MTD_DOC2000_H__ */ #endif /* __MTD_DOC2000_H__ */

View file

@ -0,0 +1,91 @@
/*
* $Id: inftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $
*
* Parts of INFTL headers shared with userspace
*
*/
#ifndef __MTD_INFTL_USER_H__
#define __MTD_INFTL_USER_H__
#define OSAK_VERSION 0x5120
#define PERCENTUSED 98
#define SECTORSIZE 512
/* Block Control Information */
struct inftl_bci {
uint8_t ECCsig[6];
uint8_t Status;
uint8_t Status1;
} __attribute__((packed));
struct inftl_unithead1 {
uint16_t virtualUnitNo;
uint16_t prevUnitNo;
uint8_t ANAC;
uint8_t NACs;
uint8_t parityPerField;
uint8_t discarded;
} __attribute__((packed));
struct inftl_unithead2 {
uint8_t parityPerField;
uint8_t ANAC;
uint16_t prevUnitNo;
uint16_t virtualUnitNo;
uint8_t NACs;
uint8_t discarded;
} __attribute__((packed));
struct inftl_unittail {
uint8_t Reserved[4];
uint16_t EraseMark;
uint16_t EraseMark1;
} __attribute__((packed));
union inftl_uci {
struct inftl_unithead1 a;
struct inftl_unithead2 b;
struct inftl_unittail c;
};
struct inftl_oob {
struct inftl_bci b;
union inftl_uci u;
};
/* INFTL Media Header */
struct INFTLPartition {
__u32 virtualUnits;
__u32 firstUnit;
__u32 lastUnit;
__u32 flags;
__u32 spareUnits;
__u32 Reserved0;
__u32 Reserved1;
} __attribute__((packed));
struct INFTLMediaHeader {
char bootRecordID[8];
__u32 NoOfBootImageBlocks;
__u32 NoOfBinaryPartitions;
__u32 NoOfBDTLPartitions;
__u32 BlockMultiplierBits;
__u32 FormatFlags;
__u32 OsakVersion;
__u32 PercentUsed;
struct INFTLPartition Partitions[4];
} __attribute__((packed));
/* Partition flag types */
#define INFTL_BINARY 0x20000000
#define INFTL_BDTL 0x40000000
#define INFTL_LAST 0x80000000
#endif /* __MTD_INFTL_USER_H__ */

View file

@ -0,0 +1,35 @@
/*
* $Id: jffs2-user.h,v 1.1 2004/05/05 11:57:54 dwmw2 Exp $
*
* JFFS2 definitions for use in user space only
*/
#ifndef __JFFS2_USER_H__
#define __JFFS2_USER_H__
/* This file is blessed for inclusion by userspace */
#include <linux/jffs2.h>
#include <endian.h>
#include <byteswap.h>
#undef cpu_to_je16
#undef cpu_to_je32
#undef cpu_to_jemode
#undef je16_to_cpu
#undef je32_to_cpu
#undef jemode_to_cpu
extern int target_endian;
#define t16(x) ({ uint16_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); })
#define t32(x) ({ uint32_t __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); })
#define cpu_to_je16(x) ((jint16_t){t16(x)})
#define cpu_to_je32(x) ((jint32_t){t32(x)})
#define cpu_to_jemode(x) ((jmode_t){t32(x)})
#define je16_to_cpu(x) (t16((x).v16))
#define je32_to_cpu(x) (t32((x).v32))
#define jemode_to_cpu(x) (t32((x).m))
#endif /* __JFFS2_USER_H__ */

View file

@ -1,5 +1,5 @@
/* /*
* $Id: mtd-abi.h,v 1.7 2004/11/23 15:37:32 gleixner Exp $ * $Id: mtd-abi.h,v 1.13 2005/11/07 11:14:56 gleixner Exp $
* *
* Portions of MTD ABI definition which are shared by kernel and user space * Portions of MTD ABI definition which are shared by kernel and user space
*/ */
@ -7,6 +7,10 @@
#ifndef __MTD_ABI_H__ #ifndef __MTD_ABI_H__
#define __MTD_ABI_H__ #define __MTD_ABI_H__
#if 1
#include <linux/mtd/compat.h>
#endif
struct erase_info_user { struct erase_info_user {
uint32_t start; uint32_t start;
uint32_t length; uint32_t length;
@ -15,7 +19,7 @@ struct erase_info_user {
struct mtd_oob_buf { struct mtd_oob_buf {
uint32_t start; uint32_t start;
uint32_t length; uint32_t length;
unsigned char *ptr; unsigned char __user *ptr;
}; };
#define MTD_ABSENT 0 #define MTD_ABSENT 0
@ -23,47 +27,41 @@ struct mtd_oob_buf {
#define MTD_ROM 2 #define MTD_ROM 2
#define MTD_NORFLASH 3 #define MTD_NORFLASH 3
#define MTD_NANDFLASH 4 #define MTD_NANDFLASH 4
#define MTD_PEROM 5 #define MTD_DATAFLASH 6
#define MTD_OTHER 14 #define MTD_UBIVOLUME 7
#define MTD_UNKNOWN 15
#define MTD_CLEAR_BITS 1 /* Bits can be cleared (flash) */ #define MTD_WRITEABLE 0x400 /* Device is writeable */
#define MTD_SET_BITS 2 /* Bits can be set */ #define MTD_BIT_WRITEABLE 0x800 /* Single bits can be flipped */
#define MTD_ERASEABLE 4 /* Has an erase function */ #define MTD_NO_ERASE 0x1000 /* No erase necessary */
#define MTD_WRITEB_WRITEABLE 8 /* Direct IO is possible */ #define MTD_STUPID_LOCK 0x2000 /* Always locked after reset */
#define MTD_VOLATILE 16 /* Set for RAMs */
#define MTD_XIP 32 /* eXecute-In-Place possible */
#define MTD_OOB 64 /* Out-of-band data (NAND flash) */
#define MTD_ECC 128 /* Device capable of automatic ECC */
#define MTD_NO_VIRTBLOCKS 256 /* Virtual blocks not allowed */
/* Some common devices / combinations of capabilities */ // Some common devices / combinations of capabilities
#define MTD_CAP_ROM 0 #define MTD_CAP_ROM 0
#define MTD_CAP_RAM (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEB_WRITEABLE) #define MTD_CAP_RAM (MTD_WRITEABLE | MTD_BIT_WRITEABLE | MTD_NO_ERASE)
#define MTD_CAP_NORFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE) #define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE)
#define MTD_CAP_NANDFLASH (MTD_CLEAR_BITS|MTD_ERASEABLE|MTD_OOB) #define MTD_CAP_NANDFLASH (MTD_WRITEABLE)
#define MTD_WRITEABLE (MTD_CLEAR_BITS|MTD_SET_BITS)
/* Types of automatic ECC/Checksum available */
#define MTD_ECC_NONE 0 /* No automatic ECC available */
#define MTD_ECC_RS_DiskOnChip 1 /* Automatic ECC on DiskOnChip */
#define MTD_ECC_SW 2 /* SW ECC for Toshiba & Samsung devices */
/* ECC byte placement */ /* ECC byte placement */
#define MTD_NANDECC_OFF 0 /* Switch off ECC (Not recommended) */ #define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended)
#define MTD_NANDECC_PLACE 1 /* Use the given placement in the structure (YAFFS1 legacy mode) */ #define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode)
#define MTD_NANDECC_AUTOPLACE 2 /* Use the default placement scheme */ #define MTD_NANDECC_AUTOPLACE 2 // Use the default placement scheme
#define MTD_NANDECC_PLACEONLY 3 /* Use the given placement in the structure (Do not store ecc result on read) */ #define MTD_NANDECC_PLACEONLY 3 // Use the given placement in the structure (Do not store ecc result on read)
#define MTD_NANDECC_AUTOPL_USR 4 /* Use the given autoplacement scheme rather than using the default */ #define MTD_NANDECC_AUTOPL_USR 4 // Use the given autoplacement scheme rather than using the default
/* OTP mode selection */
#define MTD_OTP_OFF 0
#define MTD_OTP_FACTORY 1
#define MTD_OTP_USER 2
struct mtd_info_user { struct mtd_info_user {
uint8_t type; uint8_t type;
uint32_t flags; uint32_t flags;
uint32_t size; /* Total size of the MTD */ uint32_t size; // Total size of the MTD
uint32_t erasesize; uint32_t erasesize;
uint32_t oobblock; /* Size of OOB blocks (e.g. 512) */ uint32_t writesize;
uint32_t oobsize; /* Amount of OOB data per block (e.g. 16) */ uint32_t oobsize; // Amount of OOB data per block (e.g. 16)
/* The below two fields are obsolete and broken, do not use them
* (TODO: remove at some point) */
uint32_t ecctype; uint32_t ecctype;
uint32_t eccsize; uint32_t eccsize;
}; };
@ -76,6 +74,12 @@ struct region_info_user {
uint32_t regionindex; uint32_t regionindex;
}; };
struct otp_info {
uint32_t start;
uint32_t length;
uint32_t locked;
};
#define MEMGETINFO _IOR('M', 1, struct mtd_info_user) #define MEMGETINFO _IOR('M', 1, struct mtd_info_user)
#define MEMERASE _IOW('M', 2, struct erase_info_user) #define MEMERASE _IOW('M', 2, struct erase_info_user)
#define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf) #define MEMWRITEOOB _IOWR('M', 3, struct mtd_oob_buf)
@ -88,7 +92,18 @@ struct region_info_user {
#define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo) #define MEMGETOOBSEL _IOR('M', 10, struct nand_oobinfo)
#define MEMGETBADBLOCK _IOW('M', 11, loff_t) #define MEMGETBADBLOCK _IOW('M', 11, loff_t)
#define MEMSETBADBLOCK _IOW('M', 12, loff_t) #define MEMSETBADBLOCK _IOW('M', 12, loff_t)
#define OTPSELECT _IOR('M', 13, int)
#define OTPGETREGIONCOUNT _IOW('M', 14, int)
#define OTPGETREGIONINFO _IOW('M', 15, struct otp_info)
#define OTPLOCK _IOR('M', 16, struct otp_info)
#define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout)
#define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats)
#define MTDFILEMODE _IO('M', 19)
/*
* Obsolete legacy interface. Keep it in order not to break userspace
* interfaces
*/
struct nand_oobinfo { struct nand_oobinfo {
uint32_t useecc; uint32_t useecc;
uint32_t eccbytes; uint32_t eccbytes;
@ -96,4 +111,46 @@ struct nand_oobinfo {
uint32_t eccpos[48]; uint32_t eccpos[48];
}; };
struct nand_oobfree {
uint32_t offset;
uint32_t length;
};
#define MTD_MAX_OOBFREE_ENTRIES 8
/*
* ECC layout control structure. Exported to userspace for
* diagnosis and to allow creation of raw images
*/
struct nand_ecclayout {
uint32_t eccbytes;
uint32_t eccpos[64];
uint32_t oobavail;
struct nand_oobfree oobfree[MTD_MAX_OOBFREE_ENTRIES];
};
/**
* struct mtd_ecc_stats - error correction stats
*
* @corrected: number of corrected bits
* @failed: number of uncorrectable errors
* @badblocks: number of bad blocks in this partition
* @bbtblocks: number of blocks reserved for bad block tables
*/
struct mtd_ecc_stats {
uint32_t corrected;
uint32_t failed;
uint32_t badblocks;
uint32_t bbtblocks;
};
/*
* Read/write file modes for access to MTD
*/
enum mtd_file_modes {
MTD_MODE_NORMAL = MTD_OTP_OFF,
MTD_MODE_OTP_FACTORY = MTD_OTP_FACTORY,
MTD_MODE_OTP_USER = MTD_OTP_USER,
MTD_MODE_RAW,
};
#endif /* __MTD_ABI_H__ */ #endif /* __MTD_ABI_H__ */

View file

@ -1,5 +1,5 @@
/* /*
* $Id: mtd.h,v 1.56 2004/08/09 18:46:04 dmarlin Exp $ * $Id: mtd.h,v 1.61 2005/11/07 11:14:54 gleixner Exp $
* *
* Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al. * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al.
* *
@ -8,10 +8,13 @@
#ifndef __MTD_MTD_H__ #ifndef __MTD_MTD_H__
#define __MTD_MTD_H__ #define __MTD_MTD_H__
#include <linux/types.h> #include <linux/types.h>
#include <linux/mtd/mtd-abi.h> #include <linux/mtd/mtd-abi.h>
#define MAX_MTD_DEVICES 16 #define MTD_CHAR_MAJOR 90
#define MTD_BLOCK_MAJOR 31
#define MAX_MTD_DEVICES 32
#define MTD_ERASE_PENDING 0x01 #define MTD_ERASE_PENDING 0x01
#define MTD_ERASING 0x02 #define MTD_ERASING 0x02
@ -41,32 +44,83 @@ struct mtd_erase_region_info {
u_int32_t offset; /* At which this region starts, from the beginning of the MTD */ u_int32_t offset; /* At which this region starts, from the beginning of the MTD */
u_int32_t erasesize; /* For this region */ u_int32_t erasesize; /* For this region */
u_int32_t numblocks; /* Number of blocks of erasesize in this region */ u_int32_t numblocks; /* Number of blocks of erasesize in this region */
unsigned long *lockmap; /* If keeping bitmap of locks */
};
/*
* oob operation modes
*
* MTD_OOB_PLACE: oob data are placed at the given offset
* MTD_OOB_AUTO: oob data are automatically placed at the free areas
* which are defined by the ecclayout
* MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data
* is inserted into the data. Thats a raw image of the
* flash contents.
*/
typedef enum {
MTD_OOB_PLACE,
MTD_OOB_AUTO,
MTD_OOB_RAW,
} mtd_oob_mode_t;
/**
* struct mtd_oob_ops - oob operation operands
* @mode: operation mode
*
* @len: number of data bytes to write/read
*
* @retlen: number of data bytes written/read
*
* @ooblen: number of oob bytes to write/read
* @oobretlen: number of oob bytes written/read
* @ooboffs: offset of oob data in the oob area (only relevant when
* mode = MTD_OOB_PLACE)
* @datbuf: data buffer - if NULL only oob data are read/written
* @oobbuf: oob data buffer
*
* Note, it is allowed to read more then one OOB area at one go, but not write.
* The interface assumes that the OOB write requests program only one page's
* OOB area.
*/
struct mtd_oob_ops {
mtd_oob_mode_t mode;
size_t len;
size_t retlen;
size_t ooblen;
size_t oobretlen;
uint32_t ooboffs;
uint8_t *datbuf;
uint8_t *oobbuf;
}; };
struct mtd_info { struct mtd_info {
u_char type; u_char type;
u_int32_t flags; u_int32_t flags;
u_int32_t size; /* Total size of the MTD */ u_int32_t size; // Total size of the MTD
/* "Major" erase size for the device. Naïve users may take this /* "Major" erase size for the device. Naïve users may take this
* to be the only erase size available, or may use the more detailed * to be the only erase size available, or may use the more detailed
* information below if they desire * information below if they desire
*/ */
u_int32_t erasesize; u_int32_t erasesize;
/* Minimal writable flash unit size. In case of NOR flash it is 1 (even
* though individual bits can be cleared), in case of NAND flash it is
* one NAND page (or half, or one-fourths of it), in case of ECC-ed NOR
* it is of ECC block size, etc. It is illegal to have writesize = 0.
* Any driver registering a struct mtd_info must ensure a writesize of
* 1 or larger.
*/
u_int32_t writesize;
u_int32_t oobblock; /* Size of OOB blocks (e.g. 512) */ u_int32_t oobsize; // Amount of OOB data per block (e.g. 16)
u_int32_t oobsize; /* Amount of OOB data per block (e.g. 16) */ u_int32_t oobavail; // Available OOB bytes per block
u_int32_t oobavail; /* Number of bytes in OOB area available for fs */
u_int32_t ecctype;
u_int32_t eccsize;
// Kernel-only stuff starts here.
/* Kernel-only stuff starts here. */
char *name; char *name;
int index; int index;
/* oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO) */ /* ecc layout structure pointer - read only ! */
struct nand_oobinfo oobinfo; struct nand_ecclayout *ecclayout;
/* Data for variable erase regions. If numeraseregions is zero, /* Data for variable erase regions. If numeraseregions is zero,
* it means that the whole device has erasesize as given above. * it means that the whole device has erasesize as given above.
@ -74,9 +128,6 @@ struct mtd_info {
int numeraseregions; int numeraseregions;
struct mtd_erase_region_info *eraseregions; struct mtd_erase_region_info *eraseregions;
/* This really shouldn't be here. It can go away in 2.5 */
u_int32_t bank_size;
int (*erase) (struct mtd_info *mtd, struct erase_info *instr); int (*erase) (struct mtd_info *mtd, struct erase_info *instr);
/* This stuff for eXecute-In-Place */ /* This stuff for eXecute-In-Place */
@ -89,39 +140,35 @@ struct mtd_info {
int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); int (*read_oob) (struct mtd_info *mtd, loff_t from,
int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); struct mtd_oob_ops *ops);
int (*write_oob) (struct mtd_info *mtd, loff_t to,
int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); struct mtd_oob_ops *ops);
int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
/* /*
* Methods to access the protection register area, present in some * Methods to access the protection register area, present in some
* flash devices. The user data is one time programmable but the * flash devices. The user data is one time programmable but the
* factory data is read only. * factory data is read only.
*/ */
int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*get_fact_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*get_user_prot_info) (struct mtd_info *mtd, struct otp_info *buf, size_t len);
/* This function is not yet implemented */ int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
int (*lock_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len);
/* XXX U-BOOT XXX */
#if 0 #if 0
/* kvec-based read/write methods. We need these especially for NAND flash, /* kvec-based read/write methods.
with its limited number of write cycles per erase.
NB: The 'count' parameter is the number of _vectors_, each of NB: The 'count' parameter is the number of _vectors_, each of
which contains an (ofs, len) tuple. which contains an (ofs, len) tuple.
*/ */
int (*readv) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from, size_t *retlen);
int (*readv_ecc) (struct mtd_info *mtd, struct kvec *vecs, unsigned long count, loff_t from,
size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen); int (*writev) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to, size_t *retlen);
int (*writev_ecc) (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count, loff_t to,
size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel);
#endif #endif
/* Sync */ /* Sync */
void (*sync) (struct mtd_info *mtd); void (*sync) (struct mtd_info *mtd);
#if 0
/* Chip-supported device locking */ /* Chip-supported device locking */
int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len); int (*lock) (struct mtd_info *mtd, loff_t ofs, size_t len);
int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len); int (*unlock) (struct mtd_info *mtd, loff_t ofs, size_t len);
@ -129,15 +176,32 @@ struct mtd_info {
/* Power Management functions */ /* Power Management functions */
int (*suspend) (struct mtd_info *mtd); int (*suspend) (struct mtd_info *mtd);
void (*resume) (struct mtd_info *mtd); void (*resume) (struct mtd_info *mtd);
#endif
/* Bad block management functions */ /* Bad block management functions */
int (*block_isbad) (struct mtd_info *mtd, loff_t ofs); int (*block_isbad) (struct mtd_info *mtd, loff_t ofs);
int (*block_markbad) (struct mtd_info *mtd, loff_t ofs); int (*block_markbad) (struct mtd_info *mtd, loff_t ofs);
/* XXX U-BOOT XXX */
#if 0
struct notifier_block reboot_notifier; /* default mode before reboot */
#endif
/* ECC status information */
struct mtd_ecc_stats ecc_stats;
/* Subpage shift (NAND) */
int subpage_sft;
void *priv; void *priv;
struct module *owner; struct module *owner;
int usecount; int usecount;
/* If the driver is something smart, like UBI, it may need to maintain
* its own reference counting. The below functions are only for driver.
* The driver may register its callbacks. These callbacks are not
* supposed to be called by MTD users */
int (*get_device) (struct mtd_info *mtd);
void (*put_device) (struct mtd_info *mtd);
}; };
@ -147,9 +211,11 @@ extern int add_mtd_device(struct mtd_info *mtd);
extern int del_mtd_device (struct mtd_info *mtd); extern int del_mtd_device (struct mtd_info *mtd);
extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num);
extern struct mtd_info *get_mtd_device_nm(const char *name);
extern void put_mtd_device(struct mtd_info *mtd); extern void put_mtd_device(struct mtd_info *mtd);
/* XXX U-BOOT XXX */
#if 0 #if 0
struct mtd_notifier { struct mtd_notifier {
void (*add)(struct mtd_info *mtd); void (*add)(struct mtd_info *mtd);
@ -157,7 +223,6 @@ struct mtd_notifier {
struct list_head list; struct list_head list;
}; };
extern void register_mtd_user (struct mtd_notifier *new); extern void register_mtd_user (struct mtd_notifier *new);
extern int unregister_mtd_user (struct mtd_notifier *old); extern int unregister_mtd_user (struct mtd_notifier *old);
@ -168,20 +233,6 @@ int default_mtd_readv(struct mtd_info *mtd, struct kvec *vecs,
unsigned long count, loff_t from, size_t *retlen); unsigned long count, loff_t from, size_t *retlen);
#endif #endif
#define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args)
#define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d))
#define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg)
#define MTD_READ(mtd, args...) (*(mtd->read))(mtd, args)
#define MTD_WRITE(mtd, args...) (*(mtd->write))(mtd, args)
#define MTD_READV(mtd, args...) (*(mtd->readv))(mtd, args)
#define MTD_WRITEV(mtd, args...) (*(mtd->writev))(mtd, args)
#define MTD_READECC(mtd, args...) (*(mtd->read_ecc))(mtd, args)
#define MTD_WRITEECC(mtd, args...) (*(mtd->write_ecc))(mtd, args)
#define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args)
#define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args)
#define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0)
#ifdef CONFIG_MTD_PARTITIONS #ifdef CONFIG_MTD_PARTITIONS
void mtd_erase_callback(struct erase_info *instr); void mtd_erase_callback(struct erase_info *instr);
#else #else
@ -208,7 +259,6 @@ static inline void mtd_erase_callback(struct erase_info *instr)
} while(0) } while(0)
#else /* CONFIG_MTD_DEBUG */ #else /* CONFIG_MTD_DEBUG */
#define MTDDEBUG(n, args...) do { } while(0) #define MTDDEBUG(n, args...) do { } while(0)
#endif /* CONFIG_MTD_DEBUG */ #endif /* CONFIG_MTD_DEBUG */
#endif /* __MTD_MTD_H__ */ #endif /* __MTD_MTD_H__ */

View file

@ -5,7 +5,7 @@
* Steven J. Hill <sjhill@realitydiluted.com> * Steven J. Hill <sjhill@realitydiluted.com>
* Thomas Gleixner <tglx@linutronix.de> * Thomas Gleixner <tglx@linutronix.de>
* *
* $Id: nand.h,v 1.68 2004/11/12 10:40:37 gleixner Exp $ * $Id: nand.h,v 1.74 2005/09/15 13:58:50 vwool Exp $
* *
* This program is free software; you can redistribute it and/or modify * This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as * it under the terms of the GNU General Public License version 2 as
@ -15,101 +15,110 @@
* Contains standard defines and IDs for NAND flash devices * Contains standard defines and IDs for NAND flash devices
* *
* Changelog: * Changelog:
* 01-31-2000 DMW Created * See git changelog.
* 09-18-2000 SJH Moved structure out of the Disk-On-Chip drivers
* so it can be used by other NAND flash device
* drivers. I also changed the copyright since none
* of the original contents of this file are specific
* to DoC devices. David can whack me with a baseball
* bat later if I did something naughty.
* 10-11-2000 SJH Added private NAND flash structure for driver
* 10-24-2000 SJH Added prototype for 'nand_scan' function
* 10-29-2001 TG changed nand_chip structure to support
* hardwarespecific function for accessing control lines
* 02-21-2002 TG added support for different read/write adress and
* ready/busy line access function
* 02-26-2002 TG added chip_delay to nand_chip structure to optimize
* command delay times for different chips
* 04-28-2002 TG OOB config defines moved from nand.c to avoid duplicate
* defines in jffs2/wbuf.c
* 08-07-2002 TG forced bad block location to byte 5 of OOB, even if
* CONFIG_MTD_NAND_ECC_JFFS2 is not set
* 08-10-2002 TG extensions to nand_chip structure to support HW-ECC
*
* 08-29-2002 tglx nand_chip structure: data_poi for selecting
* internal / fs-driver buffer
* support for 6byte/512byte hardware ECC
* read_ecc, write_ecc extended for different oob-layout
* oob layout selections: NAND_NONE_OOB, NAND_JFFS2_OOB,
* NAND_YAFFS_OOB
* 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL
* Split manufacturer and device ID structures
*
* 02-08-2004 tglx added option field to nand structure for chip anomalities
* 05-25-2004 tglx added bad block table support, ST-MICRO manufacturer id
* update of nand_chip structure description
*/ */
#ifndef __LINUX_MTD_NAND_H #ifndef __LINUX_MTD_NAND_H
#define __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H
#include <linux/mtd/compat.h> /* XXX U-BOOT XXX */
#if 0
#include <linux/wait.h>
#include <linux/spinlock.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#endif
#include "config.h"
#include "linux/mtd/compat.h"
#include "linux/mtd/mtd.h"
struct mtd_info; struct mtd_info;
/* Scan and identify a NAND device */ /* Scan and identify a NAND device */
extern int nand_scan (struct mtd_info *mtd, int max_chips); extern int nand_scan (struct mtd_info *mtd, int max_chips);
/* Separate phases of nand_scan(), allowing board driver to intervene
* and override command or ECC setup according to flash type */
extern int nand_scan_ident(struct mtd_info *mtd, int max_chips);
extern int nand_scan_tail(struct mtd_info *mtd);
/* Free resources held by the NAND device */ /* Free resources held by the NAND device */
extern void nand_release (struct mtd_info *mtd); extern void nand_release (struct mtd_info *mtd);
/* Read raw data from the device without ECC */ /* Internal helper for board drivers which need to override command function */
extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen); extern void nand_wait_ready(struct mtd_info *mtd);
/* The maximum number of NAND chips in an array */
#ifndef NAND_MAX_CHIPS
#define NAND_MAX_CHIPS 8
#endif
/* This constant declares the max. oobsize / page, which /* This constant declares the max. oobsize / page, which
* is supported now. If you add a chip with bigger oobsize/page * is supported now. If you add a chip with bigger oobsize/page
* adjust this accordingly. * adjust this accordingly.
*/ */
#define NAND_MAX_OOBSIZE 64 #define NAND_MAX_OOBSIZE 128
#define NAND_MAX_PAGESIZE 4096
/* /*
* Constants for hardware specific CLE/ALE/NCE function * Constants for hardware specific CLE/ALE/NCE function
*
* These are bits which can be or'ed to set/clear multiple
* bits in one go.
*/ */
/* Select the chip by setting nCE to low */ /* Select the chip by setting nCE to low */
#define NAND_CTL_SETNCE 1 #define NAND_NCE 0x01
/* Deselect the chip by setting nCE to high */
#define NAND_CTL_CLRNCE 2
/* Select the command latch by setting CLE to high */ /* Select the command latch by setting CLE to high */
#define NAND_CTL_SETCLE 3 #define NAND_CLE 0x02
/* Deselect the command latch by setting CLE to low */
#define NAND_CTL_CLRCLE 4
/* Select the address latch by setting ALE to high */ /* Select the address latch by setting ALE to high */
#define NAND_CTL_SETALE 5 #define NAND_ALE 0x04
/* Deselect the address latch by setting ALE to low */
#define NAND_CTL_CLRALE 6 #define NAND_CTRL_CLE (NAND_NCE | NAND_CLE)
/* Set write protection by setting WP to high. Not used! */ #define NAND_CTRL_ALE (NAND_NCE | NAND_ALE)
#define NAND_CTL_SETWP 7 #define NAND_CTRL_CHANGE 0x80
/* Clear write protection by setting WP to low. Not used! */
#define NAND_CTL_CLRWP 8
/* /*
* Standard NAND flash commands * Standard NAND flash commands
*/ */
#define NAND_CMD_READ0 0 #define NAND_CMD_READ0 0
#define NAND_CMD_READ1 1 #define NAND_CMD_READ1 1
#define NAND_CMD_RNDOUT 5
#define NAND_CMD_PAGEPROG 0x10 #define NAND_CMD_PAGEPROG 0x10
#define NAND_CMD_READOOB 0x50 #define NAND_CMD_READOOB 0x50
#define NAND_CMD_ERASE1 0x60 #define NAND_CMD_ERASE1 0x60
#define NAND_CMD_STATUS 0x70 #define NAND_CMD_STATUS 0x70
#define NAND_CMD_STATUS_MULTI 0x71 #define NAND_CMD_STATUS_MULTI 0x71
#define NAND_CMD_SEQIN 0x80 #define NAND_CMD_SEQIN 0x80
#define NAND_CMD_RNDIN 0x85
#define NAND_CMD_READID 0x90 #define NAND_CMD_READID 0x90
#define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_ERASE2 0xd0
#define NAND_CMD_RESET 0xff #define NAND_CMD_RESET 0xff
/* Extended commands for large page devices */ /* Extended commands for large page devices */
#define NAND_CMD_READSTART 0x30 #define NAND_CMD_READSTART 0x30
#define NAND_CMD_RNDOUTSTART 0xE0
#define NAND_CMD_CACHEDPROG 0x15 #define NAND_CMD_CACHEDPROG 0x15
/* Extended commands for AG-AND device */
/*
* Note: the command for NAND_CMD_DEPLETE1 is really 0x00 but
* there is no way to distinguish that from NAND_CMD_READ0
* until the remaining sequence of commands has been completed
* so add a high order bit and mask it off in the command.
*/
#define NAND_CMD_DEPLETE1 0x100
#define NAND_CMD_DEPLETE2 0x38
#define NAND_CMD_STATUS_MULTI 0x71
#define NAND_CMD_STATUS_ERROR 0x72
/* multi-bank error status (banks 0-3) */
#define NAND_CMD_STATUS_ERROR0 0x73
#define NAND_CMD_STATUS_ERROR1 0x74
#define NAND_CMD_STATUS_ERROR2 0x75
#define NAND_CMD_STATUS_ERROR3 0x76
#define NAND_CMD_STATUS_RESET 0x7f
#define NAND_CMD_STATUS_CLEAR 0xff
#define NAND_CMD_NONE -1
/* Status bits */ /* Status bits */
#define NAND_STATUS_FAIL 0x01 #define NAND_STATUS_FAIL 0x01
#define NAND_STATUS_FAIL_N1 0x02 #define NAND_STATUS_FAIL_N1 0x02
@ -120,21 +129,12 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
/* /*
* Constants for ECC_MODES * Constants for ECC_MODES
*/ */
typedef enum {
/* No ECC. Usage is not recommended ! */ NAND_ECC_NONE,
#define NAND_ECC_NONE 0 NAND_ECC_SOFT,
/* Software ECC 3 byte ECC per 256 Byte data */ NAND_ECC_HW,
#define NAND_ECC_SOFT 1 NAND_ECC_HW_SYNDROME,
/* Hardware ECC 3 byte ECC per 256 Byte data */ } nand_ecc_modes_t;
#define NAND_ECC_HW3_256 2
/* Hardware ECC 3 byte ECC per 512 Byte data */
#define NAND_ECC_HW3_512 3
/* Hardware ECC 6 byte ECC per 512 Byte data */
#define NAND_ECC_HW6_512 4
/* Hardware ECC 8 byte ECC per 512 Byte data */
#define NAND_ECC_HW8_512 6
/* Hardware ECC 12 byte ECC per 2048 Byte data */
#define NAND_ECC_HW12_2048 7
/* /*
* Constants for Hardware ECC * Constants for Hardware ECC
@ -146,6 +146,10 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
/* Enable Hardware ECC before syndrom is read back from flash */ /* Enable Hardware ECC before syndrom is read back from flash */
#define NAND_ECC_READSYN 2 #define NAND_ECC_READSYN 2
/* Bit mask for flags passed to do_nand_read_ecc */
#define NAND_GET_DEVICE 0x80
/* Option constants for bizarre disfunctionality and real /* Option constants for bizarre disfunctionality and real
* features * features
*/ */
@ -165,6 +169,17 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
/* Chip has a array of 4 pages which can be read without /* Chip has a array of 4 pages which can be read without
* additional ready /busy waits */ * additional ready /busy waits */
#define NAND_4PAGE_ARRAY 0x00000040 #define NAND_4PAGE_ARRAY 0x00000040
/* Chip requires that BBT is periodically rewritten to prevent
* bits from adjacent blocks from 'leaking' in altering data.
* This happens with the Renesas AG-AND chips, possibly others. */
#define BBT_AUTO_REFRESH 0x00000080
/* Chip does not require ready check on read. True
* for all large page devices, as they do not support
* autoincrement.*/
#define NAND_NO_READRDY 0x00000100
/* Chip does not allow subpage writes */
#define NAND_NO_SUBPAGE_WRITE 0x00000200
/* Options valid for Samsung large page devices */ /* Options valid for Samsung large page devices */
#define NAND_SAMSUNG_LP_OPTIONS \ #define NAND_SAMSUNG_LP_OPTIONS \
@ -183,18 +198,18 @@ extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_
/* Use a flash based bad block table. This option is passed to the /* Use a flash based bad block table. This option is passed to the
* default bad block table function. */ * default bad block table function. */
#define NAND_USE_FLASH_BBT 0x00010000 #define NAND_USE_FLASH_BBT 0x00010000
/* The hw ecc generator provides a syndrome instead a ecc value on read /* This option skips the bbt scan during initialization. */
* This can only work if we have the ecc bytes directly behind the #define NAND_SKIP_BBTSCAN 0x00020000
* data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */ /* This option is defined if the board driver allocates its own buffers
#define NAND_HWECC_SYNDROME 0x00020000 (e.g. because it needs them DMA-coherent */
#define NAND_OWN_BUFFERS 0x00040000
/* Options set by nand scan */ /* Options set by nand scan */
/* Nand scan has allocated oob_buf */ /* Nand scan has allocated controller struct */
#define NAND_OOBBUF_ALLOC 0x40000000 #define NAND_CONTROLLER_ALLOC 0x80000000
/* Nand scan has allocated data_buf */
#define NAND_DATABUF_ALLOC 0x80000000
/* Cell info constants */
#define NAND_CI_CHIPNR_MSK 0x03
#define NAND_CI_CELLTYPE_MSK 0x0C
/* /*
* nand_state_t - chip states * nand_state_t - chip states
@ -207,135 +222,216 @@ typedef enum {
FL_ERASING, FL_ERASING,
FL_SYNCING, FL_SYNCING,
FL_CACHEDPRG, FL_CACHEDPRG,
FL_PM_SUSPENDED,
} nand_state_t; } nand_state_t;
/* Keep gcc happy */ /* Keep gcc happy */
struct nand_chip; struct nand_chip;
#if 0
/** /**
* struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independend devices * struct nand_hw_control - Control structure for hardware controller (e.g ECC generator) shared among independent devices
* @lock: protection lock * @lock: protection lock
* @active: the mtd device which holds the controller currently * @active: the mtd device which holds the controller currently
* @wq: wait queue to sleep on if a NAND operation is in progress
* used instead of the per chip wait queue when a hw controller is available
*/ */
struct nand_hw_control { struct nand_hw_control {
#if 0
spinlock_t lock; spinlock_t lock;
wait_queue_head_t wq;
#endif
struct nand_chip *active; struct nand_chip *active;
}; };
#endif
/**
* struct nand_ecc_ctrl - Control structure for ecc
* @mode: ecc mode
* @steps: number of ecc steps per page
* @size: data bytes per ecc step
* @bytes: ecc bytes per step
* @total: total number of ecc bytes per page
* @prepad: padding information for syndrome based ecc generators
* @postpad: padding information for syndrome based ecc generators
* @layout: ECC layout control struct pointer
* @hwctl: function to control hardware ecc generator. Must only
* be provided if an hardware ECC is available
* @calculate: function for ecc calculation or readback from ecc hardware
* @correct: function for ecc correction, matching to ecc generator (sw/hw)
* @read_page_raw: function to read a raw page without ECC
* @write_page_raw: function to write a raw page without ECC
* @read_page: function to read a page according to the ecc generator requirements
* @write_page: function to write a page according to the ecc generator requirements
* @read_oob: function to read chip OOB data
* @write_oob: function to write chip OOB data
*/
struct nand_ecc_ctrl {
nand_ecc_modes_t mode;
int steps;
int size;
int bytes;
int total;
int prepad;
int postpad;
struct nand_ecclayout *layout;
void (*hwctl)(struct mtd_info *mtd, int mode);
int (*calculate)(struct mtd_info *mtd,
const uint8_t *dat,
uint8_t *ecc_code);
int (*correct)(struct mtd_info *mtd, uint8_t *dat,
uint8_t *read_ecc,
uint8_t *calc_ecc);
int (*read_page_raw)(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf);
void (*write_page_raw)(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf);
int (*read_page)(struct mtd_info *mtd,
struct nand_chip *chip,
uint8_t *buf);
void (*write_page)(struct mtd_info *mtd,
struct nand_chip *chip,
const uint8_t *buf);
int (*read_oob)(struct mtd_info *mtd,
struct nand_chip *chip,
int page,
int sndcmd);
int (*write_oob)(struct mtd_info *mtd,
struct nand_chip *chip,
int page);
};
/**
* struct nand_buffers - buffer structure for read/write
* @ecccalc: buffer for calculated ecc
* @ecccode: buffer for ecc read from flash
* @databuf: buffer for data - dynamically sized
*
* Do not change the order of buffers. databuf and oobrbuf must be in
* consecutive order.
*/
struct nand_buffers {
uint8_t ecccalc[NAND_MAX_OOBSIZE];
uint8_t ecccode[NAND_MAX_OOBSIZE];
uint8_t databuf[NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE];
};
/** /**
* struct nand_chip - NAND Private Flash Chip Data * struct nand_chip - NAND Private Flash Chip Data
* @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device * @IO_ADDR_R: [BOARDSPECIFIC] address to read the 8 I/O lines of the flash device
* @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device * @IO_ADDR_W: [BOARDSPECIFIC] address to write the 8 I/O lines of the flash device
* @read_byte: [REPLACEABLE] read one byte from the chip * @read_byte: [REPLACEABLE] read one byte from the chip
* @write_byte: [REPLACEABLE] write one byte to the chip
* @read_word: [REPLACEABLE] read one word from the chip * @read_word: [REPLACEABLE] read one word from the chip
* @write_word: [REPLACEABLE] write one word to the chip
* @write_buf: [REPLACEABLE] write data from the buffer to the chip * @write_buf: [REPLACEABLE] write data from the buffer to the chip
* @read_buf: [REPLACEABLE] read data from the chip into the buffer * @read_buf: [REPLACEABLE] read data from the chip into the buffer
* @verify_buf: [REPLACEABLE] verify buffer contents against the chip data * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data
* @select_chip: [REPLACEABLE] select chip nr * @select_chip: [REPLACEABLE] select chip nr
* @block_bad: [REPLACEABLE] check, if the block is bad * @block_bad: [REPLACEABLE] check, if the block is bad
* @block_markbad: [REPLACEABLE] mark the block bad * @block_markbad: [REPLACEABLE] mark the block bad
* @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines * @cmd_ctrl: [BOARDSPECIFIC] hardwarespecific funtion for controlling
* ALE/CLE/nCE. Also used to write command and address
* @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line * @dev_ready: [BOARDSPECIFIC] hardwarespecific function for accesing device ready/busy line
* If set to NULL no access to ready/busy is available and the ready/busy information * If set to NULL no access to ready/busy is available and the ready/busy information
* is read from the chip status register * is read from the chip status register
* @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip
* @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready
* @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware * @ecc: [BOARDSPECIFIC] ecc control ctructure
* @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw) * @buffers: buffer structure for read/write
* @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only * @hwcontrol: platform-specific hardware control structure
* be provided if a hardware ECC is available * @ops: oob operation operands
* @erase_cmd: [INTERN] erase command write function, selectable due to AND support * @erase_cmd: [INTERN] erase command write function, selectable due to AND support
* @scan_bbt: [REPLACEABLE] function to scan bad block table * @scan_bbt: [REPLACEABLE] function to scan bad block table
* @eccmode: [BOARDSPECIFIC] mode of ecc, see defines
* @eccsize: [INTERN] databytes used per ecc-calculation
* @eccbytes: [INTERN] number of ecc bytes per ecc-calculation step
* @eccsteps: [INTERN] number of ecc calculation steps per page
* @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR) * @chip_delay: [BOARDSPECIFIC] chip dependent delay for transfering data from array to read regs (tR)
* @chip_lock: [INTERN] spinlock used to protect access to this structure and the chip
* @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress
* @state: [INTERN] the current state of the NAND device * @state: [INTERN] the current state of the NAND device
* @oob_poi: poison value buffer
* @page_shift: [INTERN] number of address bits in a page (column address bits) * @page_shift: [INTERN] number of address bits in a page (column address bits)
* @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock * @phys_erase_shift: [INTERN] number of address bits in a physical eraseblock
* @bbt_erase_shift: [INTERN] number of address bits in a bbt entry * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
* @chip_shift: [INTERN] number of address bits in one chip * @chip_shift: [INTERN] number of address bits in one chip
* @data_buf: [INTERN] internal buffer for one page + oob * @datbuf: [INTERN] internal buffer for one page + oob
* @oob_buf: [INTERN] oob buffer for one eraseblock * @oobbuf: [INTERN] oob buffer for one eraseblock
* @oobdirty: [INTERN] indicates that oob_buf must be reinitialized * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized
* @data_poi: [INTERN] pointer to a data buffer * @data_poi: [INTERN] pointer to a data buffer
* @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about * @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
* special functionality. See the defines for further explanation * special functionality. See the defines for further explanation
* @badblockpos: [INTERN] position of the bad block marker in the oob area * @badblockpos: [INTERN] position of the bad block marker in the oob area
* @cellinfo: [INTERN] MLC/multichip data from chip ident
* @numchips: [INTERN] number of physical chips * @numchips: [INTERN] number of physical chips
* @chipsize: [INTERN] the size of one chip for multichip arrays * @chipsize: [INTERN] the size of one chip for multichip arrays
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1 * @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
* @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf * @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf
* @autooob: [REPLACEABLE] the default (auto)placement scheme * @subpagesize: [INTERN] holds the subpagesize
* @ecclayout: [REPLACEABLE] the default ecc placement scheme
* @bbt: [INTERN] bad block table pointer * @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup * @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor * @bbt_md: [REPLACEABLE] bad block table mirror descriptor
* @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan * @badblock_pattern: [REPLACEABLE] bad block scan pattern used for initial bad block scan
* @controller: [OPTIONAL] a pointer to a hardware controller structure which is shared among multiple independend devices * @controller: [REPLACEABLE] a pointer to a hardware controller structure
* which is shared among multiple independend devices
* @priv: [OPTIONAL] pointer to private chip date * @priv: [OPTIONAL] pointer to private chip date
* @errstat: [OPTIONAL] hardware specific function to perform additional error status checks
* (determine if errors are correctable)
* @write_page: [REPLACEABLE] High-level page write function
*/ */
struct nand_chip { struct nand_chip {
void __iomem *IO_ADDR_R; void __iomem *IO_ADDR_R;
void __iomem *IO_ADDR_W; void __iomem *IO_ADDR_W;
u_char (*read_byte)(struct mtd_info *mtd); uint8_t (*read_byte)(struct mtd_info *mtd);
void (*write_byte)(struct mtd_info *mtd, u_char byte);
u16 (*read_word)(struct mtd_info *mtd); u16 (*read_word)(struct mtd_info *mtd);
void (*write_word)(struct mtd_info *mtd, u16 word); void (*write_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, uint8_t *buf, int len);
void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); int (*verify_buf)(struct mtd_info *mtd, const uint8_t *buf, int len);
void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len);
int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len);
void (*select_chip)(struct mtd_info *mtd, int chip); void (*select_chip)(struct mtd_info *mtd, int chip);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip); int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs); int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*hwcontrol)(struct mtd_info *mtd, int cmd); void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
unsigned int ctrl);
int (*dev_ready)(struct mtd_info *mtd); int (*dev_ready)(struct mtd_info *mtd);
void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr);
int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this);
int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
void (*enable_hwecc)(struct mtd_info *mtd, int mode);
void (*erase_cmd)(struct mtd_info *mtd, int page); void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd); int (*scan_bbt)(struct mtd_info *mtd);
int eccmode; int (*errstat)(struct mtd_info *mtd, struct nand_chip *this, int state, int status, int page);
int eccsize; int (*write_page)(struct mtd_info *mtd, struct nand_chip *chip,
int eccbytes; const uint8_t *buf, int page, int cached, int raw);
int eccsteps;
int chip_delay; int chip_delay;
#if 0 unsigned int options;
spinlock_t chip_lock;
wait_queue_head_t wq;
nand_state_t state;
#endif
int page_shift; int page_shift;
int phys_erase_shift; int phys_erase_shift;
int bbt_erase_shift; int bbt_erase_shift;
int chip_shift; int chip_shift;
u_char *data_buf;
u_char *oob_buf;
int oobdirty;
u_char *data_poi;
unsigned int options;
int badblockpos;
int numchips; int numchips;
unsigned long chipsize; unsigned long chipsize;
int pagemask; int pagemask;
int pagebuf; int pagebuf;
struct nand_oobinfo *autooob; int subpagesize;
uint8_t cellinfo;
int badblockpos;
nand_state_t state;
uint8_t *oob_poi;
struct nand_hw_control *controller;
struct nand_ecclayout *ecclayout;
struct nand_ecc_ctrl ecc;
struct nand_buffers *buffers;
struct nand_hw_control hwcontrol;
struct mtd_oob_ops ops;
uint8_t *bbt; uint8_t *bbt;
struct nand_bbt_descr *bbt_td; struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md; struct nand_bbt_descr *bbt_md;
struct nand_bbt_descr *badblock_pattern; struct nand_bbt_descr *badblock_pattern;
struct nand_hw_control *controller;
void *priv; void *priv;
}; };
@ -348,11 +444,11 @@ struct nand_chip {
#define NAND_MFR_NATIONAL 0x8f #define NAND_MFR_NATIONAL 0x8f
#define NAND_MFR_RENESAS 0x07 #define NAND_MFR_RENESAS 0x07
#define NAND_MFR_STMICRO 0x20 #define NAND_MFR_STMICRO 0x20
#define NAND_MFR_HYNIX 0xad
#define NAND_MFR_MICRON 0x2c #define NAND_MFR_MICRON 0x2c
/** /**
* struct nand_flash_dev - NAND Flash Device ID Structure * struct nand_flash_dev - NAND Flash Device ID Structure
*
* @name: Identify the device type * @name: Identify the device type
* @id: device ID code * @id: device ID code
* @pagesize: Pagesize in bytes. Either 256 or 512 or 0 * @pagesize: Pagesize in bytes. Either 256 or 512 or 0
@ -462,7 +558,10 @@ extern int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd);
extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs); extern int nand_update_bbt(struct mtd_info *mtd, loff_t offs);
extern int nand_default_bbt(struct mtd_info *mtd); extern int nand_default_bbt(struct mtd_info *mtd);
extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt); extern int nand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt);
extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt); extern int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr,
int allowbbt);
extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, uint8_t * buf);
/* /*
* Constants for oob configuration * Constants for oob configuration
@ -470,4 +569,67 @@ extern int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int
#define NAND_SMALL_BADBLOCK_POS 5 #define NAND_SMALL_BADBLOCK_POS 5
#define NAND_LARGE_BADBLOCK_POS 0 #define NAND_LARGE_BADBLOCK_POS 0
/**
* struct platform_nand_chip - chip level device structure
* @nr_chips: max. number of chips to scan for
* @chip_offset: chip number offset
* @nr_partitions: number of partitions pointed to by partitions (or zero)
* @partitions: mtd partition list
* @chip_delay: R/B delay value in us
* @options: Option flags, e.g. 16bit buswidth
* @ecclayout: ecc layout info structure
* @part_probe_types: NULL-terminated array of probe types
* @priv: hardware controller specific settings
*/
struct platform_nand_chip {
int nr_chips;
int chip_offset;
int nr_partitions;
struct mtd_partition *partitions;
struct nand_ecclayout *ecclayout;
int chip_delay;
unsigned int options;
const char **part_probe_types;
void *priv;
};
/**
* struct platform_nand_ctrl - controller level device structure
* @hwcontrol: platform specific hardware control structure
* @dev_ready: platform specific function to read ready/busy pin
* @select_chip: platform specific chip select function
* @cmd_ctrl: platform specific function for controlling
* ALE/CLE/nCE. Also used to write command and address
* @priv: private data to transport driver specific settings
*
* All fields are optional and depend on the hardware driver requirements
*/
struct platform_nand_ctrl {
void (*hwcontrol)(struct mtd_info *mtd, int cmd);
int (*dev_ready)(struct mtd_info *mtd);
void (*select_chip)(struct mtd_info *mtd, int chip);
void (*cmd_ctrl)(struct mtd_info *mtd, int dat,
unsigned int ctrl);
void *priv;
};
/**
* struct platform_nand_data - container structure for platform-specific data
* @chip: chip level chip structure
* @ctrl: controller level device structure
*/
struct platform_nand_data {
struct platform_nand_chip chip;
struct platform_nand_ctrl ctrl;
};
/* Some helpers to access the data structures */
static inline
struct platform_nand_chip *get_platform_nandchip(struct mtd_info *mtd)
{
struct nand_chip *chip = mtd->priv;
return chip->priv;
}
#endif /* __LINUX_MTD_NAND_H */ #endif /* __LINUX_MTD_NAND_H */

View file

@ -0,0 +1,76 @@
/*
* $Id: nftl-user.h,v 1.2 2005/11/07 11:14:56 gleixner Exp $
*
* Parts of NFTL headers shared with userspace
*
*/
#ifndef __MTD_NFTL_USER_H__
#define __MTD_NFTL_USER_H__
/* Block Control Information */
struct nftl_bci {
unsigned char ECCSig[6];
uint8_t Status;
uint8_t Status1;
}__attribute__((packed));
/* Unit Control Information */
struct nftl_uci0 {
uint16_t VirtUnitNum;
uint16_t ReplUnitNum;
uint16_t SpareVirtUnitNum;
uint16_t SpareReplUnitNum;
} __attribute__((packed));
struct nftl_uci1 {
uint32_t WearInfo;
uint16_t EraseMark;
uint16_t EraseMark1;
} __attribute__((packed));
struct nftl_uci2 {
uint16_t FoldMark;
uint16_t FoldMark1;
uint32_t unused;
} __attribute__((packed));
union nftl_uci {
struct nftl_uci0 a;
struct nftl_uci1 b;
struct nftl_uci2 c;
};
struct nftl_oob {
struct nftl_bci b;
union nftl_uci u;
};
/* NFTL Media Header */
struct NFTLMediaHeader {
char DataOrgID[6];
uint16_t NumEraseUnits;
uint16_t FirstPhysicalEUN;
uint32_t FormattedSize;
unsigned char UnitSizeFactor;
} __attribute__((packed));
#define MAX_ERASE_ZONES (8192 - 512)
#define ERASE_MARK 0x3c69
#define SECTOR_FREE 0xff
#define SECTOR_USED 0x55
#define SECTOR_IGNORE 0x11
#define SECTOR_DELETED 0x00
#define FOLD_MARK_IN_PROGRESS 0x5555
#define ZONE_GOOD 0xff
#define ZONE_BAD_ORIGINAL 0
#define ZONE_BAD_MARKED 7
#endif /* __MTD_NFTL_USER_H__ */

View file

@ -1,75 +1,16 @@
/*
/* Defines for NAND Flash Translation Layer */ * $Id: nftl.h,v 1.16 2004/06/30 14:49:00 dbrown Exp $
/* (c) 1999 Machine Vision Holdings, Inc. */ *
/* Author: David Woodhouse <dwmw2@mvhi.com> */ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org>
/* $Id: nftl.h,v 1.10 2000/12/29 00:25:38 dwmw2 Exp $ */ */
#ifndef __MTD_NFTL_H__ #ifndef __MTD_NFTL_H__
#define __MTD_NFTL_H__ #define __MTD_NFTL_H__
/* Block Control Information */ #include <linux/mtd/mtd.h>
#include <linux/mtd/blktrans.h>
struct nftl_bci { #include <linux/mtd/nftl-user.h>
unsigned char ECCSig[6];
__u8 Status;
__u8 Status1;
}__attribute__((packed));
/* Unit Control Information */
struct nftl_uci0 {
__u16 VirtUnitNum;
__u16 ReplUnitNum;
__u16 SpareVirtUnitNum;
__u16 SpareReplUnitNum;
} __attribute__((packed));
struct nftl_uci1 {
__u32 WearInfo;
__u16 EraseMark;
__u16 EraseMark1;
} __attribute__((packed));
struct nftl_uci2 {
__u16 FoldMark;
__u16 FoldMark1;
__u32 unused;
} __attribute__((packed));
union nftl_uci {
struct nftl_uci0 a;
struct nftl_uci1 b;
struct nftl_uci2 c;
};
struct nftl_oob {
struct nftl_bci b;
union nftl_uci u;
};
/* NFTL Media Header */
struct NFTLMediaHeader {
char DataOrgID[6];
__u16 NumEraseUnits;
__u16 FirstPhysicalEUN;
__u32 FormattedSize;
unsigned char UnitSizeFactor;
} __attribute__((packed));
#define MAX_ERASE_ZONES (8192 - 512)
#define ERASE_MARK 0x3c69
#define SECTOR_FREE 0xff
#define SECTOR_USED 0x55
#define SECTOR_IGNORE 0x11
#define SECTOR_DELETED 0x00
#define FOLD_MARK_IN_PROGRESS 0x5555
#define ZONE_GOOD 0xff
#define ZONE_BAD_ORIGINAL 0
#define ZONE_BAD_MARKED 7
/* these info are used in ReplUnitTable */ /* these info are used in ReplUnitTable */
#define BLOCK_NIL 0xffff /* last block of a chain */ #define BLOCK_NIL 0xffff /* last block of a chain */
@ -78,7 +19,7 @@ struct NFTLMediaHeader {
#define BLOCK_RESERVED 0xfffc /* bios block or bad block */ #define BLOCK_RESERVED 0xfffc /* bios block or bad block */
struct NFTLrecord { struct NFTLrecord {
struct DiskOnChip *mtd; struct mtd_blktrans_dev mbd;
__u16 MediaUnit, SpareMediaUnit; __u16 MediaUnit, SpareMediaUnit;
__u32 EraseSize; __u32 EraseSize;
struct NFTLMediaHeader MediaHdr; struct NFTLMediaHeader MediaHdr;
@ -90,16 +31,24 @@ struct NFTLrecord {
__u16 lastEUN; /* should be suppressed */ __u16 lastEUN; /* should be suppressed */
__u16 numfreeEUNs; __u16 numfreeEUNs;
__u16 LastFreeEUN; /* To speed up finding a free EUN */ __u16 LastFreeEUN; /* To speed up finding a free EUN */
__u32 nr_sects;
int head,sect,cyl; int head,sect,cyl;
__u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */ __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */
__u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */ __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */
unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_blocks; /* number of physical blocks */
unsigned int nb_boot_blocks; /* number of blocks used by the bios */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */
struct erase_info instr;
struct nand_ecclayout oobinfo;
}; };
int NFTL_mount(struct NFTLrecord *s);
int NFTL_formatblock(struct NFTLrecord *s, int block);
#ifndef NFTL_MAJOR
#define NFTL_MAJOR 93
#endif
#define MAX_NFTLS 16 #define MAX_NFTLS 16
#define MAX_SECTORS_PER_UNIT 32 #define MAX_SECTORS_PER_UNIT 64
#define NFTL_PARTN_BITS 4 #define NFTL_PARTN_BITS 4
#endif /* __MTD_NFTL_H__ */ #endif /* __MTD_NFTL_H__ */

View file

@ -0,0 +1,360 @@
/*
* Copyright (c) International Business Machines Corp., 2006
*
* 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
*
* Authors: Artem Bityutskiy (Битюцкий Артём)
* Thomas Gleixner
* Frank Haverkamp
* Oliver Lohmann
* Andreas Arnez
*/
/*
* This file defines the layout of UBI headers and all the other UBI on-flash
* data structures. May be included by user-space.
*/
#ifndef __UBI_HEADER_H__
#define __UBI_HEADER_H__
#include <asm/byteorder.h>
/* The version of UBI images supported by this implementation */
#define UBI_VERSION 1
/* The highest erase counter value supported by this implementation */
#define UBI_MAX_ERASECOUNTER 0x7FFFFFFF
/* The initial CRC32 value used when calculating CRC checksums */
#define UBI_CRC32_INIT 0xFFFFFFFFU
/* Erase counter header magic number (ASCII "UBI#") */
#define UBI_EC_HDR_MAGIC 0x55424923
/* Volume identifier header magic number (ASCII "UBI!") */
#define UBI_VID_HDR_MAGIC 0x55424921
/*
* Volume type constants used in the volume identifier header.
*
* @UBI_VID_DYNAMIC: dynamic volume
* @UBI_VID_STATIC: static volume
*/
enum {
UBI_VID_DYNAMIC = 1,
UBI_VID_STATIC = 2
};
/*
* Compatibility constants used by internal volumes.
*
* @UBI_COMPAT_DELETE: delete this internal volume before anything is written
* to the flash
* @UBI_COMPAT_RO: attach this device in read-only mode
* @UBI_COMPAT_PRESERVE: preserve this internal volume - do not touch its
* physical eraseblocks, don't allow the wear-leveling unit to move them
* @UBI_COMPAT_REJECT: reject this UBI image
*/
enum {
UBI_COMPAT_DELETE = 1,
UBI_COMPAT_RO = 2,
UBI_COMPAT_PRESERVE = 4,
UBI_COMPAT_REJECT = 5
};
/*
* ubi16_t/ubi32_t/ubi64_t - 16, 32, and 64-bit integers used in UBI on-flash
* data structures.
*/
typedef struct {
uint16_t int16;
} __attribute__ ((packed)) ubi16_t;
typedef struct {
uint32_t int32;
} __attribute__ ((packed)) ubi32_t;
typedef struct {
uint64_t int64;
} __attribute__ ((packed)) ubi64_t;
/*
* In this implementation of UBI uses the big-endian format for on-flash
* integers. The below are the corresponding conversion macros.
*/
#define cpu_to_ubi16(x) ((ubi16_t){__cpu_to_be16(x)})
#define ubi16_to_cpu(x) ((uint16_t)__be16_to_cpu((x).int16))
#define cpu_to_ubi32(x) ((ubi32_t){__cpu_to_be32(x)})
#define ubi32_to_cpu(x) ((uint32_t)__be32_to_cpu((x).int32))
#define cpu_to_ubi64(x) ((ubi64_t){__cpu_to_be64(x)})
#define ubi64_to_cpu(x) ((uint64_t)__be64_to_cpu((x).int64))
/* Sizes of UBI headers */
#define UBI_EC_HDR_SIZE sizeof(struct ubi_ec_hdr)
#define UBI_VID_HDR_SIZE sizeof(struct ubi_vid_hdr)
/* Sizes of UBI headers without the ending CRC */
#define UBI_EC_HDR_SIZE_CRC (UBI_EC_HDR_SIZE - sizeof(ubi32_t))
#define UBI_VID_HDR_SIZE_CRC (UBI_VID_HDR_SIZE - sizeof(ubi32_t))
/**
* struct ubi_ec_hdr - UBI erase counter header.
* @magic: erase counter header magic number (%UBI_EC_HDR_MAGIC)
* @version: version of UBI implementation which is supposed to accept this
* UBI image
* @padding1: reserved for future, zeroes
* @ec: the erase counter
* @vid_hdr_offset: where the VID header starts
* @data_offset: where the user data start
* @padding2: reserved for future, zeroes
* @hdr_crc: erase counter header CRC checksum
*
* The erase counter header takes 64 bytes and has a plenty of unused space for
* future usage. The unused fields are zeroed. The @version field is used to
* indicate the version of UBI implementation which is supposed to be able to
* work with this UBI image. If @version is greater then the current UBI
* version, the image is rejected. This may be useful in future if something
* is changed radically. This field is duplicated in the volume identifier
* header.
*
* The @vid_hdr_offset and @data_offset fields contain the offset of the the
* volume identifier header and user data, relative to the beginning of the
* physical eraseblock. These values have to be the same for all physical
* eraseblocks.
*/
struct ubi_ec_hdr {
ubi32_t magic;
uint8_t version;
uint8_t padding1[3];
ubi64_t ec; /* Warning: the current limit is 31-bit anyway! */
ubi32_t vid_hdr_offset;
ubi32_t data_offset;
uint8_t padding2[36];
ubi32_t hdr_crc;
} __attribute__ ((packed));
/**
* struct ubi_vid_hdr - on-flash UBI volume identifier header.
* @magic: volume identifier header magic number (%UBI_VID_HDR_MAGIC)
* @version: UBI implementation version which is supposed to accept this UBI
* image (%UBI_VERSION)
* @vol_type: volume type (%UBI_VID_DYNAMIC or %UBI_VID_STATIC)
* @copy_flag: if this logical eraseblock was copied from another physical
* eraseblock (for wear-leveling reasons)
* @compat: compatibility of this volume (%0, %UBI_COMPAT_DELETE,
* %UBI_COMPAT_IGNORE, %UBI_COMPAT_PRESERVE, or %UBI_COMPAT_REJECT)
* @vol_id: ID of this volume
* @lnum: logical eraseblock number
* @leb_ver: version of this logical eraseblock (IMPORTANT: obsolete, to be
* removed, kept only for not breaking older UBI users)
* @data_size: how many bytes of data this logical eraseblock contains
* @used_ebs: total number of used logical eraseblocks in this volume
* @data_pad: how many bytes at the end of this physical eraseblock are not
* used
* @data_crc: CRC checksum of the data stored in this logical eraseblock
* @padding1: reserved for future, zeroes
* @sqnum: sequence number
* @padding2: reserved for future, zeroes
* @hdr_crc: volume identifier header CRC checksum
*
* The @sqnum is the value of the global sequence counter at the time when this
* VID header was created. The global sequence counter is incremented each time
* UBI writes a new VID header to the flash, i.e. when it maps a logical
* eraseblock to a new physical eraseblock. The global sequence counter is an
* unsigned 64-bit integer and we assume it never overflows. The @sqnum
* (sequence number) is used to distinguish between older and newer versions of
* logical eraseblocks.
*
* There are 2 situations when there may be more then one physical eraseblock
* corresponding to the same logical eraseblock, i.e., having the same @vol_id
* and @lnum values in the volume identifier header. Suppose we have a logical
* eraseblock L and it is mapped to the physical eraseblock P.
*
* 1. Because UBI may erase physical eraseblocks asynchronously, the following
* situation is possible: L is asynchronously erased, so P is scheduled for
* erasure, then L is written to,i.e. mapped to another physical eraseblock P1,
* so P1 is written to, then an unclean reboot happens. Result - there are 2
* physical eraseblocks P and P1 corresponding to the same logical eraseblock
* L. But P1 has greater sequence number, so UBI picks P1 when it attaches the
* flash.
*
* 2. From time to time UBI moves logical eraseblocks to other physical
* eraseblocks for wear-leveling reasons. If, for example, UBI moves L from P
* to P1, and an unclean reboot happens before P is physically erased, there
* are two physical eraseblocks P and P1 corresponding to L and UBI has to
* select one of them when the flash is attached. The @sqnum field says which
* PEB is the original (obviously P will have lower @sqnum) and the copy. But
* it is not enough to select the physical eraseblock with the higher sequence
* number, because the unclean reboot could have happen in the middle of the
* copying process, so the data in P is corrupted. It is also not enough to
* just select the physical eraseblock with lower sequence number, because the
* data there may be old (consider a case if more data was added to P1 after
* the copying). Moreover, the unclean reboot may happen when the erasure of P
* was just started, so it result in unstable P, which is "mostly" OK, but
* still has unstable bits.
*
* UBI uses the @copy_flag field to indicate that this logical eraseblock is a
* copy. UBI also calculates data CRC when the data is moved and stores it at
* the @data_crc field of the copy (P1). So when UBI needs to pick one physical
* eraseblock of two (P or P1), the @copy_flag of the newer one (P1) is
* examined. If it is cleared, the situation* is simple and the newer one is
* picked. If it is set, the data CRC of the copy (P1) is examined. If the CRC
* checksum is correct, this physical eraseblock is selected (P1). Otherwise
* the older one (P) is selected.
*
* Note, there is an obsolete @leb_ver field which was used instead of @sqnum
* in the past. But it is not used anymore and we keep it in order to be able
* to deal with old UBI images. It will be removed at some point.
*
* There are 2 sorts of volumes in UBI: user volumes and internal volumes.
* Internal volumes are not seen from outside and are used for various internal
* UBI purposes. In this implementation there is only one internal volume - the
* layout volume. Internal volumes are the main mechanism of UBI extensions.
* For example, in future one may introduce a journal internal volume. Internal
* volumes have their own reserved range of IDs.
*
* The @compat field is only used for internal volumes and contains the "degree
* of their compatibility". It is always zero for user volumes. This field
* provides a mechanism to introduce UBI extensions and to be still compatible
* with older UBI binaries. For example, if someone introduced a journal in
* future, he would probably use %UBI_COMPAT_DELETE compatibility for the
* journal volume. And in this case, older UBI binaries, which know nothing
* about the journal volume, would just delete this volume and work perfectly
* fine. This is similar to what Ext2fs does when it is fed by an Ext3fs image
* - it just ignores the Ext3fs journal.
*
* The @data_crc field contains the CRC checksum of the contents of the logical
* eraseblock if this is a static volume. In case of dynamic volumes, it does
* not contain the CRC checksum as a rule. The only exception is when the
* data of the physical eraseblock was moved by the wear-leveling unit, then
* the wear-leveling unit calculates the data CRC and stores it in the
* @data_crc field. And of course, the @copy_flag is %in this case.
*
* The @data_size field is used only for static volumes because UBI has to know
* how many bytes of data are stored in this eraseblock. For dynamic volumes,
* this field usually contains zero. The only exception is when the data of the
* physical eraseblock was moved to another physical eraseblock for
* wear-leveling reasons. In this case, UBI calculates CRC checksum of the
* contents and uses both @data_crc and @data_size fields. In this case, the
* @data_size field contains data size.
*
* The @used_ebs field is used only for static volumes and indicates how many
* eraseblocks the data of the volume takes. For dynamic volumes this field is
* not used and always contains zero.
*
* The @data_pad is calculated when volumes are created using the alignment
* parameter. So, effectively, the @data_pad field reduces the size of logical
* eraseblocks of this volume. This is very handy when one uses block-oriented
* software (say, cramfs) on top of the UBI volume.
*/
struct ubi_vid_hdr {
ubi32_t magic;
uint8_t version;
uint8_t vol_type;
uint8_t copy_flag;
uint8_t compat;
ubi32_t vol_id;
ubi32_t lnum;
ubi32_t leb_ver; /* obsolete, to be removed, don't use */
ubi32_t data_size;
ubi32_t used_ebs;
ubi32_t data_pad;
ubi32_t data_crc;
uint8_t padding1[4];
ubi64_t sqnum;
uint8_t padding2[12];
ubi32_t hdr_crc;
} __attribute__ ((packed));
/* Internal UBI volumes count */
#define UBI_INT_VOL_COUNT 1
/*
* Starting ID of internal volumes. There is reserved room for 4096 internal
* volumes.
*/
#define UBI_INTERNAL_VOL_START (0x7FFFFFFF - 4096)
/* The layout volume contains the volume table */
#define UBI_LAYOUT_VOL_ID UBI_INTERNAL_VOL_START
#define UBI_LAYOUT_VOLUME_EBS 2
#define UBI_LAYOUT_VOLUME_NAME "layout volume"
#define UBI_LAYOUT_VOLUME_COMPAT UBI_COMPAT_REJECT
/* The maximum number of volumes per one UBI device */
#define UBI_MAX_VOLUMES 128
/* The maximum volume name length */
#define UBI_VOL_NAME_MAX 127
/* Size of the volume table record */
#define UBI_VTBL_RECORD_SIZE sizeof(struct ubi_vtbl_record)
/* Size of the volume table record without the ending CRC */
#define UBI_VTBL_RECORD_SIZE_CRC (UBI_VTBL_RECORD_SIZE - sizeof(ubi32_t))
/**
* struct ubi_vtbl_record - a record in the volume table.
* @reserved_pebs: how many physical eraseblocks are reserved for this volume
* @alignment: volume alignment
* @data_pad: how many bytes are unused at the end of the each physical
* eraseblock to satisfy the requested alignment
* @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
* @upd_marker: if volume update was started but not finished
* @name_len: volume name length
* @name: the volume name
* @padding2: reserved, zeroes
* @crc: a CRC32 checksum of the record
*
* The volume table records are stored in the volume table, which is stored in
* the layout volume. The layout volume consists of 2 logical eraseblock, each
* of which contains a copy of the volume table (i.e., the volume table is
* duplicated). The volume table is an array of &struct ubi_vtbl_record
* objects indexed by the volume ID.
*
* If the size of the logical eraseblock is large enough to fit
* %UBI_MAX_VOLUMES records, the volume table contains %UBI_MAX_VOLUMES
* records. Otherwise, it contains as many records as it can fit (i.e., size of
* logical eraseblock divided by sizeof(struct ubi_vtbl_record)).
*
* The @upd_marker flag is used to implement volume update. It is set to %1
* before update and set to %0 after the update. So if the update operation was
* interrupted, UBI knows that the volume is corrupted.
*
* The @alignment field is specified when the volume is created and cannot be
* later changed. It may be useful, for example, when a block-oriented file
* system works on top of UBI. The @data_pad field is calculated using the
* logical eraseblock size and @alignment. The alignment must be multiple to the
* minimal flash I/O unit. If @alignment is 1, all the available space of
* the physical eraseblocks is used.
*
* Empty records contain all zeroes and the CRC checksum of those zeroes.
*/
struct ubi_vtbl_record {
ubi32_t reserved_pebs;
ubi32_t alignment;
ubi32_t data_pad;
uint8_t vol_type;
uint8_t upd_marker;
ubi16_t name_len;
uint8_t name[UBI_VOL_NAME_MAX+1];
uint8_t padding2[24];
ubi32_t crc;
} __attribute__ ((packed));
#endif /* !__UBI_HEADER_H__ */

View file

@ -0,0 +1,161 @@
/*
* Copyright (c) International Business Machines Corp., 2006
*
* 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
*
* Author: Artem Bityutskiy (Битюцкий Артём)
*/
#ifndef __UBI_USER_H__
#define __UBI_USER_H__
/*
* UBI volume creation
* ~~~~~~~~~~~~~~~~~~~
*
* UBI volumes are created via the %UBI_IOCMKVOL IOCTL command of UBI character
* device. A &struct ubi_mkvol_req object has to be properly filled and a
* pointer to it has to be passed to the IOCTL.
*
* UBI volume deletion
* ~~~~~~~~~~~~~~~~~~~
*
* To delete a volume, the %UBI_IOCRMVOL IOCTL command of the UBI character
* device should be used. A pointer to the 32-bit volume ID hast to be passed
* to the IOCTL.
*
* UBI volume re-size
* ~~~~~~~~~~~~~~~~~~
*
* To re-size a volume, the %UBI_IOCRSVOL IOCTL command of the UBI character
* device should be used. A &struct ubi_rsvol_req object has to be properly
* filled and a pointer to it has to be passed to the IOCTL.
*
* UBI volume update
* ~~~~~~~~~~~~~~~~~
*
* Volume update should be done via the %UBI_IOCVOLUP IOCTL command of the
* corresponding UBI volume character device. A pointer to a 64-bit update
* size should be passed to the IOCTL. After then, UBI expects user to write
* this number of bytes to the volume character device. The update is finished
* when the claimed number of bytes is passed. So, the volume update sequence
* is something like:
*
* fd = open("/dev/my_volume");
* ioctl(fd, UBI_IOCVOLUP, &image_size);
* write(fd, buf, image_size);
* close(fd);
*/
/*
* When a new volume is created, users may either specify the volume number they
* want to create or to let UBI automatically assign a volume number using this
* constant.
*/
#define UBI_VOL_NUM_AUTO (-1)
/* Maximum volume name length */
#define UBI_MAX_VOLUME_NAME 127
/* IOCTL commands of UBI character devices */
#define UBI_IOC_MAGIC 'o'
/* Create an UBI volume */
#define UBI_IOCMKVOL _IOW(UBI_IOC_MAGIC, 0, struct ubi_mkvol_req)
/* Remove an UBI volume */
#define UBI_IOCRMVOL _IOW(UBI_IOC_MAGIC, 1, int32_t)
/* Re-size an UBI volume */
#define UBI_IOCRSVOL _IOW(UBI_IOC_MAGIC, 2, struct ubi_rsvol_req)
/* IOCTL commands of UBI volume character devices */
#define UBI_VOL_IOC_MAGIC 'O'
/* Start UBI volume update */
#define UBI_IOCVOLUP _IOW(UBI_VOL_IOC_MAGIC, 0, int64_t)
/* An eraseblock erasure command, used for debugging, disabled by default */
#define UBI_IOCEBER _IOW(UBI_VOL_IOC_MAGIC, 1, int32_t)
/*
* UBI volume type constants.
*
* @UBI_DYNAMIC_VOLUME: dynamic volume
* @UBI_STATIC_VOLUME: static volume
*/
enum {
UBI_DYNAMIC_VOLUME = 3,
UBI_STATIC_VOLUME = 4
};
/**
* struct ubi_mkvol_req - volume description data structure used in
* volume creation requests.
* @vol_id: volume number
* @alignment: volume alignment
* @bytes: volume size in bytes
* @vol_type: volume type (%UBI_DYNAMIC_VOLUME or %UBI_STATIC_VOLUME)
* @padding1: reserved for future, not used
* @name_len: volume name length
* @padding2: reserved for future, not used
* @name: volume name
*
* This structure is used by userspace programs when creating new volumes. The
* @used_bytes field is only necessary when creating static volumes.
*
* The @alignment field specifies the required alignment of the volume logical
* eraseblock. This means, that the size of logical eraseblocks will be aligned
* to this number, i.e.,
* (UBI device logical eraseblock size) mod (@alignment) = 0.
*
* To put it differently, the logical eraseblock of this volume may be slightly
* shortened in order to make it properly aligned. The alignment has to be
* multiple of the flash minimal input/output unit, or %1 to utilize the entire
* available space of logical eraseblocks.
*
* The @alignment field may be useful, for example, when one wants to maintain
* a block device on top of an UBI volume. In this case, it is desirable to fit
* an integer number of blocks in logical eraseblocks of this UBI volume. With
* alignment it is possible to update this volume using plane UBI volume image
* BLOBs, without caring about how to properly align them.
*/
struct ubi_mkvol_req {
int32_t vol_id;
int32_t alignment;
int64_t bytes;
int8_t vol_type;
int8_t padding1;
int16_t name_len;
int8_t padding2[4];
char name[UBI_MAX_VOLUME_NAME+1];
} __attribute__ ((packed));
/**
* struct ubi_rsvol_req - a data structure used in volume re-size requests.
* @vol_id: ID of the volume to re-size
* @bytes: new size of the volume in bytes
*
* Re-sizing is possible for both dynamic and static volumes. But while dynamic
* volumes may be re-sized arbitrarily, static volumes cannot be made to be
* smaller then the number of bytes they bear. To arbitrarily shrink a static
* volume, it must be wiped out first (by means of volume update operation with
* zero number of bytes).
*/
struct ubi_rsvol_req {
int64_t bytes;
int32_t vol_id;
} __attribute__ ((packed));
#endif /* __UBI_USER_H__ */

View file

@ -84,6 +84,7 @@ struct nand_write_options {
}; };
typedef struct nand_write_options nand_write_options_t; typedef struct nand_write_options nand_write_options_t;
typedef struct mtd_oob_ops mtd_oob_ops_t;
struct nand_read_options { struct nand_read_options {
u_char *buffer; /* memory block in which read image is written*/ u_char *buffer; /* memory block in which read image is written*/
@ -107,7 +108,7 @@ struct nand_erase_options {
typedef struct nand_erase_options nand_erase_options_t; typedef struct nand_erase_options nand_erase_options_t;
int nand_write_opts(nand_info_t *meminfo, const nand_write_options_t *opts); int nand_write_opts(nand_info_t *mtd, loff_t to, mtd_oob_ops_t *ops);
int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts); int nand_read_opts(nand_info_t *meminfo, const nand_read_options_t *opts);
int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts); int nand_erase_opts(nand_info_t *meminfo, const nand_erase_options_t *opts);