Commit 987817ef authored by David Woodhouse's avatar David Woodhouse

NAND flash driver updates.

Update the core NAND code:
 - support multiple chips
 - support bad block tables
 - improved generic ECC support and 'spare area' usage.
 - 16-bit NAND
 - Large-block NAND devices
 - Renesas AG-AND devices
 - M-Systems DiskOnChip devices
 - Other new board support wrappers

Most of the work was done by Thomas Gleixner.
Signed-Off-By: default avatarDavid Woodhouse <dwmw2@infradead.org>
parent 29b6d1fb
...@@ -1090,6 +1090,10 @@ E: jbglaw@lug-owl.de ...@@ -1090,6 +1090,10 @@ E: jbglaw@lug-owl.de
D: SRM environment driver (for Alpha systems) D: SRM environment driver (for Alpha systems)
P: 1024D/8399E1BB 250D 3BCF 7127 0D8C A444 A961 1DBD 5E75 8399 E1BB P: 1024D/8399E1BB 250D 3BCF 7127 0D8C A444 A961 1DBD 5E75 8399 E1BB
N: Thomas Gleixner
E: tglx@linutronix.de
D: NAND flash hardware support, JFFS2 on NAND flash
N: Richard E. Gooch N: Richard E. Gooch
E: rgooch@atnf.csiro.au E: rgooch@atnf.csiro.au
D: parent process death signal to children D: parent process death signal to children
......
...@@ -5,7 +5,7 @@ ...@@ -5,7 +5,7 @@
* *
* This code is GPL * This code is GPL
* *
* $Id: mtdpart.c,v 1.41 2003/06/18 14:53:02 dwmw2 Exp $ * $Id: mtdpart.c,v 1.46 2004/07/12 13:28:07 dwmw2 Exp $
* *
* 02-21-2002 Thomas Gleixner <gleixner@autronix.de> * 02-21-2002 Thomas Gleixner <gleixner@autronix.de>
* added support for read_oob, write_oob * added support for read_oob, write_oob
...@@ -239,12 +239,16 @@ static int part_readv_ecc (struct mtd_info *mtd, struct kvec *vecs, ...@@ -239,12 +239,16 @@ static int part_readv_ecc (struct mtd_info *mtd, struct kvec *vecs,
static int part_erase (struct mtd_info *mtd, struct erase_info *instr) static int part_erase (struct mtd_info *mtd, struct erase_info *instr)
{ {
struct mtd_part *part = PART(mtd); struct mtd_part *part = PART(mtd);
int ret;
if (!(mtd->flags & MTD_WRITEABLE)) if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS; return -EROFS;
if (instr->addr >= mtd->size) if (instr->addr >= mtd->size)
return -EINVAL; return -EINVAL;
instr->addr += part->offset; instr->addr += part->offset;
return part->master->erase(part->master, instr); ret = part->master->erase(part->master, instr);
if (instr->fail_addr != 0xffffffff)
instr->fail_addr -= part->offset;
return ret;
} }
static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len) static int part_lock (struct mtd_info *mtd, loff_t ofs, size_t len)
...@@ -281,6 +285,26 @@ static void part_resume(struct mtd_info *mtd) ...@@ -281,6 +285,26 @@ static void part_resume(struct mtd_info *mtd)
part->master->resume(part->master); part->master->resume(part->master);
} }
static int part_block_isbad (struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = PART(mtd);
if (ofs >= mtd->size)
return -EINVAL;
ofs += part->offset;
return part->master->block_isbad(part->master, ofs);
}
static int part_block_markbad (struct mtd_info *mtd, loff_t ofs)
{
struct mtd_part *part = PART(mtd);
if (!(mtd->flags & MTD_WRITEABLE))
return -EROFS;
if (ofs >= mtd->size)
return -EINVAL;
ofs += part->offset;
return part->master->block_markbad(part->master, ofs);
}
/* /*
* This function unregisters and destroy all slave MTD objects which are * This function unregisters and destroy all slave MTD objects which are
* attached to the given master MTD object. * attached to the given master MTD object.
...@@ -316,7 +340,7 @@ int del_mtd_partitions(struct mtd_info *master) ...@@ -316,7 +340,7 @@ int del_mtd_partitions(struct mtd_info *master)
*/ */
int add_mtd_partitions(struct mtd_info *master, int add_mtd_partitions(struct mtd_info *master,
struct mtd_partition *parts, const struct mtd_partition *parts,
int nbparts) int nbparts)
{ {
struct mtd_part *slave; struct mtd_part *slave;
...@@ -391,6 +415,10 @@ int add_mtd_partitions(struct mtd_info *master, ...@@ -391,6 +415,10 @@ int add_mtd_partitions(struct mtd_info *master,
slave->mtd.lock = part_lock; slave->mtd.lock = part_lock;
if (master->unlock) if (master->unlock)
slave->mtd.unlock = part_unlock; slave->mtd.unlock = part_unlock;
if (master->block_isbad)
slave->mtd.block_isbad = part_block_isbad;
if (master->block_markbad)
slave->mtd.block_markbad = part_block_markbad;
slave->mtd.erase = part_erase; slave->mtd.erase = part_erase;
slave->master = master; slave->master = master;
slave->offset = parts[i].offset; slave->offset = parts[i].offset;
...@@ -461,6 +489,9 @@ int add_mtd_partitions(struct mtd_info *master, ...@@ -461,6 +489,9 @@ int add_mtd_partitions(struct mtd_info *master,
parts[i].name); parts[i].name);
} }
/* copy oobinfo from master */
memcpy(&slave->mtd.oobinfo, &master->oobinfo, sizeof(slave->mtd.oobinfo));
if(parts[i].mtdp) if(parts[i].mtdp)
{ /* store the object pointer (caller may or may not register it */ { /* store the object pointer (caller may or may not register it */
*parts[i].mtdp = &slave->mtd; *parts[i].mtdp = &slave->mtd;
......
# drivers/mtd/nand/Kconfig # drivers/mtd/nand/Kconfig
# $Id: Kconfig,v 1.4 2003/05/28 10:04:23 dwmw2 Exp $ # $Id: Kconfig,v 1.14 2004/07/13 00:14:35 dbrown Exp $
menu "NAND Flash Device Drivers" menu "NAND Flash Device Drivers"
depends on MTD!=n depends on MTD!=n
...@@ -9,7 +9,7 @@ config MTD_NAND ...@@ -9,7 +9,7 @@ config MTD_NAND
depends on MTD depends on MTD
help help
This enables support for accessing all type of NAND flash This enables support for accessing all type of NAND flash
devices with an 8-bit data bus interface. For further information see devices. For further information see
<http://www.linux-mtd.infradead.org/tech/nand.html>. <http://www.linux-mtd.infradead.org/tech/nand.html>.
config MTD_NAND_VERIFY_WRITE config MTD_NAND_VERIFY_WRITE
...@@ -42,10 +42,73 @@ config MTD_NAND_SPIA ...@@ -42,10 +42,73 @@ config MTD_NAND_SPIA
help help
If you had to ask, you don't have one. Say 'N'. If you had to ask, you don't have one. Say 'N'.
config MTD_NAND_TOTO
tristate "NAND Flash device on TOTO board"
depends on ARM && ARCH_OMAP && MTD_NAND
help
Support for NAND flash on Texas Instruments Toto platform.
config MTD_NAND_IDS config MTD_NAND_IDS
tristate tristate
default y if MTD_NAND = y || MTD_DOC2000 = y || MTD_DOC2001 = y || MTD_DOC2001PLUS = y default y if MTD_NAND = y || MTD_DOC2000 = y || MTD_DOC2001 = y || MTD_DOC2001PLUS = y
default m if MTD_NAND = m || MTD_DOC2000 = m || MTD_DOC2001 = m || MTD_DOC2001PLUS = m default m if MTD_NAND = m || MTD_DOC2000 = m || MTD_DOC2001 = m || MTD_DOC2001PLUS = m
endmenu
config MTD_NAND_TX4925NDFMC
tristate "SmartMedia Card on Toshiba RBTX4925 reference board"
depends on TOSHIBA_RBTX4925 && MTD_NAND && TOSHIBA_RBTX4925_MPLEX_NAND
help
This enables the driver for the NAND flash device found on the
Toshiba RBTX4925 reference board, which is a SmartMediaCard.
config MTD_NAND_TX4938NDFMC
tristate "NAND Flash device on Toshiba RBTX4938 reference board"
depends on TOSHIBA_RBTX4938 && MTD_NAND && TOSHIBA_RBTX4938_MPLEX_NAND
help
This enables the driver for the NAND flash device found on the
Toshiba RBTX4938 reference board.
config MTD_NAND_AU1550
tristate "Au1550 NAND support"
depends on SOC_AU1550 && MTD_NAND
help
This enables the driver for the NAND flash controller on the
AMD/Alchemy 1550 SOC.
config MTD_NAND_PPCHAMELEONEVB
tristate "NAND Flash device on PPChameleonEVB board"
depends on PPCHAMELEONEVB && MTD_NAND
help
This enables the NAND flash driver on the PPChameleon EVB Board.
config MTD_NAND_DISKONCHIP
tristate "DiskOnChip 2000 and Millennium (NAND reimplementation) (EXPERIMENTAL)"
depends on MTD_NAND && EXPERIMENTAL
help
This is a reimplementation of M-Systems DiskOnChip 2000 and
Millennium as a standard NAND device driver, as opposed to the
earlier self-contained MTD device drivers.
This should enable, among other things, proper JFFS2 operation on
these devices.
config MTD_NAND_DISKONCHIP_BBTWRITE
bool "Allow BBT writes on DiskOnChip Millennium and 2000TSOP"
depends on MTD_NAND_DISKONCHIP
help
On DiskOnChip devices shipped with the INFTL filesystem (Millennium
and 2000 TSOP/Alon), Linux reserves some space at the end of the
device for the Bad Block Table (BBT). If you have existing INFTL
data on your device (created by non-Linux tools such as M-Systems'
DOS drivers), your data might overlap the area Linux wants to use for
the BBT. If this is a concern for you, leave this option disabled and
Linux will not write BBT data into this area.
The downside of leaving this option disabled is that if bad blocks
are detected by Linux, they will not be recorded in the BBT, which
could cause future problems.
Once you enable this option, new filesystems (INFTL or others, created
in Linux or other operating systems) will not use the reserved area.
The only reason not to enable this option is to prevent damage to
preexisting filesystems.
Even if you leave this disabled, you can enable BBT writes at module
load time (assuming you build diskonchip as a module) with the module
parameter "inftl_bbt_write=1".
endmenu
# #
# linux/drivers/nand/Makefile # linux/drivers/nand/Makefile
# #
# $Id: Makefile.common,v 1.2 2003/05/28 11:38:54 dwmw2 Exp $ # $Id: Makefile.common,v 1.9 2004/07/12 16:07:31 dwmw2 Exp $
obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o
obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o
obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o obj-$(CONFIG_MTD_NAND_TOTO) += toto.o
obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o
obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o
obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o
obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o
obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o
obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB) += ppchameleonevb.o
obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o
nand-objs = nand_base.o nand_bbt.o
/*
* drivers/mtd/nand/au1550nd.c
*
* Copyright (C) 2004 Embedded Edge, LLC
*
* $Id: au1550nd.c,v 1.5 2004/05/17 07:19:35 ppopov Exp $
*
* 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
* published by the Free Software Foundation.
*
*/
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/au1000.h>
#ifdef CONFIG_MIPS_PB1550
#include <asm/pb1550.h>
#endif
#ifdef CONFIG_MIPS_DB1550
#include <asm/db1x00.h>
#endif
/*
* MTD structure for NAND controller
*/
static struct mtd_info *au1550_mtd = NULL;
static volatile u32 p_nand;
static int nand_width = 1; /* default, only x8 supported for now */
/* Internal buffers. Page buffer and oob buffer for one block*/
static u_char data_buf[512 + 16];
static u_char oob_buf[16 * 32];
/*
* Define partitions for flash device
*/
const static struct mtd_partition partition_info[] = {
#ifdef CONFIG_MIPS_PB1550
#define NUM_PARTITIONS 2
{
.name = "Pb1550 NAND FS 0",
.offset = 0,
.size = 8*1024*1024
},
{
.name = "Pb1550 NAND FS 1",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL
}
#endif
#ifdef CONFIG_MIPS_DB1550
#define NUM_PARTITIONS 2
{
.name = "Db1550 NAND FS 0",
.offset = 0,
.size = 8*1024*1024
},
{
.name = "Db1550 NAND FS 1",
.offset = MTDPART_OFS_APPEND,
.size = MTDPART_SIZ_FULL
}
#endif
};
static inline void write_cmd_reg(u8 cmd)
{
if (nand_width)
*((volatile u8 *)(p_nand + MEM_STNAND_CMD)) = cmd;
else
*((volatile u16 *)(p_nand + MEM_STNAND_CMD)) = cmd;
au_sync();
}
static inline void write_addr_reg(u8 addr)
{
if (nand_width)
*((volatile u8 *)(p_nand + MEM_STNAND_ADDR)) = addr;
else
*((volatile u16 *)(p_nand + MEM_STNAND_ADDR)) = addr;
au_sync();
}
static inline void write_data_reg(u8 data)
{
if (nand_width)
*((volatile u8 *)(p_nand + MEM_STNAND_DATA)) = data;
else
*((volatile u16 *)(p_nand + MEM_STNAND_DATA)) = data;
au_sync();
}
static inline u32 read_data_reg(void)
{
u32 data;
if (nand_width) {
data = *((volatile u8 *)(p_nand + MEM_STNAND_DATA));
au_sync();
}
else {
data = *((volatile u16 *)(p_nand + MEM_STNAND_DATA));
au_sync();
}
return data;
}
void au1550_hwcontrol(struct mtd_info *mtd, int cmd)
{
}
int au1550_device_ready(struct mtd_info *mtd)
{
int ready;
ready = (au_readl(MEM_STSTAT) & 0x1) ? 1 : 0;
return ready;
}
static u_char au1550_nand_read_byte(struct mtd_info *mtd)
{
u_char ret;
ret = read_data_reg();
return ret;
}
static void au1550_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
write_data_reg((u8)byte);
}
static void
au1550_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
for (i=0; i<len; i++)
write_data_reg(buf[i]);
}
static void
au1550_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
for (i=0; i<len; i++)
buf[i] = (u_char)read_data_reg();
}
static int
au1550_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
for (i=0; i<len; i++)
if (buf[i] != (u_char)read_data_reg())
return -EFAULT;
return 0;
}
static void au1550_nand_select_chip(struct mtd_info *mtd, int chip)
{
switch(chip) {
case -1:
/* deassert chip enable */
au_writel(au_readl(MEM_STNDCTL) & ~0x20 , MEM_STNDCTL);
break;
case 0:
/* assert (force assert) chip enable */
au_writel(au_readl(MEM_STNDCTL) | 0x20 , MEM_STNDCTL);
break;
default:
BUG();
}
}
static void au1550_nand_command (struct mtd_info *mtd, unsigned command,
int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
/*
* Write out the command to the device.
*/
if (command == NAND_CMD_SEQIN) {
int readcmd;
if (column >= mtd->oobblock) {
/* OOB area */
column -= mtd->oobblock;
readcmd = NAND_CMD_READOOB;
} else if (column < 256) {
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0;
} else {
column -= 256;
readcmd = NAND_CMD_READ1;
}
write_cmd_reg(readcmd);
}
write_cmd_reg(command);
if (column != -1 || page_addr != -1) {
/* Serially input address */
if (column != -1)
write_addr_reg(column);
if (page_addr != -1) {
write_addr_reg((unsigned char) (page_addr & 0xff));
write_addr_reg(((page_addr >> 8) & 0xff));
/* One more address cycle for higher density devices */
if (mtd->size & 0x0c000000)
write_addr_reg((unsigned char) ((page_addr >> 16) & 0x0f));
}
}
switch (command) {
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_STATUS:
break;
case NAND_CMD_RESET:
if (this->dev_ready)
break;
udelay(this->chip_delay);
write_cmd_reg(NAND_CMD_STATUS);
while ( !(read_data_reg() & 0x40));
return;
/* This applies to read commands */
default:
udelay (this->chip_delay);
}
/* wait until command is processed */
while (!this->dev_ready(mtd));
}
/*
* Main initialization routine
*/
int __init au1550_init (void)
{
struct nand_chip *this;
u16 boot_swapboot = 0; /* default value */
u32 mem_time;
/* Allocate memory for MTD device structure and private data */
au1550_mtd = kmalloc (sizeof(struct mtd_info) +
sizeof (struct nand_chip), GFP_KERNEL);
if (!au1550_mtd) {
printk ("Unable to allocate NAND MTD dev structure.\n");
return -ENOMEM;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&au1550_mtd[1]);
/* Initialize structures */
memset((char *) au1550_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
au1550_mtd->priv = this;
/* disable interrupts */
au_writel(au_readl(MEM_STNDCTL) & ~(1<<8), MEM_STNDCTL);
/* disable NAND boot */
au_writel(au_readl(MEM_STNDCTL) & ~(1<<0), MEM_STNDCTL);
#ifdef CONFIG_MIPS_PB1550
/* set gpio206 high */
au_writel(au_readl(GPIO2_DIR) & ~(1<<6), GPIO2_DIR);
boot_swapboot = (au_readl(MEM_STSTAT) & (0x7<<1)) |
((bcsr->status >> 6) & 0x1);
switch (boot_swapboot) {
case 0:
case 2:
case 8:
case 0xC:
case 0xD:
/* x16 NAND Flash */
nand_width = 0;
printk("Pb1550 NAND: 16-bit NAND not supported by MTD\n");
break;
case 1:
case 9:
case 3:
case 0xE:
case 0xF:
/* x8 NAND Flash */
nand_width = 1;
break;
default:
printk("Pb1550 NAND: bad boot:swap\n");
kfree(au1550_mtd);
return 1;
}
/* Configure RCE1 - should be done by YAMON */
au_writel(0x5 | (nand_width << 22), MEM_STCFG1);
au_writel(NAND_TIMING, MEM_STTIME1);
mem_time = au_readl(MEM_STTIME1);
au_sync();
/* setup and enable chip select */
/* we really need to decode offsets only up till 0x20 */
au_writel((1<<28) | (NAND_PHYS_ADDR>>4) |
(((NAND_PHYS_ADDR + 0x1000)-1) & (0x3fff<<18)>>18),
MEM_STADDR1);
au_sync();
#endif
#ifdef CONFIG_MIPS_DB1550
/* Configure RCE1 - should be done by YAMON */
au_writel(0x00400005, MEM_STCFG1);
au_writel(0x00007774, MEM_STTIME1);
au_writel(0x12000FFF, MEM_STADDR1);
#endif
p_nand = (volatile struct nand_regs *)ioremap(NAND_PHYS_ADDR, 0x1000);
/* Set address of hardware control function */
this->hwcontrol = au1550_hwcontrol;
this->dev_ready = au1550_device_ready;
/* 30 us command delay time */
this->chip_delay = 30;
this->cmdfunc = au1550_nand_command;
this->select_chip = au1550_nand_select_chip;
this->write_byte = au1550_nand_write_byte;
this->read_byte = au1550_nand_read_byte;
this->write_buf = au1550_nand_write_buf;
this->read_buf = au1550_nand_read_buf;
this->verify_buf = au1550_nand_verify_buf;
this->eccmode = NAND_ECC_SOFT;
/* Set internal data buffer */
this->data_buf = data_buf;
this->oob_buf = oob_buf;
/* Scan to find existence of the device */
if (nand_scan (au1550_mtd, 1)) {
kfree (au1550_mtd);
return -ENXIO;
}
/* Register the partitions */
add_mtd_partitions(au1550_mtd, partition_info, NUM_PARTITIONS);
return 0;
}
module_init(au1550_init);
/*
* Clean up routine
*/
#ifdef MODULE
static void __exit au1550_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &au1550_mtd[1];
iounmap ((void *)p_nand);
/* Unregister partitions */
del_mtd_partitions(au1550_mtd);
/* Unregister the device */
del_mtd_device (au1550_mtd);
/* Free the MTD device structure */
kfree (au1550_mtd);
}
module_exit(au1550_cleanup);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Embedded Edge, LLC");
MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on Pb1550 board");
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Derived from drivers/mtd/spia.c * Derived from drivers/mtd/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
* *
* $Id: autcpu12.c,v 1.11 2003/06/04 17:04:09 gleixner Exp $ * $Id: autcpu12.c,v 1.19 2004/07/12 15:02:15 dwmw2 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,7 +15,7 @@ ...@@ -15,7 +15,7 @@
* Overview: * Overview:
* This is a device driver for the NAND flash device found on the * This is a device driver for the NAND flash device found on the
* autronix autcpu12 board, which is a SmartMediaCard. It supports * autronix autcpu12 board, which is a SmartMediaCard. It supports
* 16MB, 32MB and 64MB cards. * 16MiB, 32MiB and 64MiB cards.
* *
* *
* 02-12-2002 TG Cleanup of module params * 02-12-2002 TG Cleanup of module params
...@@ -71,42 +71,40 @@ __setup("autcpu12_pedr=",autcpu12_pedr); ...@@ -71,42 +71,40 @@ __setup("autcpu12_pedr=",autcpu12_pedr);
/* /*
* Define partitions for flash devices * Define partitions for flash devices
*/ */
extern struct nand_oobinfo jffs2_oobinfo;
static struct mtd_partition partition_info16k[] = { static struct mtd_partition partition_info16k[] = {
{ .name = "AUTCPU12 flash partition 1", { .name = "AUTCPU12 flash partition 1",
.offset = 0, .offset = 0,
.size = 8 * SZ_1M }, .size = 8 * SZ_1M },
{ .name = "AUTCPU12 flash partition 2", { .name = "AUTCPU12 flash partition 2",
.offset = 8 * SZ_1M, .offset = 8 * SZ_1M,
.size = 8 * SZ_1M }, .size = 8 * SZ_1M },
}; };
static struct mtd_partition partition_info32k[] = { static struct mtd_partition partition_info32k[] = {
{ .name = "AUTCPU12 flash partition 1", { .name = "AUTCPU12 flash partition 1",
.offset = 0, .offset = 0,
.size = 8 * SZ_1M }, .size = 8 * SZ_1M },
{ .name = "AUTCPU12 flash partition 2", { .name = "AUTCPU12 flash partition 2",
.offset = 8 * SZ_1M, .offset = 8 * SZ_1M,
.size = 24 * SZ_1M }, .size = 24 * SZ_1M },
}; };
static struct mtd_partition partition_info64k[] = { static struct mtd_partition partition_info64k[] = {
{ .name = "AUTCPU12 flash partition 1", { .name = "AUTCPU12 flash partition 1",
.offset = 0, .offset = 0,
.size = 16 * SZ_1M }, .size = 16 * SZ_1M },
{ .name = "AUTCPU12 flash partition 2", { .name = "AUTCPU12 flash partition 2",
.offset = 16 * SZ_1M, .offset = 16 * SZ_1M,
.size = 48 * SZ_1M }, .size = 48 * SZ_1M },
}; };
static struct mtd_partition partition_info128k[] = { static struct mtd_partition partition_info128k[] = {
{ .name = "AUTCPU12 flash partition 1", { .name = "AUTCPU12 flash partition 1",
.offset = 0, .offset = 0,
.size = 16 * SZ_1M }, .size = 16 * SZ_1M },
{ .name = "AUTCPU12 flash partition 2", { .name = "AUTCPU12 flash partition 2",
.offset = 16 * SZ_1M, .offset = 16 * SZ_1M,
.size = 112 * SZ_1M }, .size = 112 * SZ_1M },
}; };
#define NUM_PARTITIONS16K 2 #define NUM_PARTITIONS16K 2
...@@ -116,7 +114,7 @@ static struct mtd_partition partition_info128k[] = { ...@@ -116,7 +114,7 @@ static struct mtd_partition partition_info128k[] = {
/* /*
* hardware specific access to control-lines * hardware specific access to control-lines
*/ */
void autcpu12_hwcontrol(int cmd) static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd)
{ {
switch(cmd){ switch(cmd){
...@@ -135,12 +133,13 @@ void autcpu12_hwcontrol(int cmd) ...@@ -135,12 +133,13 @@ void autcpu12_hwcontrol(int cmd)
/* /*
* read device ready pin * read device ready pin
*/ */
int autcpu12_device_ready(void) int autcpu12_device_ready(struct mtd_info *mtd)
{ {
return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0; return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0;
} }
/* /*
* Main initialization routine * Main initialization routine
*/ */
...@@ -185,20 +184,18 @@ int __init autcpu12_init (void) ...@@ -185,20 +184,18 @@ int __init autcpu12_init (void)
this->chip_delay = 20; this->chip_delay = 20;
this->eccmode = NAND_ECC_SOFT; this->eccmode = NAND_ECC_SOFT;
/* Enable the following for a flash based bad block table */
/*
this->options = NAND_USE_FLASH_BBT;
*/
this->options = NAND_USE_FLASH_BBT;
/* Scan to find existance of the device */ /* Scan to find existance of the device */
if (nand_scan (autcpu12_mtd)) { if (nand_scan (autcpu12_mtd, 1)) {
err = -ENXIO; err = -ENXIO;
goto out_ior; goto out_ior;
} }
/* Allocate memory for internal data buffer */
this->data_buf = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL);
if (!this->data_buf) {
printk ("Unable to allocate NAND data buffer for AUTCPU12.\n");
err = -ENOMEM;
goto out_ior;
}
/* Register the partitions */ /* Register the partitions */
switch(autcpu12_mtd->size){ switch(autcpu12_mtd->size){
case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break; case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break;
...@@ -208,13 +205,11 @@ int __init autcpu12_init (void) ...@@ -208,13 +205,11 @@ int __init autcpu12_init (void)
default: { default: {
printk ("Unsupported SmartMedia device\n"); printk ("Unsupported SmartMedia device\n");
err = -ENXIO; err = -ENXIO;
goto out_buf; goto out_ior;
} }
} }
goto out; goto out;
out_buf:
kfree (this->data_buf);
out_ior: out_ior:
iounmap((void *)autcpu12_fio_base); iounmap((void *)autcpu12_fio_base);
out_mtd: out_mtd:
...@@ -231,20 +226,12 @@ module_init(autcpu12_init); ...@@ -231,20 +226,12 @@ module_init(autcpu12_init);
#ifdef MODULE #ifdef MODULE
static void __exit autcpu12_cleanup (void) static void __exit autcpu12_cleanup (void)
{ {
struct nand_chip *this = (struct nand_chip *) &autcpu12_mtd[1]; /* Release resources, unregister device */
nand_release (autcpu12_mtd);
/* Unregister partitions */
del_mtd_partitions(autcpu12_mtd);
/* Unregister the device */
del_mtd_device (autcpu12_mtd);
/* Free internal data buffers */
kfree (this->data_buf);
/* unmap physical adress */ /* unmap physical adress */
iounmap((void *)autcpu12_fio_base); iounmap((void *)autcpu12_fio_base);
/* Free the MTD device structure */ /* Free the MTD device structure */
kfree (autcpu12_mtd); kfree (autcpu12_mtd);
} }
......
/*
* drivers/mtd/nand/diskonchip.c
*
* (C) 2003 Red Hat, Inc.
*
* Author: David Woodhouse <dwmw2@infradead.org>
*
* Interface to generic NAND code for M-Systems DiskOnChip devices
*
* $Id: diskonchip.c,v 1.23 2004/07/13 00:14:35 dbrown Exp $
*/
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/doc2000.h>
#include <linux/mtd/compatmac.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/inftl.h>
/* Where to look for the devices? */
#ifndef CONFIG_MTD_DOCPROBE_ADDRESS
#define CONFIG_MTD_DOCPROBE_ADDRESS 0
#endif
static unsigned long __initdata doc_locations[] = {
#if defined (__alpha__) || defined(__i386__) || defined(__x86_64__)
#ifdef CONFIG_MTD_DOCPROBE_HIGH
0xfffc8000, 0xfffca000, 0xfffcc000, 0xfffce000,
0xfffd0000, 0xfffd2000, 0xfffd4000, 0xfffd6000,
0xfffd8000, 0xfffda000, 0xfffdc000, 0xfffde000,
0xfffe0000, 0xfffe2000, 0xfffe4000, 0xfffe6000,
0xfffe8000, 0xfffea000, 0xfffec000, 0xfffee000,
#else /* CONFIG_MTD_DOCPROBE_HIGH */
0xc8000, 0xca000, 0xcc000, 0xce000,
0xd0000, 0xd2000, 0xd4000, 0xd6000,
0xd8000, 0xda000, 0xdc000, 0xde000,
0xe0000, 0xe2000, 0xe4000, 0xe6000,
0xe8000, 0xea000, 0xec000, 0xee000,
#endif /* CONFIG_MTD_DOCPROBE_HIGH */
#elif defined(__PPC__)
0xe4000000,
#elif defined(CONFIG_MOMENCO_OCELOT)
0x2f000000,
0xff000000,
#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C)
0xff000000,
##else
#warning Unknown architecture for DiskOnChip. No default probe locations defined
#endif
0xffffffff };
static struct mtd_info *doclist = NULL;
struct doc_priv {
unsigned long virtadr;
unsigned long physadr;
u_char ChipID;
u_char CDSNControl;
int chips_per_floor; /* The number of chips detected on each floor */
int curfloor;
int curchip;
int mh0_page;
int mh1_page;
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
page, one with all 0xff for data and stored ecc code. */
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
page, one with all 0xff for data. */
static u_char empty_write_ecc[6] = { 0x4b, 0x00, 0xe2, 0x0e, 0x93, 0xf7 };
#define INFTL_BBT_RESERVED_BLOCKS 4
#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil)
#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k)
static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd);
static void doc200x_select_chip(struct mtd_info *mtd, int chip);
static int debug=0;
MODULE_PARM(debug, "i");
static int try_dword=1;
MODULE_PARM(try_dword, "i");
static int no_ecc_failures=0;
MODULE_PARM(no_ecc_failures, "i");
static int no_autopart=0;
MODULE_PARM(no_autopart, "i");
#ifdef MTD_NAND_DISKONCHIP_BBTWRITE
static int inftl_bbt_write=1;
#else
static int inftl_bbt_write=0;
#endif
MODULE_PARM(inftl_bbt_write, "i");
static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS;
MODULE_PARM(doc_config_location, "l");
MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip");
static void DoC_Delay(struct doc_priv *doc, unsigned short cycles)
{
volatile char dummy;
int i;
for (i = 0; i < cycles; i++) {
if (DoC_is_Millennium(doc))
dummy = ReadDOC(doc->virtadr, NOP);
else
dummy = ReadDOC(doc->virtadr, DOCStatus);
}
}
/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */
static int _DoC_WaitReady(struct doc_priv *doc)
{
unsigned long docptr = doc->virtadr;
unsigned long timeo = jiffies + (HZ * 10);
if(debug) printk("_DoC_WaitReady...\n");
/* Out-of-line routine to wait for chip response */
while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
if (time_after(jiffies, timeo)) {
printk("_DoC_WaitReady timed out.\n");
return -EIO;
}
udelay(1);
cond_resched();
}
return 0;
}
static inline int DoC_WaitReady(struct doc_priv *doc)
{
unsigned long docptr = doc->virtadr;
int ret = 0;
DoC_Delay(doc, 4);
if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B))
/* Call the out-of-line routine to wait */
ret = _DoC_WaitReady(doc);
DoC_Delay(doc, 2);
if(debug) printk("DoC_WaitReady OK\n");
return ret;
}
static void doc2000_write_byte(struct mtd_info *mtd, u_char datum)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
if(debug)printk("write_byte %02x\n", datum);
WriteDOC(datum, docptr, CDSNSlowIO);
WriteDOC(datum, docptr, 2k_CDSN_IO);
}
static u_char doc2000_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
u_char ret;
ReadDOC(docptr, CDSNSlowIO);
DoC_Delay(doc, 2);
ret = ReadDOC(docptr, 2k_CDSN_IO);
if (debug) printk("read_byte returns %02x\n", ret);
return ret;
}
static void doc2000_writebuf(struct mtd_info *mtd,
const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int i;
if (debug)printk("writebuf of %d bytes: ", len);
for (i=0; i < len; i++) {
WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i);
if (debug && i < 16)
printk("%02x ", buf[i]);
}
if (debug) printk("\n");
}
static void doc2000_readbuf(struct mtd_info *mtd,
u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int i;
if (debug)printk("readbuf of %d bytes: ", len);
for (i=0; i < len; i++) {
buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i);
}
}
static void doc2000_readbuf_dword(struct mtd_info *mtd,
u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int i;
if (debug) printk("readbuf_dword of %d bytes: ", len);
if (unlikely((((unsigned long)buf)|len) & 3)) {
for (i=0; i < len; i++) {
*(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i);
}
} else {
for (i=0; i < len; i+=4) {
*(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i);
}
}
}
static int doc2000_verifybuf(struct mtd_info *mtd,
const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int i;
for (i=0; i < len; i++)
if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO))
return -EFAULT;
return 0;
}
static uint16_t __init doc200x_ident_chip(struct mtd_info *mtd, int nr)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
uint16_t ret;
doc200x_select_chip(mtd, nr);
doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_READID);
doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
this->write_byte(mtd, 0);
doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
ret = this->read_byte(mtd) << 8;
ret |= this->read_byte(mtd);
if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) {
/* First chip probe. See if we get same results by 32-bit access */
union {
uint32_t dword;
uint8_t byte[4];
} ident;
unsigned long docptr = doc->virtadr;
doc200x_hwcontrol(mtd, NAND_CTL_SETCLE);
doc2000_write_byte(mtd, NAND_CMD_READID);
doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE);
doc200x_hwcontrol(mtd, NAND_CTL_SETALE);
doc2000_write_byte(mtd, 0);
doc200x_hwcontrol(mtd, NAND_CTL_CLRALE);
ident.dword = readl(docptr + DoC_2k_CDSN_IO);
if (((ident.byte[0] << 8) | ident.byte[1]) == ret) {
printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n");
this->read_buf = &doc2000_readbuf_dword;
}
}
return ret;
}
static void __init doc2000_count_chips(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
uint16_t mfrid;
int i;
/* Max 4 chips per floor on DiskOnChip 2000 */
doc->chips_per_floor = 4;
/* Find out what the first chip is */
mfrid = doc200x_ident_chip(mtd, 0);
/* Find how many chips in each floor. */
for (i = 1; i < 4; i++) {
if (doc200x_ident_chip(mtd, i) != mfrid)
break;
}
doc->chips_per_floor = 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)
{
struct doc_priv *doc = (void *)this->priv;
int status;
DoC_WaitReady(doc);
this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1);
DoC_WaitReady(doc);
status = (int)this->read_byte(mtd);
return status;
}
static void doc2001_write_byte(struct mtd_info *mtd, u_char datum)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
WriteDOC(datum, docptr, CDSNSlowIO);
WriteDOC(datum, docptr, Mil_CDSN_IO);
WriteDOC(datum, docptr, WritePipeTerm);
}
static u_char doc2001_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
//ReadDOC(docptr, CDSNSlowIO);
/* 11.4.5 -- delay twice to allow extended length cycle */
DoC_Delay(doc, 2);
ReadDOC(docptr, ReadPipeInit);
//return ReadDOC(docptr, Mil_CDSN_IO);
return ReadDOC(docptr, LastDataRead);
}
static void doc2001_writebuf(struct mtd_info *mtd,
const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int i;
for (i=0; i < len; i++)
WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i);
/* Terminate write pipeline */
WriteDOC(0x00, docptr, WritePipeTerm);
}
static void doc2001_readbuf(struct mtd_info *mtd,
u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int i;
/* Start read pipeline */
ReadDOC(docptr, ReadPipeInit);
for (i=0; i < len-1; i++)
buf[i] = ReadDOC(docptr, Mil_CDSN_IO);
/* Terminate read pipeline */
buf[i] = ReadDOC(docptr, LastDataRead);
}
static int doc2001_verifybuf(struct mtd_info *mtd,
const u_char *buf, int len)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int i;
/* Start read pipeline */
ReadDOC(docptr, ReadPipeInit);
for (i=0; i < len-1; i++)
if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) {
ReadDOC(docptr, LastDataRead);
return i;
}
if (buf[i] != ReadDOC(docptr, LastDataRead))
return i;
return 0;
}
static void doc200x_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int floor = 0;
/* 11.4.4 -- deassert CE before changing chip */
doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE);
if(debug)printk("select chip (%d)\n", chip);
if (chip == -1)
return;
floor = chip / doc->chips_per_floor;
chip -= (floor * doc->chips_per_floor);
WriteDOC(floor, docptr, FloorSelect);
WriteDOC(chip, docptr, CDSNDeviceSelect);
doc200x_hwcontrol(mtd, NAND_CTL_SETNCE);
doc->curchip = chip;
doc->curfloor = floor;
}
static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
switch(cmd) {
case NAND_CTL_SETNCE:
doc->CDSNControl |= CDSN_CTRL_CE;
break;
case NAND_CTL_CLRNCE:
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);
/* 11.4.3 -- 4 NOPs after CSDNControl write */
DoC_Delay(doc, 4);
}
static int doc200x_dev_ready(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
/* 11.4.2 -- must NOP four times before checking FR/B# */
DoC_Delay(doc, 4);
if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) {
if(debug)
printk("not ready\n");
return 0;
}
/* 11.4.2 -- Must NOP twice if it's ready */
DoC_Delay(doc, 2);
if (debug)printk("was ready\n");
return 1;
}
static int doc200x_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
/* This is our last resort if we couldn't find or create a BBT. Just
pretend all blocks are good. */
return 0;
}
static void doc200x_enable_hwecc(struct mtd_info *mtd, int mode)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
/* Prime the ECC engine */
switch(mode) {
case NAND_ECC_READ:
WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
WriteDOC(DOC_ECC_EN, docptr, ECCConf);
break;
case NAND_ECC_WRITE:
WriteDOC(DOC_ECC_RESET, docptr, ECCConf);
WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, ECCConf);
break;
}
}
/* This code is only called on write */
static int doc200x_calculate_ecc(struct mtd_info *mtd, const u_char *dat,
unsigned char *ecc_code)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
int i;
int emptymatch = 1;
/* flush the pipeline */
if (DoC_is_2000(doc)) {
WriteDOC(doc->CDSNControl & ~CDSN_CTRL_FLASH_IO, docptr, CDSNControl);
WriteDOC(0, docptr, 2k_CDSN_IO);
WriteDOC(0, docptr, 2k_CDSN_IO);
WriteDOC(0, docptr, 2k_CDSN_IO);
WriteDOC(doc->CDSNControl, docptr, CDSNControl);
} else {
WriteDOC(0, docptr, NOP);
WriteDOC(0, docptr, NOP);
WriteDOC(0, docptr, NOP);
}
for (i = 0; i < 6; i++) {
ecc_code[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
if (ecc_code[i] != empty_write_ecc[i])
emptymatch = 0;
}
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
#if 0
/* If emptymatch=1, we might have an all-0xff data buffer. Check. */
if (emptymatch) {
/* Note: this somewhat expensive test should not be triggered
often. It could be optimized away by examining the data in
the writebuf routine, and remembering the result. */
for (i = 0; i < 512; i++) {
if (dat[i] == 0xff) continue;
emptymatch = 0;
break;
}
}
/* If emptymatch still =1, we do have an all-0xff data buffer.
Return all-0xff ecc value instead of the computed one, so
it'll look just like a freshly-erased page. */
if (emptymatch) memset(ecc_code, 0xff, 6);
#endif
return 0;
}
static int doc200x_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
{
int i, ret = 0;
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
unsigned long docptr = doc->virtadr;
volatile u_char dummy;
int emptymatch = 1;
/* flush the pipeline */
if (DoC_is_2000(doc)) {
dummy = ReadDOC(docptr, 2k_ECCStatus);
dummy = ReadDOC(docptr, 2k_ECCStatus);
dummy = ReadDOC(docptr, 2k_ECCStatus);
} else {
dummy = ReadDOC(docptr, ECCConf);
dummy = ReadDOC(docptr, ECCConf);
dummy = ReadDOC(docptr, ECCConf);
}
/* Error occured ? */
if (dummy & 0x80) {
for (i = 0; i < 6; i++) {
calc_ecc[i] = ReadDOC_(docptr, DoC_ECCSyndrome0 + i);
if (calc_ecc[i] != empty_read_syndrome[i])
emptymatch = 0;
}
/* If emptymatch=1, the read syndrome is consistent with an
all-0xff data and stored ecc block. Check the stored ecc. */
if (emptymatch) {
for (i = 0; i < 6; i++) {
if (read_ecc[i] == 0xff) continue;
emptymatch = 0;
break;
}
}
/* If emptymatch still =1, check the data block. */
if (emptymatch) {
/* Note: this somewhat expensive test should not be triggered
often. It could be optimized away by examining the data in
the readbuf routine, and remembering the result. */
for (i = 0; i < 512; i++) {
if (dat[i] == 0xff) continue;
emptymatch = 0;
break;
}
}
/* If emptymatch still =1, this is almost certainly a freshly-
erased block, in which case the ECC will not come out right.
We'll suppress the error and tell the caller everything's
OK. Because it is. */
if (!emptymatch) ret = doc_decode_ecc (dat, calc_ecc);
if (ret > 0)
printk(KERN_ERR "doc200x_correct_data corrected %d errors\n", ret);
}
WriteDOC(DOC_ECC_DIS, docptr, ECCConf);
if (no_ecc_failures && (ret == -1)) {
printk(KERN_ERR "suppressing ECC failure\n");
ret = 0;
}
return ret;
}
//u_char mydatabuf[528];
static struct nand_oobinfo doc200x_oobinfo = {
.useecc = MTD_NANDECC_AUTOPLACE,
.eccbytes = 6,
.eccpos = {0, 1, 2, 3, 4, 5},
.oobfree = { {8, 8} }
};
/* Find the (I)NFTL Media Header, and optionally also the mirror media header.
On sucessful return, buf will contain a copy of the media header for
further processing. id is the string to scan for, and will presumably be
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
mh1_page in the DOC private structure. */
static int __init find_media_headers(struct mtd_info *mtd, u_char *buf,
const char *id, int findmirror)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
int offs, end = (MAX_MEDIAHEADER_SCAN << this->phys_erase_shift);
int ret, retlen;
end = min(end, mtd->size); // paranoia
for (offs = 0; offs < end; offs += mtd->erasesize) {
ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
if (retlen != mtd->oobblock) continue;
if (ret) {
printk(KERN_WARNING "ECC error scanning DOC at 0x%x\n",
offs);
}
if (memcmp(buf, id, 6)) continue;
printk(KERN_INFO "Found DiskOnChip %s Media Header at 0x%x\n", id, offs);
if (doc->mh0_page == -1) {
doc->mh0_page = offs >> this->page_shift;
if (!findmirror) return 1;
continue;
}
doc->mh1_page = offs >> this->page_shift;
return 2;
}
if (doc->mh0_page == -1) {
printk(KERN_WARNING "DiskOnChip %s Media Header not found.\n", id);
return 0;
}
/* 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. */
offs = doc->mh0_page << this->page_shift;
ret = mtd->read(mtd, offs, mtd->oobblock, &retlen, buf);
if (retlen != mtd->oobblock) {
/* Insanity. Give up. */
printk(KERN_ERR "Read DiskOnChip Media Header once, but can't reread it???\n");
return 0;
}
return 1;
}
static inline int __init nftl_partscan(struct mtd_info *mtd,
struct mtd_partition *parts)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
u_char *buf = this->data_buf;
struct NFTLMediaHeader *mh = (struct NFTLMediaHeader *) buf;
const int psize = 1 << this->page_shift;
int blocks, maxblocks;
int offs, numheaders;
if (!(numheaders=find_media_headers(mtd, buf, "ANAND", 1))) return 0;
//#ifdef CONFIG_MTD_DEBUG_VERBOSE
// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
printk(KERN_INFO " DataOrgID = %s\n"
" NumEraseUnits = %d\n"
" FirstPhysicalEUN = %d\n"
" FormattedSize = %d\n"
" UnitSizeFactor = %d\n",
mh->DataOrgID, mh->NumEraseUnits,
mh->FirstPhysicalEUN, mh->FormattedSize,
mh->UnitSizeFactor);
//#endif
blocks = mtd->size >> this->phys_erase_shift;
maxblocks = min(32768, mtd->erasesize - psize);
if (mh->UnitSizeFactor == 0x00) {
/* Auto-determine UnitSizeFactor. The constraints are:
- There can be at most 32768 virtual blocks.
- There can be at most (virtual block size - page size)
virtual blocks (because MediaHeader+BBT must fit in 1).
*/
mh->UnitSizeFactor = 0xff;
while (blocks > maxblocks) {
blocks >>= 1;
maxblocks = min(32768, (maxblocks << 1) + psize);
mh->UnitSizeFactor--;
}
printk(KERN_WARNING "UnitSizeFactor=0x00 detected. Correct value is assumed to be 0x%02x.\n", mh->UnitSizeFactor);
}
/* NOTE: The lines below modify internal variables of the NAND and MTD
layers; variables with have already been configured by nand_scan.
Unfortunately, we didn't know before this point what these values
should be. Thus, this code is somewhat dependant on the exact
implementation of the NAND layer. */
if (mh->UnitSizeFactor != 0xff) {
this->bbt_erase_shift += (0xff - mh->UnitSizeFactor);
mtd->erasesize <<= (0xff - mh->UnitSizeFactor);
printk(KERN_INFO "Setting virtual erase size to %d\n", mtd->erasesize);
blocks = mtd->size >> this->bbt_erase_shift;
maxblocks = min(32768, mtd->erasesize - psize);
}
if (blocks > maxblocks) {
printk(KERN_ERR "UnitSizeFactor of 0x%02x is inconsistent with device size. Aborting.\n", mh->UnitSizeFactor);
return 0;
}
/* Skip past the media headers. */
offs = max(doc->mh0_page, doc->mh1_page);
offs <<= this->page_shift;
offs += mtd->erasesize;
//parts[0].name = " DiskOnChip Boot / Media Header partition";
//parts[0].offset = 0;
//parts[0].size = offs;
parts[0].name = " DiskOnChip BDTL partition";
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;
return 2;
}
return 1;
}
/* This is a stripped-down copy of the code in inftlmount.c */
static inline int __init inftl_partscan(struct mtd_info *mtd,
struct mtd_partition *parts)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
u_char *buf = this->data_buf;
struct INFTLMediaHeader *mh = (struct INFTLMediaHeader *) buf;
struct INFTLPartition *ip;
int numparts = 0;
int blocks;
int vshift, lastvunit = 0;
int i;
int end = mtd->size;
if (inftl_bbt_write)
end -= (INFTL_BBT_RESERVED_BLOCKS << this->phys_erase_shift);
if (!find_media_headers(mtd, buf, "BNAND", 0)) return 0;
doc->mh1_page = doc->mh0_page + (4096 >> this->page_shift);
mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks);
mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions);
mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions);
mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits);
mh->FormatFlags = le32_to_cpu(mh->FormatFlags);
mh->PercentUsed = le32_to_cpu(mh->PercentUsed);
//#ifdef CONFIG_MTD_DEBUG_VERBOSE
// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
printk(KERN_INFO " bootRecordID = %s\n"
" NoOfBootImageBlocks = %d\n"
" NoOfBinaryPartitions = %d\n"
" NoOfBDTLPartitions = %d\n"
" BlockMultiplerBits = %d\n"
" FormatFlgs = %d\n"
" OsakVersion = 0x%x\n"
" PercentUsed = %d\n",
mh->bootRecordID, mh->NoOfBootImageBlocks,
mh->NoOfBinaryPartitions,
mh->NoOfBDTLPartitions,
mh->BlockMultiplierBits, mh->FormatFlags,
mh->OsakVersion, mh->PercentUsed);
//#endif
vshift = this->phys_erase_shift + mh->BlockMultiplierBits;
blocks = mtd->size >> vshift;
if (blocks > 32768) {
printk(KERN_ERR "BlockMultiplierBits=%d is inconsistent with device size. Aborting.\n", mh->BlockMultiplierBits);
return 0;
}
blocks = doc->chips_per_floor << (this->chip_shift - this->phys_erase_shift);
if (inftl_bbt_write && (blocks > mtd->erasesize)) {
printk(KERN_ERR "Writeable BBTs spanning more than one erase block are not yet supported. FIX ME!\n");
return 0;
}
/* Scan the partitions */
for (i = 0; (i < 4); i++) {
ip = &(mh->Partitions[i]);
ip->virtualUnits = le32_to_cpu(ip->virtualUnits);
ip->firstUnit = le32_to_cpu(ip->firstUnit);
ip->lastUnit = le32_to_cpu(ip->lastUnit);
ip->flags = le32_to_cpu(ip->flags);
ip->spareUnits = le32_to_cpu(ip->spareUnits);
ip->Reserved0 = le32_to_cpu(ip->Reserved0);
//#ifdef CONFIG_MTD_DEBUG_VERBOSE
// if (CONFIG_MTD_DEBUG_VERBOSE >= 2)
printk(KERN_INFO " PARTITION[%d] ->\n"
" virtualUnits = %d\n"
" firstUnit = %d\n"
" lastUnit = %d\n"
" flags = 0x%x\n"
" spareUnits = %d\n",
i, ip->virtualUnits, ip->firstUnit,
ip->lastUnit, ip->flags,
ip->spareUnits);
//#endif
/*
if ((i == 0) && (ip->firstUnit > 0)) {
parts[0].name = " DiskOnChip IPL / Media Header partition";
parts[0].offset = 0;
parts[0].size = mtd->erasesize * ip->firstUnit;
numparts = 1;
}
*/
if (ip->flags & INFTL_BINARY)
parts[numparts].name = " DiskOnChip BDK partition";
else
parts[numparts].name = " DiskOnChip BDTL partition";
parts[numparts].offset = ip->firstUnit << vshift;
parts[numparts].size = (1 + ip->lastUnit - ip->firstUnit) << vshift;
numparts++;
if (ip->lastUnit > lastvunit) lastvunit = ip->lastUnit;
if (ip->flags & INFTL_LAST) break;
}
lastvunit++;
if ((lastvunit << vshift) < end) {
parts[numparts].name = " DiskOnChip Remainder partition";
parts[numparts].offset = lastvunit << vshift;
parts[numparts].size = end - parts[numparts].offset;
numparts++;
}
return numparts;
}
static int __init nftl_scan_bbt(struct mtd_info *mtd)
{
int ret, numparts;
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
struct mtd_partition parts[2];
memset((char *) parts, 0, sizeof(parts));
/* On NFTL, we have to find the media headers before we can read the
BBTs, since they're stored in the media header eraseblocks. */
numparts = nftl_partscan(mtd, parts);
if (!numparts) return -EIO;
this->bbt_td->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
NAND_BBT_VERSION;
this->bbt_td->veroffs = 7;
this->bbt_td->pages[0] = doc->mh0_page + 1;
if (doc->mh1_page != -1) {
this->bbt_md->options = NAND_BBT_ABSPAGE | NAND_BBT_8BIT |
NAND_BBT_SAVECONTENT | NAND_BBT_WRITE |
NAND_BBT_VERSION;
this->bbt_md->veroffs = 7;
this->bbt_md->pages[0] = doc->mh1_page + 1;
} else {
this->bbt_md = NULL;
}
/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
At least as nand_bbt.c is currently written. */
if ((ret = nand_scan_bbt(mtd, NULL)))
return ret;
add_mtd_device(mtd);
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);
#endif
return 0;
}
static int __init inftl_scan_bbt(struct mtd_info *mtd)
{
int ret, numparts;
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
struct mtd_partition parts[5];
if (this->numchips > doc->chips_per_floor) {
printk(KERN_ERR "Multi-floor INFTL devices not yet supported.\n");
return -EIO;
}
if (mtd->size == (8<<20)) {
#if 0
/* This doesn't seem to work for me. I get ECC errors on every page. */
/* The Millennium 8MiB is actually an NFTL device! */
mtd->name = "DiskOnChip Millennium 8MiB (NFTL)";
return nftl_scan_bbt(mtd);
#endif
printk(KERN_ERR "DiskOnChip Millennium 8MiB is not supported.\n");
return -EIO;
}
this->bbt_td->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
NAND_BBT_VERSION;
if (inftl_bbt_write)
this->bbt_td->options |= NAND_BBT_WRITE;
this->bbt_td->offs = 8;
this->bbt_td->len = 8;
this->bbt_td->veroffs = 7;
this->bbt_td->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
this->bbt_td->reserved_block_code = 0x01;
this->bbt_td->pattern = "MSYS_BBT";
this->bbt_md->options = NAND_BBT_LASTBLOCK | NAND_BBT_8BIT |
NAND_BBT_VERSION;
if (inftl_bbt_write)
this->bbt_md->options |= NAND_BBT_WRITE;
this->bbt_md->offs = 8;
this->bbt_md->len = 8;
this->bbt_md->veroffs = 7;
this->bbt_md->maxblocks = INFTL_BBT_RESERVED_BLOCKS;
this->bbt_md->reserved_block_code = 0x01;
this->bbt_md->pattern = "TBB_SYSM";
/* It's safe to set bd=NULL below because NAND_BBT_CREATE is not set.
At least as nand_bbt.c is currently written. */
if ((ret = nand_scan_bbt(mtd, NULL)))
return ret;
memset((char *) parts, 0, sizeof(parts));
numparts = inftl_partscan(mtd, parts);
/* 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
autopartitioning, but I want to give it more thought. */
if (!numparts) return -EIO;
add_mtd_device(mtd);
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
if (!no_autopart) add_mtd_partitions(mtd, parts, numparts);
#endif
return 0;
}
static inline int __init doc2000_init(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
this->write_byte = doc2000_write_byte;
this->read_byte = doc2000_read_byte;
this->write_buf = doc2000_writebuf;
this->read_buf = doc2000_readbuf;
this->verify_buf = doc2000_verifybuf;
this->scan_bbt = nftl_scan_bbt;
doc->CDSNControl = CDSN_CTRL_FLASH_IO | CDSN_CTRL_ECC_IO;
doc2000_count_chips(mtd);
mtd->name = "DiskOnChip 2000 (NFTL Model)";
return (4 * doc->chips_per_floor);
}
static inline int __init doc2001_init(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
struct doc_priv *doc = (void *)this->priv;
this->write_byte = doc2001_write_byte;
this->read_byte = doc2001_read_byte;
this->write_buf = doc2001_writebuf;
this->read_buf = doc2001_readbuf;
this->verify_buf = doc2001_verifybuf;
this->scan_bbt = inftl_scan_bbt;
ReadDOC(doc->virtadr, ChipID);
ReadDOC(doc->virtadr, ChipID);
ReadDOC(doc->virtadr, ChipID);
if (ReadDOC(doc->virtadr, ChipID) != DOC_ChipID_DocMil) {
/* It's not a Millennium; it's one of the newer
DiskOnChip 2000 units with a similar ASIC.
Treat it like a Millennium, except that it
can have multiple chips. */
doc2000_count_chips(mtd);
mtd->name = "DiskOnChip 2000 (INFTL Model)";
return (4 * doc->chips_per_floor);
} else {
/* Bog-standard Millennium */
doc->chips_per_floor = 1;
mtd->name = "DiskOnChip Millennium";
return 1;
}
}
static inline int __init doc_probe(unsigned long physadr)
{
unsigned char ChipID;
struct mtd_info *mtd;
struct nand_chip *nand;
struct doc_priv *doc;
unsigned long virtadr;
unsigned char save_control;
unsigned char tmp, tmpb, tmpc;
int reg, len, numchips;
int ret = 0;
virtadr = (unsigned long)ioremap(physadr, DOC_IOREMAP_LEN);
if (!virtadr) {
printk(KERN_ERR "Diskonchip ioremap failed: 0x%x bytes at 0x%lx\n", DOC_IOREMAP_LEN, physadr);
return -EIO;
}
/* It's not possible to cleanly detect the DiskOnChip - the
* bootup procedure will put the device into reset mode, and
* it's not possible to talk to it without actually writing
* to the DOCControl register. So we store the current contents
* of the DOCControl register's location, in case we later decide
* that it's not a DiskOnChip, and want to put it back how we
* found it.
*/
save_control = ReadDOC(virtadr, DOCControl);
/* Reset the DiskOnChip ASIC */
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 */
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);
switch(ChipID) {
case DOC_ChipID_Doc2k:
reg = DoC_2k_ECCStatus;
break;
case DOC_ChipID_DocMil:
reg = DoC_ECCConf;
break;
default:
ret = -ENODEV;
goto notfound;
}
/* Check the TOGGLE bit in the ECC register */
tmp = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
tmpb = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
tmpc = ReadDOC_(virtadr, reg) & DOC_TOGGLE_BIT;
if ((tmp == tmpb) || (tmp != tmpc)) {
printk(KERN_WARNING "Possible DiskOnChip at 0x%lx failed TOGGLE test, dropping.\n", physadr);
ret = -ENODEV;
goto notfound;
}
for (mtd = doclist; mtd; mtd = doc->nextdoc) {
nand = mtd->priv;
doc = (void *)nand->priv;
/* Use the alias resolution register to determine if this is
in fact the same DOC aliased to a new address. If writes
to one chip's alias resolution register change the value on
the other chip, they're the same chip. */
unsigned char oldval = ReadDOC(doc->virtadr, AliasResolution);
unsigned char newval = ReadDOC(virtadr, AliasResolution);
if (oldval != newval)
continue;
WriteDOC(~newval, virtadr, AliasResolution);
oldval = ReadDOC(doc->virtadr, AliasResolution);
WriteDOC(newval, virtadr, AliasResolution); // restore it
newval = ~newval;
if (oldval == newval) {
//printk(KERN_DEBUG "Found alias of DOC at 0x%lx to 0x%lx\n", doc->physadr, physadr);
goto notfound;
}
}
printk(KERN_NOTICE "DiskOnChip found at 0x%lx\n", physadr);
len = sizeof(struct mtd_info) +
sizeof(struct nand_chip) +
sizeof(struct doc_priv) +
(2 * sizeof(struct nand_bbt_descr));
mtd = kmalloc(len, GFP_KERNEL);
if (!mtd) {
printk(KERN_ERR "DiskOnChip kmalloc (%d bytes) failed!\n", len);
ret = -ENOMEM;
goto fail;
}
memset(mtd, 0, len);
nand = (struct nand_chip *) (mtd + 1);
doc = (struct doc_priv *) (nand + 1);
nand->bbt_td = (struct nand_bbt_descr *) (doc + 1);
nand->bbt_md = nand->bbt_td + 1;
mtd->priv = (void *) nand;
mtd->owner = THIS_MODULE;
nand->priv = (void *) doc;
nand->select_chip = doc200x_select_chip;
nand->hwcontrol = doc200x_hwcontrol;
nand->dev_ready = doc200x_dev_ready;
nand->waitfunc = doc200x_wait;
nand->block_bad = doc200x_block_bad;
nand->enable_hwecc = doc200x_enable_hwecc;
nand->calculate_ecc = doc200x_calculate_ecc;
nand->correct_data = doc200x_correct_data;
//nand->data_buf
nand->autooob = &doc200x_oobinfo;
nand->eccmode = NAND_ECC_HW6_512;
nand->options = NAND_USE_FLASH_BBT | NAND_HWECC_SYNDROME;
doc->physadr = physadr;
doc->virtadr = virtadr;
doc->ChipID = ChipID;
doc->curfloor = -1;
doc->curchip = -1;
doc->mh0_page = -1;
doc->mh1_page = -1;
doc->nextdoc = doclist;
if (ChipID == DOC_ChipID_Doc2k)
numchips = doc2000_init(mtd);
else
numchips = doc2001_init(mtd);
if ((ret = nand_scan(mtd, numchips))) {
/* DBB note: i believe nand_release is necessary here, as
buffers may have been allocated in nand_base. Check with
Thomas. FIX ME! */
/* nand_release will call del_mtd_device, but we haven't yet
added it. This is handled without incident by
del_mtd_device, as far as I can tell. */
nand_release(mtd);
kfree(mtd);
goto fail;
}
/* Success! */
doclist = mtd;
return 0;
notfound:
/* Put back the contents of the DOCControl register, in case it's not
actually a DiskOnChip. */
WriteDOC(save_control, virtadr, DOCControl);
fail:
iounmap((void *)virtadr);
return ret;
}
int __init init_nanddoc(void)
{
int i;
if (doc_config_location) {
printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location);
return doc_probe(doc_config_location);
} else {
for (i=0; (doc_locations[i] != 0xffffffff); i++) {
doc_probe(doc_locations[i]);
}
}
/* No banner message any more. Print a message if no DiskOnChip
found, so the user knows we at least tried. */
if (!doclist) {
printk(KERN_INFO "No valid DiskOnChip devices found\n");
return -ENODEV;
}
return 0;
}
void __exit cleanup_nanddoc(void)
{
struct mtd_info *mtd, *nextmtd;
struct nand_chip *nand;
struct doc_priv *doc;
for (mtd = doclist; mtd; mtd = nextmtd) {
nand = mtd->priv;
doc = (void *)nand->priv;
nextmtd = doc->nextdoc;
nand_release(mtd);
iounmap((void *)doc->virtadr);
kfree(mtd);
}
}
module_init(init_nanddoc);
module_exit(cleanup_nanddoc);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("M-Systems DiskOnChip 2000 and Millennium device driver\n");
...@@ -6,7 +6,7 @@ ...@@ -6,7 +6,7 @@
* Derived from drivers/mtd/nand/autcpu12.c * Derived from drivers/mtd/nand/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
* *
* $Id: edb7312.c,v 1.5 2003/04/20 07:24:40 gleixner Exp $ * $Id: edb7312.c,v 1.8 2004/07/12 15:03:26 dwmw2 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
...@@ -20,6 +20,7 @@ ...@@ -20,6 +20,7 @@
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/init.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
...@@ -83,7 +84,7 @@ static struct mtd_partition partition_info[] = { ...@@ -83,7 +84,7 @@ static struct mtd_partition partition_info[] = {
/* /*
* hardware specific access to control-lines * hardware specific access to control-lines
*/ */
static void ep7312_hwcontrol(int cmd) static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd)
{ {
switch(cmd) { switch(cmd) {
...@@ -113,10 +114,13 @@ static void ep7312_hwcontrol(int cmd) ...@@ -113,10 +114,13 @@ static void ep7312_hwcontrol(int cmd)
/* /*
* read device ready pin * read device ready pin
*/ */
static int ep7312_device_ready(void) static int ep7312_device_ready(struct mtd_info *mtd)
{ {
return 1; return 1;
} }
#ifdef CONFIG_MTD_PARTITIONS
const char *part_probes[] = { "cmdlinepart", NULL };
#endif
/* /*
* Main initialization routine * Main initialization routine
...@@ -171,7 +175,7 @@ static int __init ep7312_init (void) ...@@ -171,7 +175,7 @@ static int __init ep7312_init (void)
this->chip_delay = 15; this->chip_delay = 15;
/* Scan to find existence of the device */ /* Scan to find existence of the device */
if (nand_scan (ep7312_mtd)) { if (nand_scan (ep7312_mtd, 1)) {
iounmap((void *)ep7312_fio_base); iounmap((void *)ep7312_fio_base);
kfree (ep7312_mtd); kfree (ep7312_mtd);
return -ENXIO; return -ENXIO;
...@@ -186,16 +190,16 @@ static int __init ep7312_init (void) ...@@ -186,16 +190,16 @@ static int __init ep7312_init (void)
return -ENOMEM; return -ENOMEM;
} }
#ifdef CONFIG_MTD_CMDLINE_PARTS #ifdef CONFIG_PARTITIONS
mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts, ep7312_mtd->name = "edb7312-nand";
"edb7312-nand"); mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes,
&mtd_parts, 0);
if (mtd_parts_nb > 0) if (mtd_parts_nb > 0)
part_type = "command line"; part_type = "command line";
else else
mtd_parts_nb = 0; mtd_parts_nb = 0;
#endif #endif
if (mtd_parts_nb == 0) if (mtd_parts_nb == 0) {
{
mtd_parts = partition_info; mtd_parts = partition_info;
mtd_parts_nb = NUM_PARTITIONS; mtd_parts_nb = NUM_PARTITIONS;
part_type = "static"; part_type = "static";
......
/*
* drivers/mtd/nand.c
*
* Overview:
* This is the generic MTD driver for NAND flash devices. It should be
* capable of working with almost all NAND chips currently available.
*
* Additional technical information is available on
* http://www.linux-mtd.infradead.org/tech/nand.html
*
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
* 2002 Thomas Gleixner (tglx@linutronix.de)
*
* 10-29-2001 Thomas Gleixner (tglx@linutronix.de)
* - Changed nand_chip structure for controlline function to
* support different hardware structures (Access to
* controllines ALE,CLE,NCE via hardware specific function.
* - exit out of "failed erase block" changed, to avoid
* driver hangup
* - init_waitqueue_head added in function nand_scan !!
*
* 01-30-2002 Thomas Gleixner (tglx@linutronix.de)
* change in nand_writev to block invalid vecs entries
*
* 02-11-2002 Thomas Gleixner (tglx@linutronix.de)
* - major rewrite to avoid duplicated code
* common nand_write_page function
* common get_chip function
* - added oob_config structure for out of band layouts
* - write_oob changed for partial programming
* - read cache for faster access for subsequent reads
* from the same page.
* - support for different read/write address
* - support for device ready/busy line
* - read oob for more than one page enabled
*
* 02-27-2002 Thomas Gleixner (tglx@linutronix.de)
* - command-delay can be programmed
* - fixed exit from erase with callback-function enabled
*
* 03-21-2002 Thomas Gleixner (tglx@linutronix.de)
* - DEBUG improvements provided by Elizabeth Clarke
* (eclarke@aminocom.com)
* - added zero check for this->chip_delay
*
* 04-03-2002 Thomas Gleixner (tglx@linutronix.de)
* - added added hw-driver supplied command and wait functions
* - changed blocking for erase (erase suspend enabled)
* - check pointers before accessing flash provided by
* John Hall (john.hall@optionexist.co.uk)
*
* 04-09-2002 Thomas Gleixner (tglx@linutronix.de)
* - nand_wait repaired
*
* 04-28-2002 Thomas Gleixner (tglx@linutronix.de)
* - OOB config defines moved to nand.h
*
* 08-01-2002 Thomas Gleixner (tglx@linutronix.de)
* - changed my mailaddress, added pointer to tech/nand.html
*
* 08-07-2002 Thomas Gleixner (tglx@linutronix.de)
* forced bad block location to byte 5 of OOB, even if
* CONFIG_MTD_NAND_ECC_JFFS2 is not set, to prevent
* erase /dev/mtdX from erasing bad blocks and destroying
* bad block info
*
* 08-10-2002 Thomas Gleixner (tglx@linutronix.de)
* Fixed writing tail of data. Thanks to Alice Hennessy
* <ahennessy@mvista.com>.
*
* 08-10-2002 Thomas Gleixner (tglx@linutronix.de)
* nand_read_ecc and nand_write_page restructured to support
* hardware ECC. Thanks to Steven Hein (ssh@sgi.com)
* for basic implementation and suggestions.
* 3 new pointers in nand_chip structure:
* calculate_ecc, correct_data, enabled_hwecc
* forcing all hw-drivers to support page cache
* eccvalid_pos is now mandatory
*
* 08-17-2002 tglx: fixed signed/unsigned missmatch in write.c
* Thanks to Ken Offer <koffer@arlut.utexas.edu>
*
* 08-29-2002 tglx: use buffered read/write only for non pagealigned
* access, speed up the aligned path by using the fs-buffer
* reset chip removed from nand_select(), implicit done
* only, when erase is interrupted
* waitfuntion use yield, instead of schedule_timeout
* support for 6byte/512byte hardware ECC
* read_ecc, write_ecc extended for different oob-layout
* selections: Implemented NAND_NONE_OOB, NAND_JFFS2_OOB,
* NAND_YAFFS_OOB. fs-driver gives one of these constants
* to select the oob-layout fitting the filesystem.
* oobdata can be read together with the raw data, when
* the fs-driver supplies a big enough buffer.
* size = 12 * number of pages to read (256B pagesize)
* 24 * number of pages to read (512B pagesize)
* the buffer contains 8/16 byte oobdata and 4/8 byte
* returncode from calculate_ecc
* oobdata can be given from filesystem to program them
* in one go together with the raw data. ECC codes are
* filled in at the place selected by oobsel.
*
* 09-04-2002 tglx: fixed write_verify (John Hall (john.hall@optionexist.co.uk))
*
* 11-11-2002 tglx: fixed debug output in nand_write_page
* (John Hall (john.hall@optionexist.co.uk))
*
* 11-25-2002 tglx: Moved device ID/ manufacturer ID from nand_ids.h
* Splitted device ID and manufacturer ID table.
* Removed CONFIG_MTD_NAND_ECC, as it defaults to ECC_NONE for
* mtd->read / mtd->write and is controllable by the fs driver
* for mtd->read_ecc / mtd->write_ecc
* some minor cleanups
*
* 12-05-2002 tglx: Dave Ellis (DGE@sixnetio) provided the fix for
* WRITE_VERIFY long time ago. Thanks for remembering me.
*
* 02-14-2003 tglx: Reject non page aligned writes
* Fixed ecc select in nand_write_page to match semantics.
*
* 02-18-2003 tglx: Changed oobsel to pointer. Added a default oob-selector
*
* 02-18-2003 tglx: Implemented oobsel again. Now it uses a pointer to
+ a structure, which will be supplied by a filesystem driver
* If NULL is given, then the defaults (none or defaults
* supplied by ioctl (MEMSETOOBSEL) are used.
* For partitions the partition defaults are used (mtdpart.c)
*
* 06-04-2003 tglx: fix compile errors and fix write verify problem for
* some chips, which need either a delay between the readback
* and the next write command or have the CE removed. The
* CE disable/enable is much faster than a 20us delay and
* it should work on all available chips.
*
* $Id: nand.c,v 1.46 2003/06/04 17:10:36 gleixner Exp $
*
* 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
* published by the Free Software Foundation.
*
*/
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/sched.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/interrupt.h>
#include <asm/io.h>
/*
* Macros for low-level register control
*/
#define nand_select() this->hwcontrol(NAND_CTL_SETNCE);
#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE);
/*
* NAND low-level MTD interface functions
*/
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
static int nand_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);
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
static int nand_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);
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t * retlen);
static int nand_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);
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
static void nand_sync (struct mtd_info *mtd);
static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel);
/*
* Send command to NAND device
*/
static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
register unsigned long NAND_IO_ADDR = this->IO_ADDR_W;
/* Begin command latch cycle */
this->hwcontrol (NAND_CTL_SETCLE);
/*
* Write out the command to the device.
*/
if (command != NAND_CMD_SEQIN)
writeb (command, NAND_IO_ADDR);
else {
if (mtd->oobblock == 256 && column >= 256) {
column -= 256;
writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
} else if (mtd->oobblock == 512 && column >= 256) {
if (column < 512) {
column -= 256;
writeb (NAND_CMD_READ1, NAND_IO_ADDR);
writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
} else {
column -= 512;
writeb (NAND_CMD_READOOB, NAND_IO_ADDR);
writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
}
} else {
writeb (NAND_CMD_READ0, NAND_IO_ADDR);
writeb (NAND_CMD_SEQIN, NAND_IO_ADDR);
}
}
/* Set ALE and clear CLE to start address cycle */
this->hwcontrol (NAND_CTL_CLRCLE);
if (column != -1 || page_addr != -1) {
this->hwcontrol (NAND_CTL_SETALE);
/* Serially input address */
if (column != -1)
writeb (column, NAND_IO_ADDR);
if (page_addr != -1) {
writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR);
writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR);
/* One more address cycle for higher density devices */
if (mtd->size & 0x0c000000)
writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR);
}
/* Latch in address */
this->hwcontrol (NAND_CTL_CLRALE);
}
/*
* program and erase have their own busy handlers
* status and sequential in needs no delay
*/
switch (command) {
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET:
if (this->dev_ready)
break;
this->hwcontrol (NAND_CTL_SETCLE);
writeb (NAND_CMD_STATUS, NAND_IO_ADDR);
this->hwcontrol (NAND_CTL_CLRCLE);
while ( !(readb (this->IO_ADDR_R) & 0x40));
return;
/* This applies to read commands */
default:
/*
* If we don't have access to the busy pin, we apply the given
* command delay
*/
if (!this->dev_ready) {
udelay (this->chip_delay);
return;
}
}
/* wait until command is processed */
while (!this->dev_ready());
}
/*
* Get chip for selected access
*/
static inline void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state)
{
DECLARE_WAITQUEUE (wait, current);
/*
* Grab the lock and see if the device is available
* For erasing, we keep the spinlock until the
* erase command is written.
*/
retry:
spin_lock_bh (&this->chip_lock);
if (this->state == FL_READY) {
this->state = new_state;
if (new_state != FL_ERASING)
spin_unlock_bh (&this->chip_lock);
return;
}
if (this->state == FL_ERASING) {
if (new_state != FL_ERASING) {
this->state = new_state;
spin_unlock_bh (&this->chip_lock);
nand_select (); /* select in any case */
this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
return;
}
}
set_current_state (TASK_UNINTERRUPTIBLE);
add_wait_queue (&this->wq, &wait);
spin_unlock_bh (&this->chip_lock);
schedule ();
remove_wait_queue (&this->wq, &wait);
goto retry;
}
/*
* Wait for command done. This applies to erase and program only
* Erase can take up to 400ms and program up to 20ms according to
* general NAND and SmartMedia specs
*
*/
static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
{
unsigned long timeo = jiffies;
int status;
if (state == FL_ERASING)
timeo += (HZ * 400) / 1000;
else
timeo += (HZ * 20) / 1000;
spin_lock_bh (&this->chip_lock);
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
while (time_before(jiffies, timeo)) {
/* Check, if we were interrupted */
if (this->state != state) {
spin_unlock_bh (&this->chip_lock);
return 0;
}
if (this->dev_ready) {
if (this->dev_ready ())
break;
}
if (readb (this->IO_ADDR_R) & 0x40)
break;
spin_unlock_bh (&this->chip_lock);
yield ();
spin_lock_bh (&this->chip_lock);
}
status = (int) readb (this->IO_ADDR_R);
spin_unlock_bh (&this->chip_lock);
return status;
}
/*
* Nand_page_program function is used for write and writev !
* This function will always program a full page of data
* If you call it with a non page aligned buffer, you're lost :)
*/
static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel)
{
int i, status;
u_char ecc_code[6], *oob_data;
int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
int *oob_config = oobsel->eccpos;
/* pad oob area, if we have no oob buffer from fs-driver */
if (!oob_buf) {
oob_data = &this->data_buf[mtd->oobblock];
for (i = 0; i < mtd->oobsize; i++)
oob_data[i] = 0xff;
} else
oob_data = oob_buf;
/* Send command to begin auto page programming */
this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
/* Write out complete page of data, take care of eccmode */
switch (eccmode) {
/* No ecc and software ecc 3/256, write all */
case NAND_ECC_NONE:
printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
for (i = 0; i < mtd->oobblock; i++)
writeb ( this->data_poi[i] , this->IO_ADDR_W);
break;
case NAND_ECC_SOFT:
this->calculate_ecc (&this->data_poi[0], &(ecc_code[0]));
for (i = 0; i < 3; i++)
oob_data[oob_config[i]] = ecc_code[i];
/* Calculate and write the second ECC for 512 Byte page size */
if (mtd->oobblock == 512) {
this->calculate_ecc (&this->data_poi[256], &(ecc_code[3]));
for (i = 3; i < 6; i++)
oob_data[oob_config[i]] = ecc_code[i];
}
for (i = 0; i < mtd->oobblock; i++)
writeb ( this->data_poi[i] , this->IO_ADDR_W);
break;
/* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */
case NAND_ECC_HW3_256:
this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write */
for (i = 0; i < mtd->eccsize; i++)
writeb ( this->data_poi[i] , this->IO_ADDR_W);
this->calculate_ecc (NULL, &(ecc_code[0]));
for (i = 0; i < 3; i++)
oob_data[oob_config[i]] = ecc_code[i];
if (mtd->oobblock == 512) {
this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write*/
for (i = mtd->eccsize; i < mtd->oobblock; i++)
writeb ( this->data_poi[i] , this->IO_ADDR_W);
this->calculate_ecc (NULL, &(ecc_code[3]));
for (i = 3; i < 6; i++)
oob_data[oob_config[i]] = ecc_code[i];
}
break;
/* Hardware ecc 3 byte / 512 byte data, write full page */
case NAND_ECC_HW3_512:
this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */
for (i = 0; i < mtd->oobblock; i++)
writeb ( this->data_poi[i] , this->IO_ADDR_W);
this->calculate_ecc (NULL, &(ecc_code[0]));
for (i = 0; i < 3; i++)
oob_data[oob_config[i]] = ecc_code[i];
break;
/* Hardware ecc 6 byte / 512 byte data, write full page */
case NAND_ECC_HW6_512:
this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */
for (i = 0; i < mtd->oobblock; i++)
writeb ( this->data_poi[i] , this->IO_ADDR_W);
this->calculate_ecc (NULL, &(ecc_code[0]));
for (i = 0; i < 6; i++)
oob_data[oob_config[i]] = ecc_code[i];
break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
}
/* Write out OOB data */
for (i = 0; i < mtd->oobsize; i++)
writeb ( oob_data[i] , this->IO_ADDR_W);
/* Send command to actually program the data */
this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
/* call wait ready function */
status = this->waitfunc (mtd, this, FL_WRITING);
/* See if device thinks it succeeded */
if (status & 0x01) {
DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
return -EIO;
}
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/*
* The NAND device assumes that it is always writing to
* a cleanly erased page. Hence, it performs its internal
* write verification only on bits that transitioned from
* 1 to 0. The device does NOT verify the whole page on a
* byte by byte basis. It is possible that the page was
* not completely erased or the page is becoming unusable
* due to wear. The read with ECC would catch the error
* later when the ECC page check fails, but we would rather
* catch it early in the page write stage. Better to write
* no data than invalid data.
*/
/* Send command to read back the page */
this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
/* Loop through and verify the data */
for (i = 0; i < mtd->oobblock; i++) {
if (this->data_poi[i] != readb (this->IO_ADDR_R)) {
DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
return -EIO;
}
}
/* check, if we have a fs-supplied oob-buffer */
if (oob_buf) {
for (i = 0; i < mtd->oobsize; i++) {
if (oob_data[i] != readb (this->IO_ADDR_R)) {
DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
return -EIO;
}
}
} else {
if (eccmode != NAND_ECC_NONE) {
int ecc_bytes = 0;
switch (this->eccmode) {
case NAND_ECC_SOFT:
case NAND_ECC_HW3_256: ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; break;
case NAND_ECC_HW3_512: ecc_bytes = 3; break;
case NAND_ECC_HW6_512: ecc_bytes = 6; break;
}
for (i = 0; i < mtd->oobsize; i++)
oob_data[i] = readb (this->IO_ADDR_R);
for (i = 0; i < ecc_bytes; i++) {
if (oob_data[oob_config[i]] != ecc_code[i]) {
DEBUG (MTD_DEBUG_LEVEL0,
"%s: Failed ECC write "
"verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
return -EIO;
}
}
}
}
/*
* Terminate the read command. This is faster than sending a reset command or
* applying a 20us delay before issuing the next programm sequence.
* This is not a problem for all chips, but I have found a bunch of them.
*/
nand_deselect();
nand_select();
#endif
return 0;
}
/*
* Use NAND read ECC
*/
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
{
return (nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL));
}
/*
* NAND read with ECC
*/
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
{
int j, col, page, end, ecc;
int erase_state = 0;
int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
struct nand_chip *this = mtd->priv;
u_char *data_poi, *oob_data = oob_buf;
u_char ecc_calc[6];
u_char ecc_code[6];
int eccmode;
int *oob_config;
// use chip default if zero
if (oobsel == NULL)
oobsel = &mtd->oobinfo;
eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
oob_config = oobsel->eccpos;
DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
/* Do not allow reads past end of device */
if ((from + len) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
*retlen = 0;
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd ,FL_READING, &erase_state);
/* Select the NAND device */
nand_select ();
/* First we calculate the starting page */
page = from >> this->page_shift;
/* Get raw starting column */
col = from & (mtd->oobblock - 1);
end = mtd->oobblock;
ecc = mtd->eccsize;
/* Send the read command */
this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
/* Loop until all data read */
while (read < len) {
/* If we have consequent page reads, apply delay or wait for ready/busy pin */
if (read) {
if (!this->dev_ready)
udelay (this->chip_delay);
else
while (!this->dev_ready());
}
/*
* If the read is not page aligned, we have to read into data buffer
* due to ecc, else we read into return buffer direct
*/
if (!col && (len - read) >= end)
data_poi = &buf[read];
else
data_poi = this->data_buf;
/* get oob area, if we have no oob buffer from fs-driver */
if (!oob_buf) {
oob_data = &this->data_buf[end];
oob = 0;
}
j = 0;
switch (eccmode) {
case NAND_ECC_NONE: /* No ECC, Read in a page */
printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
while (j < end)
data_poi[j++] = readb (this->IO_ADDR_R);
break;
case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */
while (j < end)
data_poi[j++] = readb (this->IO_ADDR_R);
this->calculate_ecc (&data_poi[0], &ecc_calc[0]);
if (mtd->oobblock == 512)
this->calculate_ecc (&data_poi[256], &ecc_calc[3]);
break;
case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */
this->enable_hwecc (NAND_ECC_READ);
while (j < ecc)
data_poi[j++] = readb (this->IO_ADDR_R);
this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */
if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */
this->enable_hwecc (NAND_ECC_READ);
while (j < end)
data_poi[j++] = readb (this->IO_ADDR_R);
this->calculate_ecc (&data_poi[256], &ecc_calc[3]); /* read from hardware */
}
break;
case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page */
this->enable_hwecc (NAND_ECC_READ);
while (j < end)
data_poi[j++] = readb (this->IO_ADDR_R);
this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */
break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
}
/* read oobdata */
for (j = 0; j < mtd->oobsize; j++)
oob_data[oob + j] = readb (this->IO_ADDR_R);
/* Skip ECC, if not active */
if (eccmode == NAND_ECC_NONE)
goto readdata;
/* Pick the ECC bytes out of the oob data */
for (j = 0; j < 6; j++)
ecc_code[j] = oob_data[oob + oob_config[j]];
/* correct data, if neccecary */
ecc_status = this->correct_data (&data_poi[0], &ecc_code[0], &ecc_calc[0]);
/* check, if we have a fs supplied oob-buffer */
if (oob_buf) {
oob += mtd->oobsize;
*((int *)&oob_data[oob]) = ecc_status;
oob += sizeof(int);
}
if (ecc_status == -1) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
ecc_failed++;
}
if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) {
ecc_status = this->correct_data (&data_poi[256], &ecc_code[3], &ecc_calc[3]);
if (oob_buf) {
*((int *)&oob_data[oob]) = ecc_status;
oob += sizeof(int);
}
if (ecc_status == -1) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
ecc_failed++;
}
}
readdata:
if (col || (len - read) < end) {
for (j = col; j < end && read < len; j++)
buf[read++] = data_poi[j];
} else
read += mtd->oobblock;
/* For subsequent reads align to page boundary. */
col = 0;
/* Increment page address */
page++;
}
/* De-select the NAND device */
nand_deselect ();
/* Wake up anyone waiting on the device */
spin_lock_bh (&this->chip_lock);
this->state = FL_READY;
wake_up (&this->wq);
spin_unlock_bh (&this->chip_lock);
/*
* Return success, if no ECC failures, else -EIO
* fs driver will take care of that, because
* retlen == desired len and result == -EIO
*/
*retlen = read;
return ecc_failed ? -EIO : 0;
}
/*
* NAND read out-of-band
*/
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
{
int i, col, page;
int erase_state = 0;
struct nand_chip *this = mtd->priv;
DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
/* Shift to get page */
page = ((int) from) >> this->page_shift;
/* Mask to get column */
col = from & 0x0f;
/* Initialize return length value */
*retlen = 0;
/* Do not allow reads past end of device */
if ((from + len) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
*retlen = 0;
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd , FL_READING, &erase_state);
/* Select the NAND device */
nand_select ();
/* Send the read command */
this->cmdfunc (mtd, NAND_CMD_READOOB, col, page);
/*
* Read the data, if we read more than one page
* oob data, let the device transfer the data !
*/
for (i = 0; i < len; i++) {
buf[i] = readb (this->IO_ADDR_R);
if ((col++ & (mtd->oobsize - 1)) == (mtd->oobsize - 1))
udelay (this->chip_delay);
}
/* De-select the NAND device */
nand_deselect ();
/* Wake up anyone waiting on the device */
spin_lock_bh (&this->chip_lock);
this->state = FL_READY;
wake_up (&this->wq);
spin_unlock_bh (&this->chip_lock);
/* Return happy */
*retlen = len;
return 0;
}
#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
/*
* Use NAND write ECC
*/
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
{
return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
}
/*
* NAND write with ECC
*/
static int nand_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)
{
int page, ret = 0, oob = 0, written = 0;
struct nand_chip *this = mtd->priv;
DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
/* Do not allow write past end of device */
if ((to + len) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
return -EINVAL;
}
/* reject writes, which are not page aligned */
if (NOTALIGNED (to) || NOTALIGNED(len)) {
printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
return -EINVAL;
}
// if oobsel is NULL, use chip defaults
if (oobsel == NULL)
oobsel = &mtd->oobinfo;
/* Shift to get page */
page = ((int) to) >> this->page_shift;
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING, NULL);
/* Select the NAND device */
nand_select ();
/* Check the WP bit */
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
if (!(readb (this->IO_ADDR_R) & 0x80)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n");
ret = -EIO;
goto out;
}
/* Loop until all data is written */
while (written < len) {
int cnt = mtd->oobblock;
this->data_poi = (u_char*) &buf[written];
/* We use the same function for write and writev */
if (eccbuf) {
ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel);
oob += mtd->oobsize;
} else
ret = nand_write_page (mtd, this, page, NULL, oobsel);
if (ret)
goto out;
/* Update written bytes count */
written += cnt;
/* Increment page address */
page++;
}
out:
/* De-select the NAND device */
nand_deselect ();
/* Wake up anyone waiting on the device */
spin_lock_bh (&this->chip_lock);
this->state = FL_READY;
wake_up (&this->wq);
spin_unlock_bh (&this->chip_lock);
*retlen = written;
return ret;
}
/*
* NAND write out-of-band
*/
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
{
int i, column, page, status, ret = 0;
struct nand_chip *this = mtd->priv;
DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
/* Shift to get page */
page = ((int) to) >> this->page_shift;
/* Mask to get column */
column = to & 0x1f;
/* Initialize return length value */
*retlen = 0;
/* Do not allow write past end of page */
if ((column + len) > mtd->oobsize) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING, NULL);
/* Select the NAND device */
nand_select ();
/* Check the WP bit */
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
if (!(readb (this->IO_ADDR_R) & 0x80)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n");
ret = -EIO;
goto out;
}
/* Write out desired data */
this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page);
/* prepad 0xff for partial programming */
for (i = 0; i < column; i++)
writeb (0xff, this->IO_ADDR_W);
/* write data */
for (i = 0; i < len; i++)
writeb (buf[i], this->IO_ADDR_W);
/* postpad 0xff for partial programming */
for (i = len + column; i < mtd->oobsize; i++)
writeb (0xff, this->IO_ADDR_W);
/* Send command to program the OOB data */
this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
status = this->waitfunc (mtd, this, FL_WRITING);
/* See if device thinks it succeeded */
if (status & 0x01) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
ret = -EIO;
goto out;
}
/* Return happy */
*retlen = len;
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/* Send command to read back the data */
this->cmdfunc (mtd, NAND_CMD_READOOB, column, page);
/* Loop through and verify the data */
for (i = 0; i < len; i++) {
if (buf[i] != readb (this->IO_ADDR_R)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
ret = -EIO;
goto out;
}
}
#endif
out:
/* De-select the NAND device */
nand_deselect ();
/* Wake up anyone waiting on the device */
spin_lock_bh (&this->chip_lock);
this->state = FL_READY;
wake_up (&this->wq);
spin_unlock_bh (&this->chip_lock);
return ret;
}
/*
* NAND write with kvec
*/
static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count,
loff_t to, size_t * retlen)
{
return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL));
}
static int nand_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)
{
int i, page, len, total_len, ret = 0, written = 0;
struct nand_chip *this = mtd->priv;
/* Calculate total length of data */
total_len = 0;
for (i = 0; i < count; i++)
total_len += (int) vecs[i].iov_len;
DEBUG (MTD_DEBUG_LEVEL3,
"nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);
/* Do not allow write past end of page */
if ((to + total_len) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n");
return -EINVAL;
}
/* reject writes, which are not page aligned */
if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
return -EINVAL;
}
// if oobsel is NULL, use chip defaults
if (oobsel == NULL)
oobsel = &mtd->oobinfo;
/* Shift to get page */
page = ((int) to) >> this->page_shift;
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING, NULL);
/* Select the NAND device */
nand_select ();
/* Check the WP bit */
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
if (!(readb (this->IO_ADDR_R) & 0x80)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n");
ret = -EIO;
goto out;
}
/* Loop until all kvec' data has been written */
len = 0;
while (count) {
/*
* Check, if the tuple gives us not enough data for a
* full page write. Then we can use the iov direct,
* else we have to copy into data_buf.
*/
if ((vecs->iov_len - len) >= mtd->oobblock) {
this->data_poi = vecs->iov_base;
this->data_poi += len;
len += mtd->oobblock;
/* Check, if we have to switch to the next tuple */
if (len >= (int) vecs->iov_len) {
vecs++;
len = 0;
count--;
}
} else {
/*
* Read data out of each tuple until we have a full page
* to write or we've read all the tuples.
*/
int cnt = 0;
while ((cnt < mtd->oobblock) && count) {
if (vecs->iov_base != NULL && vecs->iov_len) {
this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
}
/* Check, if we have to switch to the next tuple */
if (len >= (int) vecs->iov_len) {
vecs++;
len = 0;
count--;
}
}
this->data_poi = this->data_buf;
}
/* We use the same function for write and writev !) */
ret = nand_write_page (mtd, this, page, NULL, oobsel);
if (ret)
goto out;
/* Update written bytes count */
written += mtd->oobblock;
/* Increment page address */
page++;
}
out:
/* De-select the NAND device */
nand_deselect ();
/* Wake up anyone waiting on the device */
spin_lock_bh (&this->chip_lock);
this->state = FL_READY;
wake_up (&this->wq);
spin_unlock_bh (&this->chip_lock);
*retlen = written;
return ret;
}
/*
* NAND erase a block
*/
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
{
int page, len, status, pages_per_block, ret;
struct nand_chip *this = mtd->priv;
DECLARE_WAITQUEUE (wait, current);
DEBUG (MTD_DEBUG_LEVEL3,
"nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
/* Start address must align on block boundary */
if (instr->addr & (mtd->erasesize - 1)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
return -EINVAL;
}
/* Length must align on block boundary */
if (instr->len & (mtd->erasesize - 1)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
return -EINVAL;
}
/* Do not allow erase past end of device */
if ((instr->len + instr->addr) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_ERASING, NULL);
/* Shift to get first page */
page = (int) (instr->addr >> this->page_shift);
/* Calculate pages in each block */
pages_per_block = mtd->erasesize / mtd->oobblock;
/* Select the NAND device */
nand_select ();
/* Check the WP bit */
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
if (!(readb (this->IO_ADDR_R) & 0x80)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
}
/* Loop through the pages */
len = instr->len;
instr->state = MTD_ERASING;
while (len) {
/* Check if we have a bad block, we do not erase bad blocks ! */
this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page);
if (readb (this->IO_ADDR_R) != 0xff) {
printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
}
/* Send commands to erase a page */
this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
spin_unlock_bh (&this->chip_lock);
status = this->waitfunc (mtd, this, FL_ERASING);
/* Get spinlock, in case we exit */
spin_lock_bh (&this->chip_lock);
/* See if block erase succeeded */
if (status & 0x01) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
}
/* Check, if we were interupted */
if (this->state == FL_ERASING) {
/* Increment page address and decrement length */
len -= mtd->erasesize;
page += pages_per_block;
}
/* Release the spin lock */
spin_unlock_bh (&this->chip_lock);
erase_retry:
spin_lock_bh (&this->chip_lock);
/* Check the state and sleep if it changed */
if (this->state == FL_ERASING || this->state == FL_READY) {
/* Select the NAND device again, if we were interrupted */
this->state = FL_ERASING;
nand_select ();
continue;
} else {
set_current_state (TASK_UNINTERRUPTIBLE);
add_wait_queue (&this->wq, &wait);
spin_unlock_bh (&this->chip_lock);
schedule ();
remove_wait_queue (&this->wq, &wait);
goto erase_retry;
}
}
instr->state = MTD_ERASE_DONE;
erase_exit:
/* De-select the NAND device */
nand_deselect ();
spin_unlock_bh (&this->chip_lock);
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
/* Do call back function */
if (!ret && instr->callback)
instr->callback (instr);
/* The device is ready */
spin_lock_bh (&this->chip_lock);
this->state = FL_READY;
spin_unlock_bh (&this->chip_lock);
/* Return more or less happy */
return ret;
}
/*
* NAND sync
*/
static void nand_sync (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
DECLARE_WAITQUEUE (wait, current);
DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
retry:
/* Grab the spinlock */
spin_lock_bh (&this->chip_lock);
/* See what's going on */
switch (this->state) {
case FL_READY:
case FL_SYNCING:
this->state = FL_SYNCING;
spin_unlock_bh (&this->chip_lock);
break;
default:
/* Not an idle state */
add_wait_queue (&this->wq, &wait);
spin_unlock_bh (&this->chip_lock);
schedule ();
remove_wait_queue (&this->wq, &wait);
goto retry;
}
/* Lock the device */
spin_lock_bh (&this->chip_lock);
/* Set the device to be ready again */
if (this->state == FL_SYNCING) {
this->state = FL_READY;
wake_up (&this->wq);
}
/* Unlock the device */
spin_unlock_bh (&this->chip_lock);
}
/*
* Scan for the NAND device
*/
int nand_scan (struct mtd_info *mtd)
{
int i, nand_maf_id, nand_dev_id;
struct nand_chip *this = mtd->priv;
/* check for proper chip_delay setup, set 20us if not */
if (!this->chip_delay)
this->chip_delay = 20;
/* check, if a user supplied command function given */
if (this->cmdfunc == NULL)
this->cmdfunc = nand_command;
/* check, if a user supplied wait function given */
if (this->waitfunc == NULL)
this->waitfunc = nand_wait;
/* Select the device */
nand_select ();
/* Send the command for reading device ID */
this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
nand_maf_id = readb (this->IO_ADDR_R);
nand_dev_id = readb (this->IO_ADDR_R);
/* Print and store flash device information */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) {
mtd->name = nand_flash_ids[i].name;
mtd->erasesize = nand_flash_ids[i].erasesize;
mtd->size = (1 << nand_flash_ids[i].chipshift);
mtd->eccsize = 256;
if (nand_flash_ids[i].page256) {
mtd->oobblock = 256;
mtd->oobsize = 8;
this->page_shift = 8;
} else {
mtd->oobblock = 512;
mtd->oobsize = 16;
this->page_shift = 9;
}
/* Try to identify manufacturer */
for (i = 0; nand_manuf_ids[i].id != 0x0; i++) {
if (nand_manuf_ids[i].id == nand_maf_id)
break;
}
printk (KERN_INFO "NAND device: Manufacture ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
nand_manuf_ids[i].name , mtd->name);
break;
}
}
/*
* check ECC mode, default to software
* if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
* fallback to software ECC
*/
this->eccsize = 256; /* set default eccsize */
switch (this->eccmode) {
case NAND_ECC_HW3_512:
if (mtd->oobblock == 256) {
printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
this->eccmode = NAND_ECC_SOFT;
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
break;
} else
this->eccsize = 512; /* set eccsize to 512 and fall through for function check */
case NAND_ECC_HW3_256:
if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
break;
printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
BUG();
case NAND_ECC_NONE:
printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
this->eccmode = NAND_ECC_NONE;
break;
case NAND_ECC_SOFT:
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
}
/* Initialize state, waitqueue and spinlock */
this->state = FL_READY;
init_waitqueue_head (&this->wq);
spin_lock_init (&this->chip_lock);
/* De-select the device */
nand_deselect ();
/* Print warning message for no device */
if (!mtd->size) {
printk (KERN_WARNING "No NAND device found!!!\n");
return 1;
}
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
mtd->ecctype = MTD_ECC_SW;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_ecc = nand_read_ecc;
mtd->write_ecc = nand_write_ecc;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->readv = NULL;
mtd->writev = nand_writev;
mtd->writev_ecc = nand_writev_ecc;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->suspend = NULL;
mtd->resume = NULL;
mtd->owner = THIS_MODULE;
/* Return happy */
return 0;
}
EXPORT_SYMBOL (nand_scan);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
MODULE_DESCRIPTION ("Generic NAND flash driver code");
/*
* drivers/mtd/nand.c
*
* Overview:
* This is the generic MTD driver for NAND flash devices. It should be
* capable of working with almost all NAND chips currently available.
* Basic support for AG-AND chips is provided.
*
* Additional technical information is available on
* http://www.linux-mtd.infradead.org/tech/nand.html
*
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
* 2002 Thomas Gleixner (tglx@linutronix.de)
*
* 02-08-2004 tglx: support for strange chips, which cannot auto increment
* pages on read / read_oob
*
* 03-17-2004 tglx: Check ready before auto increment check. Simon Bayes
* pointed this out, as he marked an auto increment capable chip
* as NOAUTOINCR in the board driver.
* Make reads over block boundaries work too
*
* 04-14-2004 tglx: first working version for 2k page size chips
*
* 05-19-2004 tglx: Basic support for Renesas AG-AND chips
*
* Credits:
* David Woodhouse for adding multichip support
*
* Aleph One Ltd. and Toby Churchill Ltd. for supporting the
* rework for 2K page size chips
*
* TODO:
* Enable cached programming for 2k page size chips
* Check, if mtd->ecctype should be set to MTD_ECC_HW
* if we have HW ecc support.
* The AG-AND chips have nice features for speed improvement,
* which are not supported yet. Read / program 4 pages in one go.
*
* $Id: nand_base.c,v 1.113 2004/07/14 16:31:31 gleixner Exp $
*
* 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
* published by the Free Software Foundation.
*
*/
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/sched.h>
#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/interrupt.h>
#include <linux/bitops.h>
#include <asm/io.h>
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
#include <linux/mtd/partitions.h>
#endif
/* Define default oob placement schemes for large and small page devices */
static struct nand_oobinfo nand_oob_8 = {
.useecc = MTD_NANDECC_AUTOPLACE,
.eccbytes = 3,
.eccpos = {0, 1, 2},
.oobfree = { {3, 2}, {6, 2} }
};
static struct nand_oobinfo nand_oob_16 = {
.useecc = MTD_NANDECC_AUTOPLACE,
.eccbytes = 6,
.eccpos = {0, 1, 2, 3, 6, 7},
.oobfree = { {8, 8} }
};
static struct nand_oobinfo nand_oob_64 = {
.useecc = MTD_NANDECC_AUTOPLACE,
.eccbytes = 24,
.eccpos = {
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63},
.oobfree = { {2, 38} }
};
/* This is used for padding purposes in nand_write_oob */
static u_char ffchars[] = {
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
};
/*
* NAND low-level MTD interface functions
*/
static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len);
static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len);
static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len);
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
static int nand_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);
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf);
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf);
static int nand_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);
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf);
static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs,
unsigned long count, loff_t to, size_t * retlen);
static int nand_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);
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr);
static void nand_sync (struct mtd_info *mtd);
/* Some internal functions */
static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf,
struct nand_oobinfo *oobsel, int mode);
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages,
u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode);
#else
#define nand_verify_pages(...) (0)
#endif
static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state);
/**
* nand_release_chip - [GENERIC] release chip
* @mtd: MTD device structure
*
* Deselect, release chip lock and wake up anyone waiting on the device
*/
static void nand_release_chip (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
/* De-select the NAND device */
this->select_chip(mtd, -1);
/* Release the chip */
spin_lock_bh (&this->chip_lock);
this->state = FL_READY;
wake_up (&this->wq);
spin_unlock_bh (&this->chip_lock);
}
/**
* nand_read_byte - [DEFAULT] read one byte from the chip
* @mtd: MTD device structure
*
* Default read function for 8bit buswith
*/
static u_char nand_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
return readb(this->IO_ADDR_R);
}
/**
* nand_write_byte - [DEFAULT] write one byte to the chip
* @mtd: MTD device structure
* @byte: pointer to data byte to write
*
* Default write function for 8it buswith
*/
static void nand_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd->priv;
writeb(byte, this->IO_ADDR_W);
}
/**
* nand_read_byte16 - [DEFAULT] read one byte endianess aware from the chip
* @mtd: MTD device structure
*
* Default read function for 16bit buswith with
* endianess conversion
*/
static u_char nand_read_byte16(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
return (u_char) cpu_to_le16(readw(this->IO_ADDR_R));
}
/**
* nand_write_byte16 - [DEFAULT] write one byte endianess aware to the chip
* @mtd: MTD device structure
* @byte: pointer to data byte to write
*
* Default write function for 16bit buswith with
* endianess conversion
*/
static void nand_write_byte16(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd->priv;
writew(le16_to_cpu((u16) byte), this->IO_ADDR_W);
}
/**
* nand_read_word - [DEFAULT] read one word from the chip
* @mtd: MTD device structure
*
* Default read function for 16bit buswith without
* endianess conversion
*/
static u16 nand_read_word(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
return readw(this->IO_ADDR_R);
}
/**
* nand_write_word - [DEFAULT] write one word to the chip
* @mtd: MTD device structure
* @word: data word to write
*
* Default write function for 16bit buswith without
* endianess conversion
*/
static void nand_write_word(struct mtd_info *mtd, u16 word)
{
struct nand_chip *this = mtd->priv;
writew(word, this->IO_ADDR_W);
}
/**
* nand_select_chip - [DEFAULT] control CE line
* @mtd: MTD device structure
* @chip: chipnumber to select, -1 for deselect
*
* Default select function for 1 chip devices.
*/
static void nand_select_chip(struct mtd_info *mtd, int chip)
{
struct nand_chip *this = mtd->priv;
switch(chip) {
case -1:
this->hwcontrol(mtd, NAND_CTL_CLRNCE);
break;
case 0:
this->hwcontrol(mtd, NAND_CTL_SETNCE);
break;
default:
BUG();
}
}
/**
* nand_write_buf - [DEFAULT] write buffer to chip
* @mtd: MTD device structure
* @buf: data buffer
* @len: number of bytes to write
*
* Default write function for 8bit buswith
*/
static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
writeb(buf[i], this->IO_ADDR_W);
}
/**
* nand_read_buf - [DEFAULT] read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
* @len: number of bytes to read
*
* Default read function for 8bit buswith
*/
static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
buf[i] = readb(this->IO_ADDR_R);
}
/**
* nand_verify_buf - [DEFAULT] Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*
* Default verify function for 8bit buswith
*/
static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
if (buf[i] != readb(this->IO_ADDR_R))
return -EFAULT;
return 0;
}
/**
* nand_write_buf16 - [DEFAULT] write buffer to chip
* @mtd: MTD device structure
* @buf: data buffer
* @len: number of bytes to write
*
* Default write function for 16bit buswith
*/
static void nand_write_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
for (i=0; i<len; i++)
writew(p[i], this->IO_ADDR_W);
}
/**
* nand_read_buf16 - [DEFAULT] read chip data into buffer
* @mtd: MTD device structure
* @buf: buffer to store date
* @len: number of bytes to read
*
* Default read function for 16bit buswith
*/
static void nand_read_buf16(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
for (i=0; i<len; i++)
p[i] = readw(this->IO_ADDR_R);
}
/**
* nand_verify_buf16 - [DEFAULT] Verify chip data against buffer
* @mtd: MTD device structure
* @buf: buffer containing the data to compare
* @len: number of bytes to compare
*
* Default verify function for 16bit buswith
*/
static int nand_verify_buf16(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
u16 *p = (u16 *) buf;
len >>= 1;
for (i=0; i<len; i++)
if (p[i] != readw(this->IO_ADDR_R))
return -EFAULT;
return 0;
}
/**
* nand_block_bad - [DEFAULT] Read bad block marker from the chip
* @mtd: MTD device structure
* @ofs: offset from device start
* @getchip: 0, if the chip is already selected
*
* Check, if the block is bad.
*/
static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip)
{
int page, chipnr, res = 0;
struct nand_chip *this = mtd->priv;
u16 bad;
if (getchip) {
page = (int)(ofs >> this->page_shift);
chipnr = (int)(ofs >> this->chip_shift);
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_READING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
} else
page = (int) ofs;
if (this->options & NAND_BUSWIDTH_16) {
this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos & 0xFE, page & this->pagemask);
bad = cpu_to_le16(this->read_word(mtd));
if (this->badblockpos & 0x1)
bad >>= 1;
if ((bad & 0xFF) != 0xff)
res = 1;
} else {
this->cmdfunc (mtd, NAND_CMD_READOOB, this->badblockpos, page & this->pagemask);
if (this->read_byte(mtd) != 0xff)
res = 1;
}
if (getchip) {
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
}
return res;
}
/**
* nand_default_block_markbad - [DEFAULT] mark a block bad
* @mtd: MTD device structure
* @ofs: offset from device start
*
* This is the default implementation, which can be overridden by
* a hardware specific driver.
*/
static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *this = mtd->priv;
u_char buf[2] = {0, 0};
size_t retlen;
int block;
/* Get block number */
block = ((int) ofs) >> this->bbt_erase_shift;
this->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
/* Do we have a flash based bad block table ? */
if (this->options & NAND_USE_FLASH_BBT)
return nand_update_bbt (mtd, ofs);
/* We write two bytes, so we dont have to mess with 16 bit access */
ofs += mtd->oobsize + (this->badblockpos & ~0x01);
return nand_write_oob (mtd, ofs , 2, &retlen, buf);
}
/**
* nand_check_wp - [GENERIC] check if the chip is write protected
* @mtd: MTD device structure
* Check, if the device is write protected
*
* The function expects, that the device is already selected
*/
static int nand_check_wp (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
/* Check the WP bit */
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
return (this->read_byte(mtd) & 0x80) ? 0 : 1;
}
/**
* nand_block_checkbad - [GENERIC] Check if a block is marked bad
* @mtd: MTD device structure
* @ofs: offset from device start
* @getchip: 0, if the chip is already selected
* @allowbbt: 1, if its allowed to access the bbt area
*
* Check, if the block is bad. Either by reading the bad block table or
* calling of the scan function.
*/
static int nand_block_checkbad (struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
{
struct nand_chip *this = mtd->priv;
if (!this->bbt)
return this->block_bad(mtd, ofs, getchip);
/* Return info from the table */
return nand_isbad_bbt (mtd, ofs, allowbbt);
}
/**
* nand_command - [DEFAULT] Send command to NAND device
* @mtd: MTD device structure
* @command: the command to be sent
* @column: the column address for this command, -1 if none
* @page_addr: the page address for this command, -1 if none
*
* Send command to NAND device. This function is used for small page
* devices (256/512 Bytes per page)
*/
static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
/* Begin command latch cycle */
this->hwcontrol(mtd, NAND_CTL_SETCLE);
/*
* Write out the command to the device.
*/
if (command == NAND_CMD_SEQIN) {
int readcmd;
if (column >= mtd->oobblock) {
/* OOB area */
column -= mtd->oobblock;
readcmd = NAND_CMD_READOOB;
} else if (column < 256) {
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0;
} else {
column -= 256;
readcmd = NAND_CMD_READ1;
}
this->write_byte(mtd, readcmd);
}
this->write_byte(mtd, command);
/* Set ALE and clear CLE to start address cycle */
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
if (column != -1 || page_addr != -1) {
this->hwcontrol(mtd, NAND_CTL_SETALE);
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
if (this->options & NAND_BUSWIDTH_16)
column >>= 1;
this->write_byte(mtd, column);
}
if (page_addr != -1) {
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
/* One more address cycle for higher density devices */
if (this->chipsize & 0x0c000000)
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
this->hwcontrol(mtd, NAND_CTL_CLRALE);
}
/*
* program and erase have their own busy handlers
* status and sequential in needs no delay
*/
switch (command) {
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET:
if (this->dev_ready)
break;
udelay(this->chip_delay);
this->hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_STATUS);
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
while ( !(this->read_byte(mtd) & 0x40));
return;
/* This applies to read commands */
default:
/*
* If we don't have access to the busy pin, we apply the given
* command delay
*/
if (!this->dev_ready) {
udelay (this->chip_delay);
return;
}
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
ndelay (100);
/* wait until command is processed */
while (!this->dev_ready(mtd));
}
/**
* nand_command_lp - [DEFAULT] Send command to NAND large page device
* @mtd: MTD device structure
* @command: the command to be sent
* @column: the column address for this command, -1 if none
* @page_addr: the page address for this command, -1 if none
*
* Send command to NAND device. This is the version for the new large page devices
* We dont have the seperate regions as we have in the small page devices.
* We must emulate NAND_CMD_READOOB to keep the code compatible.
*
*/
static void nand_command_lp (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
/* Emulate NAND_CMD_READOOB */
if (command == NAND_CMD_READOOB) {
column += mtd->oobblock;
command = NAND_CMD_READ0;
}
/* Begin command latch cycle */
this->hwcontrol(mtd, NAND_CTL_SETCLE);
/* Write out the command to the device. */
this->write_byte(mtd, command);
/* End command latch cycle */
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
if (column != -1 || page_addr != -1) {
this->hwcontrol(mtd, NAND_CTL_SETALE);
/* Serially input address */
if (column != -1) {
/* Adjust columns for 16 bit buswidth */
if (this->options & NAND_BUSWIDTH_16)
column >>= 1;
this->write_byte(mtd, column & 0xff);
this->write_byte(mtd, column >> 8);
}
if (page_addr != -1) {
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
/* One more address cycle for devices > 128MiB */
if (this->chipsize > (128 << 20))
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0xff));
}
/* Latch in address */
this->hwcontrol(mtd, NAND_CTL_CLRALE);
}
/*
* program and erase have their own busy handlers
* status and sequential in needs no delay
*/
switch (command) {
case NAND_CMD_CACHEDPROG:
case NAND_CMD_PAGEPROG:
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_SEQIN:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET:
if (this->dev_ready)
break;
udelay(this->chip_delay);
this->hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_STATUS);
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
while ( !(this->read_byte(mtd) & 0x40));
return;
case NAND_CMD_READ0:
/* Begin command latch cycle */
this->hwcontrol(mtd, NAND_CTL_SETCLE);
/* Write out the start read command */
this->write_byte(mtd, NAND_CMD_READSTART);
/* End command latch cycle */
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
/* Fall through into ready check */
/* This applies to read commands */
default:
/*
* If we don't have access to the busy pin, we apply the given
* command delay
*/
if (!this->dev_ready) {
udelay (this->chip_delay);
return;
}
}
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
ndelay (100);
/* wait until command is processed */
while (!this->dev_ready(mtd));
}
/**
* nand_get_chip - [GENERIC] Get chip for selected access
* @this: the nand chip descriptor
* @mtd: MTD device structure
* @new_state: the state which is requested
*
* Get the device and lock it for exclusive access
*/
static void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state)
{
DECLARE_WAITQUEUE (wait, current);
/*
* Grab the lock and see if the device is available
*/
retry:
spin_lock_bh (&this->chip_lock);
if (this->state == FL_READY) {
this->state = new_state;
spin_unlock_bh (&this->chip_lock);
return;
}
set_current_state (TASK_UNINTERRUPTIBLE);
add_wait_queue (&this->wq, &wait);
spin_unlock_bh (&this->chip_lock);
schedule ();
remove_wait_queue (&this->wq, &wait);
goto retry;
}
/**
* nand_wait - [DEFAULT] wait until the command is done
* @mtd: MTD device structure
* @this: NAND chip structure
* @state: state to select the max. timeout value
*
* Wait for command done. This applies to erase and program only
* Erase can take up to 400ms and program up to 20ms according to
* general NAND and SmartMedia specs
*
*/
static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state)
{
unsigned long timeo = jiffies;
int status;
if (state == FL_ERASING)
timeo += (HZ * 400) / 1000;
else
timeo += (HZ * 20) / 1000;
/* Apply this short delay always to ensure that we do wait tWB in
* any case on any machine. */
ndelay (100);
spin_lock_bh (&this->chip_lock);
if ((state == FL_ERASING) && (this->options & NAND_IS_AND))
this->cmdfunc (mtd, NAND_CMD_STATUS_MULTI, -1, -1);
else
this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1);
while (time_before(jiffies, timeo)) {
/* Check, if we were interrupted */
if (this->state != state) {
spin_unlock_bh (&this->chip_lock);
return 0;
}
if (this->dev_ready) {
if (this->dev_ready(mtd))
break;
}
if (this->read_byte(mtd) & NAND_STATUS_READY)
break;
spin_unlock_bh (&this->chip_lock);
yield ();
spin_lock_bh (&this->chip_lock);
}
status = (int) this->read_byte(mtd);
spin_unlock_bh (&this->chip_lock);
return status;
}
/**
* nand_write_page - [GENERIC] write one page
* @mtd: MTD device structure
* @this: NAND chip structure
* @page: startpage inside the chip, must be called with (page & this->pagemask)
* @oob_buf: out of band data buffer
* @oobsel: out of band selecttion structre
* @cached: 1 = enable cached programming if supported by chip
*
* Nand_page_program function is used for write and writev !
* This function will always program a full page of data
* If you call it with a non page aligned buffer, you're lost :)
*
* Cached programming is not supported yet.
*/
static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page,
u_char *oob_buf, struct nand_oobinfo *oobsel, int cached)
{
int i, status;
u_char ecc_code[8];
int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
int *oob_config = oobsel->eccpos;
int datidx = 0, eccidx = 0, eccsteps = this->eccsteps;
int eccbytes = 0;
/* FIXME: Enable cached programming */
cached = 0;
/* Send command to begin auto page programming */
this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page);
/* Write out complete page of data, take care of eccmode */
switch (eccmode) {
/* No ecc, write all */
case NAND_ECC_NONE:
printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n");
this->write_buf(mtd, this->data_poi, mtd->oobblock);
break;
/* Software ecc 3/256, write all */
case NAND_ECC_SOFT:
for (; eccsteps; eccsteps--) {
this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
for (i = 0; i < 3; i++, eccidx++)
oob_buf[oob_config[eccidx]] = ecc_code[i];
datidx += this->eccsize;
}
this->write_buf(mtd, this->data_poi, mtd->oobblock);
break;
/* Hardware ecc 8 byte / 512 byte data */
case NAND_ECC_HW8_512:
eccbytes += 2;
/* Hardware ecc 6 byte / 512 byte data */
case NAND_ECC_HW6_512:
eccbytes += 3;
/* Hardware ecc 3 byte / 256 data */
/* Hardware ecc 3 byte / 512 byte data */
case NAND_ECC_HW3_256:
case NAND_ECC_HW3_512:
eccbytes += 3;
for (; eccsteps; eccsteps--) {
/* enable hardware ecc logic for write */
this->enable_hwecc(mtd, NAND_ECC_WRITE);
this->write_buf(mtd, &this->data_poi[datidx], this->eccsize);
this->calculate_ecc(mtd, &this->data_poi[datidx], ecc_code);
for (i = 0; i < eccbytes; i++, eccidx++)
oob_buf[oob_config[eccidx]] = ecc_code[i];
/* If the hardware ecc provides syndromes then
* the ecc code must be written immidiately after
* the data bytes (words) */
if (this->options & NAND_HWECC_SYNDROME)
this->write_buf(mtd, ecc_code, eccbytes);
datidx += this->eccsize;
}
break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
}
/* Write out OOB data */
if (this->options & NAND_HWECC_SYNDROME)
this->write_buf(mtd, &oob_buf[oobsel->eccbytes], mtd->oobsize - oobsel->eccbytes);
else
this->write_buf(mtd, oob_buf, mtd->oobsize);
/* Send command to actually program the data */
this->cmdfunc (mtd, cached ? NAND_CMD_CACHEDPROG : NAND_CMD_PAGEPROG, -1, -1);
if (!cached) {
/* call wait ready function */
status = this->waitfunc (mtd, this, FL_WRITING);
/* See if device thinks it succeeded */
if (status & 0x01) {
DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page);
return -EIO;
}
} else {
/* FIXME: Implement cached programming ! */
/* wait until cache is ready*/
// status = this->waitfunc (mtd, this, FL_CACHEDRPG);
}
return 0;
}
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/**
* nand_verify_pages - [GENERIC] verify the chip contents after a write
* @mtd: MTD device structure
* @this: NAND chip structure
* @page: startpage inside the chip, must be called with (page & this->pagemask)
* @numpages: number of pages to verify
* @oob_buf: out of band data buffer
* @oobsel: out of band selecttion structre
* @chipnr: number of the current chip
* @oobmode: 1 = full buffer verify, 0 = ecc only
*
* The NAND device assumes that it is always writing to a cleanly erased page.
* Hence, it performs its internal write verification only on bits that
* transitioned from 1 to 0. The device does NOT verify the whole page on a
* byte by byte basis. It is possible that the page was not completely erased
* or the page is becoming unusable due to wear. The read with ECC would catch
* the error later when the ECC page check fails, but we would rather catch
* it early in the page write stage. Better to write no data than invalid data.
*/
static int nand_verify_pages (struct mtd_info *mtd, struct nand_chip *this, int page, int numpages,
u_char *oob_buf, struct nand_oobinfo *oobsel, int chipnr, int oobmode)
{
int i, j, datidx = 0, oobofs = 0, res = -EIO;
int eccsteps = this->eccsteps;
int hweccbytes;
u_char oobdata[64];
hweccbytes = (this->options & NAND_HWECC_SYNDROME) ? (oobsel->eccbytes / eccsteps) : 0;
/* Send command to read back the first page */
this->cmdfunc (mtd, NAND_CMD_READ0, 0, page);
for(;;) {
for (j = 0; j < eccsteps; j++) {
/* Loop through and verify the data */
if (this->verify_buf(mtd, &this->data_poi[datidx], mtd->eccsize)) {
DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
goto out;
}
datidx += mtd->eccsize;
/* Have we a hw generator layout ? */
if (!hweccbytes)
continue;
if (this->verify_buf(mtd, &this->oob_buf[oobofs], hweccbytes)) {
DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
goto out;
}
oobofs += hweccbytes;
}
/* check, if we must compare all data or if we just have to
* compare the ecc bytes
*/
if (oobmode) {
if (this->verify_buf(mtd, &oob_buf[oobofs], mtd->oobsize - hweccbytes * eccsteps)) {
DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page);
goto out;
}
} else {
/* Read always, else autoincrement fails */
this->read_buf(mtd, oobdata, mtd->oobsize - hweccbytes * eccsteps);
if (oobsel->useecc != MTD_NANDECC_OFF && !hweccbytes) {
int ecccnt = oobsel->eccbytes;
for (i = 0; i < ecccnt; i++) {
int idx = oobsel->eccpos[i];
if (oobdata[idx] != oob_buf[oobofs + idx] ) {
DEBUG (MTD_DEBUG_LEVEL0,
"%s: Failed ECC write "
"verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i);
goto out;
}
}
}
}
oobofs += mtd->oobsize - hweccbytes * eccsteps;
page++;
numpages--;
/* Apply delay or wait for ready/busy pin
* Do this before the AUTOINCR check, so no problems
* arise if a chip which does auto increment
* is marked as NOAUTOINCR by the board driver.
* Do this also before returning, so the chip is
* ready for the next command.
*/
if (!this->dev_ready)
udelay (this->chip_delay);
else
while (!this->dev_ready(mtd));
/* All done, return happy */
if (!numpages)
return 0;
/* Check, if the chip supports auto page increment */
if (!NAND_CANAUTOINCR(this))
this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
}
/*
* Terminate the read command. We come here in case of an error
* So we must issue a reset command.
*/
out:
this->cmdfunc (mtd, NAND_CMD_RESET, -1, -1);
return res;
}
#endif
/**
* nand_read - [MTD Interface] MTD compability function for nand_read_ecc
* @mtd: MTD device structure
* @from: offset to read from
* @len: number of bytes to read
* @retlen: pointer to variable to store the number of read bytes
* @buf: the databuffer to put data
*
* This function simply calls nand_read_ecc with oob buffer and oobsel = NULL
*/
static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
{
return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL);
}
/**
* nand_read_ecc - [MTD Interface] Read data with ECC
* @mtd: MTD device structure
* @from: offset to read from
* @len: number of bytes to read
* @retlen: pointer to variable to store the number of read bytes
* @buf: the databuffer to put data
* @oob_buf: filesystem supplied oob data buffer
* @oobsel: oob selection structure
*
* NAND read with ECC
*/
static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len,
size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel)
{
int i, j, col, realpage, page, end, ecc, chipnr, sndcmd = 1;
int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0;
struct nand_chip *this = mtd->priv;
u_char *data_poi, *oob_data = oob_buf;
u_char ecc_calc[32];
u_char ecc_code[32];
int eccmode, eccsteps;
int *oob_config, datidx;
int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
int eccbytes = 3;
int compareecc = 1;
int oobreadlen;
DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
/* Do not allow reads past end of device */
if ((from + len) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n");
*retlen = 0;
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd ,FL_READING);
/* use userspace supplied oobinfo, if zero */
if (oobsel == NULL)
oobsel = &mtd->oobinfo;
/* Autoplace of oob data ? Use the default placement scheme */
if (oobsel->useecc == MTD_NANDECC_AUTOPLACE)
oobsel = this->autooob;
eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE;
oob_config = oobsel->eccpos;
/* Select the NAND device */
chipnr = (int)(from >> this->chip_shift);
this->select_chip(mtd, chipnr);
/* First we calculate the starting page */
realpage = (int) (from >> this->page_shift);
page = realpage & this->pagemask;
/* Get raw starting column */
col = from & (mtd->oobblock - 1);
end = mtd->oobblock;
ecc = this->eccsize;
switch (eccmode) {
case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */
eccbytes = 6;
break;
case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */
eccbytes = 8;
break;
case NAND_ECC_NONE:
compareecc = 0;
break;
}
if (this->options & NAND_HWECC_SYNDROME)
compareecc = 0;
oobreadlen = mtd->oobsize;
if (this->options & NAND_HWECC_SYNDROME)
oobreadlen -= oobsel->eccbytes;
/* Loop until all data read */
while (read < len) {
int aligned = (!col && (len - read) >= end);
/*
* If the read is not page aligned, we have to read into data buffer
* due to ecc, else we read into return buffer direct
*/
if (aligned)
data_poi = &buf[read];
else
data_poi = this->data_buf;
/* Check, if we have this page in the buffer
*
* FIXME: Make it work when we must provide oob data too,
* check the usage of data_buf oob field
*/
if (realpage == this->pagebuf && !oob_buf) {
/* aligned read ? */
if (aligned)
memcpy (data_poi, this->data_buf, end);
goto readdata;
}
/* Check, if we must send the read command */
if (sndcmd) {
this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page);
sndcmd = 0;
}
/* get oob area, if we have no oob buffer from fs-driver */
if (!oob_buf || oobsel->useecc == MTD_NANDECC_AUTOPLACE)
oob_data = &this->data_buf[end];
eccsteps = this->eccsteps;
switch (eccmode) {
case NAND_ECC_NONE: { /* No ECC, Read in a page */
static unsigned long lastwhinge = 0;
if ((lastwhinge / HZ) != (jiffies / HZ)) {
printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n");
lastwhinge = jiffies;
}
this->read_buf(mtd, data_poi, end);
break;
}
case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */
this->read_buf(mtd, data_poi, end);
for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=3, datidx += ecc)
this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
break;
case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data */
case NAND_ECC_HW3_512: /* Hardware ECC 3 byte /512 byte data */
case NAND_ECC_HW6_512: /* Hardware ECC 6 byte / 512 byte data */
case NAND_ECC_HW8_512: /* Hardware ECC 8 byte / 512 byte data */
for (i = 0, datidx = 0; eccsteps; eccsteps--, i+=eccbytes, datidx += ecc) {
this->enable_hwecc(mtd, NAND_ECC_READ);
this->read_buf(mtd, &data_poi[datidx], ecc);
/* HW ecc with syndrome calculation must read the
* syndrome from flash immidiately after the data */
if (!compareecc) {
/* Some hw ecc generators need to know when the
* syndrome is read from flash */
this->enable_hwecc(mtd, NAND_ECC_READSYN);
this->read_buf(mtd, &oob_data[i], eccbytes);
/* We calc error correction directly, it checks the hw
* generator for an error, reads back the syndrome and
* does the error correction on the fly */
if (this->correct_data(mtd, &data_poi[datidx], &oob_data[i], &ecc_code[i]) == -1) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: "
"Failed ECC read, page 0x%08x on chip %d\n", page, chipnr);
ecc_failed++;
}
} else {
this->calculate_ecc(mtd, &data_poi[datidx], &ecc_calc[i]);
}
}
break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
}
/* read oobdata */
this->read_buf(mtd, &oob_data[mtd->oobsize - oobreadlen], oobreadlen);
/* Skip ECC check, if not requested (ECC_NONE or HW_ECC with syndromes) */
if (!compareecc)
goto readoob;
/* Pick the ECC bytes out of the oob data */
for (j = 0; j < oobsel->eccbytes; j++)
ecc_code[j] = oob_data[oob_config[j]];
/* correct data, if neccecary */
for (i = 0, j = 0, datidx = 0; i < this->eccsteps; i++, datidx += ecc) {
ecc_status = this->correct_data(mtd, &data_poi[datidx], &ecc_code[j], &ecc_calc[j]);
/* Get next chunk of ecc bytes */
j += eccbytes;
/* Check, if we have a fs supplied oob-buffer,
* This is the legacy mode. Used by YAFFS1
* Should go away some day
*/
if (oob_buf && oobsel->useecc == MTD_NANDECC_PLACE) {
int *p = (int *)(&oob_data[mtd->oobsize]);
p[i] = ecc_status;
}
if (ecc_status == -1) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page);
ecc_failed++;
}
}
readoob:
/* check, if we have a fs supplied oob-buffer */
if (oob_buf) {
/* without autoplace. Legacy mode used by YAFFS1 */
switch(oobsel->useecc) {
case MTD_NANDECC_AUTOPLACE:
/* Walk through the autoplace chunks */
for (i = 0, j = 0; j < mtd->oobavail; i++) {
int from = oobsel->oobfree[i][0];
int num = oobsel->oobfree[i][1];
memcpy(&oob_buf[oob], &oob_data[from], num);
j+= num;
}
oob += mtd->oobavail;
break;
case MTD_NANDECC_PLACE:
/* YAFFS1 legacy mode */
oob_data += this->eccsteps * sizeof (int);
default:
oob_data += mtd->oobsize;
}
}
readdata:
/* Partial page read, transfer data into fs buffer */
if (!aligned) {
for (j = col; j < end && read < len; j++)
buf[read++] = data_poi[j];
this->pagebuf = realpage;
} else
read += mtd->oobblock;
/* Apply delay or wait for ready/busy pin
* Do this before the AUTOINCR check, so no problems
* arise if a chip which does auto increment
* is marked as NOAUTOINCR by the board driver.
*/
if (!this->dev_ready)
udelay (this->chip_delay);
else
while (!this->dev_ready(mtd));
if (read == len)
break;
/* For subsequent reads align to page boundary. */
col = 0;
/* Increment page address */
realpage++;
page = realpage & this->pagemask;
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
this->select_chip(mtd, -1);
this->select_chip(mtd, chipnr);
}
/* Check, if the chip supports auto page increment
* or if we have hit a block boundary.
*/
if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
sndcmd = 1;
}
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
/*
* Return success, if no ECC failures, else -EIO
* fs driver will take care of that, because
* retlen == desired len and result == -EIO
*/
*retlen = read;
return ecc_failed ? -EIO : 0;
}
/**
* nand_read_oob - [MTD Interface] NAND read out-of-band
* @mtd: MTD device structure
* @from: offset to read from
* @len: number of bytes to read
* @retlen: pointer to variable to store the number of read bytes
* @buf: the databuffer to put data
*
* NAND read out-of-band data from the spare area
*/
static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf)
{
int i, col, page, chipnr;
struct nand_chip *this = mtd->priv;
int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len);
/* Shift to get page */
page = (int)(from >> this->page_shift);
chipnr = (int)(from >> this->chip_shift);
/* Mask to get column */
col = from & (mtd->oobsize - 1);
/* Initialize return length value */
*retlen = 0;
/* Do not allow reads past end of device */
if ((from + len) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n");
*retlen = 0;
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd , FL_READING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
/* Send the read command */
this->cmdfunc (mtd, NAND_CMD_READOOB, col, page & this->pagemask);
/*
* Read the data, if we read more than one page
* oob data, let the device transfer the data !
*/
i = 0;
while (i < len) {
int thislen = mtd->oobsize - col;
thislen = min_t(int, thislen, len);
this->read_buf(mtd, &buf[i], thislen);
i += thislen;
/* Apply delay or wait for ready/busy pin
* Do this before the AUTOINCR check, so no problems
* arise if a chip which does auto increment
* is marked as NOAUTOINCR by the board driver.
*/
if (!this->dev_ready)
udelay (this->chip_delay);
else
while (!this->dev_ready(mtd));
/* Read more ? */
if (i < len) {
page++;
col = 0;
/* Check, if we cross a chip boundary */
if (!(page & this->pagemask)) {
chipnr++;
this->select_chip(mtd, -1);
this->select_chip(mtd, chipnr);
}
/* Check, if the chip supports auto page increment
* or if we have hit a block boundary.
*/
if (!NAND_CANAUTOINCR(this) || !(page & blockcheck)) {
/* For subsequent page reads set offset to 0 */
this->cmdfunc (mtd, NAND_CMD_READOOB, 0x0, page & this->pagemask);
}
}
}
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
/* Return happy */
*retlen = len;
return 0;
}
/**
* nand_read_raw - [GENERIC] Read raw data including oob into buffer
* @mtd: MTD device structure
* @buf: temporary buffer
* @from: offset to read from
* @len: number of bytes to read
* @ooblen: number of oob data bytes to read
*
* Read raw data including oob into buffer
*/
int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen)
{
struct nand_chip *this = mtd->priv;
int page = (int) (from >> this->page_shift);
int chip = (int) (from >> this->chip_shift);
int sndcmd = 1;
int cnt = 0;
int pagesize = mtd->oobblock + mtd->oobsize;
int blockcheck = (1 << (this->phys_erase_shift - this->page_shift)) - 1;
/* Do not allow reads past end of device */
if ((from + len) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_read_raw: Attempt read beyond end of device\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd , FL_READING);
this->select_chip (mtd, chip);
/* Add requested oob length */
len += ooblen;
while (len) {
if (sndcmd)
this->cmdfunc (mtd, NAND_CMD_READ0, 0, page & this->pagemask);
sndcmd = 0;
this->read_buf (mtd, &buf[cnt], pagesize);
len -= pagesize;
cnt += pagesize;
page++;
if (!this->dev_ready)
udelay (this->chip_delay);
else
while (!this->dev_ready(mtd));
/* Check, if the chip supports auto page increment */
if (!NAND_CANAUTOINCR(this) || !(page & blockcheck))
sndcmd = 1;
}
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
return 0;
}
/**
* nand_prepare_oobbuf - [GENERIC] Prepare the out of band buffer
* @mtd: MTD device structure
* @fsbuf: buffer given by fs driver
* @oobsel: out of band selection structre
* @autoplace: 1 = place given buffer into the oob bytes
* @numpages: number of pages to prepare
*
* Return:
* 1. Filesystem buffer available and autoplacement is off,
* return filesystem buffer
* 2. No filesystem buffer or autoplace is off, return internal
* buffer
* 3. Filesystem buffer is given and autoplace selected
* put data from fs buffer into internal buffer and
* retrun internal buffer
*
* Note: The internal buffer is filled with 0xff. This must
* be done only once, when no autoplacement happens
* Autoplacement sets the buffer dirty flag, which
* forces the 0xff fill before using the buffer again.
*
*/
static u_char * nand_prepare_oobbuf (struct mtd_info *mtd, u_char *fsbuf, struct nand_oobinfo *oobsel,
int autoplace, int numpages)
{
struct nand_chip *this = mtd->priv;
int i, len, ofs;
/* Zero copy fs supplied buffer */
if (fsbuf && !autoplace)
return fsbuf;
/* Check, if the buffer must be filled with ff again */
if (this->oobdirty) {
memset (this->oob_buf, 0xff,
mtd->oobsize << (this->phys_erase_shift - this->page_shift));
this->oobdirty = 0;
}
/* If we have no autoplacement or no fs buffer use the internal one */
if (!autoplace || !fsbuf)
return this->oob_buf;
/* Walk through the pages and place the data */
this->oobdirty = 1;
ofs = 0;
while (numpages--) {
for (i = 0, len = 0; len < mtd->oobavail; i++) {
int to = ofs + oobsel->oobfree[i][0];
int num = oobsel->oobfree[i][1];
memcpy (&this->oob_buf[to], fsbuf, num);
len += num;
fsbuf += num;
}
ofs += mtd->oobavail;
}
return this->oob_buf;
}
#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0
/**
* nand_write - [MTD Interface] compability function for nand_write_ecc
* @mtd: MTD device structure
* @to: offset to write to
* @len: number of bytes to write
* @retlen: pointer to variable to store the number of written bytes
* @buf: the data to write
*
* This function simply calls nand_write_ecc with oob buffer and oobsel = NULL
*
*/
static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
{
return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL));
}
/**
* nand_write_ecc - [MTD Interface] NAND write with ECC
* @mtd: MTD device structure
* @to: offset to write to
* @len: number of bytes to write
* @retlen: pointer to variable to store the number of written bytes
* @buf: the data to write
* @eccbuf: filesystem supplied oob data buffer
* @oobsel: oob selection structure
*
* NAND write with ECC
*/
static int nand_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)
{
int startpage, page, ret = -EIO, oob = 0, written = 0, chipnr;
int autoplace = 0, numpages, totalpages;
struct nand_chip *this = mtd->priv;
u_char *oobbuf, *bufstart;
int ppblock = (1 << (this->phys_erase_shift - this->page_shift));
DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
/* Initialize retlen, in case of early exit */
*retlen = 0;
/* Do not allow write past end of device */
if ((to + len) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n");
return -EINVAL;
}
/* reject writes, which are not page aligned */
if (NOTALIGNED (to) || NOTALIGNED(len)) {
printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING);
/* Calculate chipnr */
chipnr = (int)(to >> this->chip_shift);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
/* Check, if it is write protected */
if (nand_check_wp(mtd))
goto out;
/* if oobsel is NULL, use chip defaults */
if (oobsel == NULL)
oobsel = &mtd->oobinfo;
/* Autoplace of oob data ? Use the default placement scheme */
if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
oobsel = this->autooob;
autoplace = 1;
}
/* Setup variables and oob buffer */
totalpages = len >> this->page_shift;
page = (int) (to >> this->page_shift);
/* Invalidate the page cache, if we write to the cached page */
if (page <= this->pagebuf && this->pagebuf < (page + totalpages))
this->pagebuf = -1;
/* Set it relative to chip */
page &= this->pagemask;
startpage = page;
/* Calc number of pages we can write in one go */
numpages = min (ppblock - (startpage & (ppblock - 1)), totalpages);
oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel, autoplace, numpages);
bufstart = (u_char *)buf;
/* Loop until all data is written */
while (written < len) {
this->data_poi = (u_char*) &buf[written];
/* Write one page. If this is the last page to write
* or the last page in this block, then use the
* real pageprogram command, else select cached programming
* if supported by the chip.
*/
ret = nand_write_page (mtd, this, page, &oobbuf[oob], oobsel, (--numpages > 0));
if (ret) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: write_page failed %d\n", ret);
goto out;
}
/* Next oob page */
oob += mtd->oobsize;
/* Update written bytes count */
written += mtd->oobblock;
if (written == len)
goto cmp;
/* Increment page address */
page++;
/* Have we hit a block boundary ? Then we have to verify and
* if verify is ok, we have to setup the oob buffer for
* the next pages.
*/
if (!(page & (ppblock - 1))){
int ofs;
this->data_poi = bufstart;
ret = nand_verify_pages (mtd, this, startpage,
page - startpage,
oobbuf, oobsel, chipnr, (eccbuf != NULL));
if (ret) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
goto out;
}
*retlen = written;
ofs = autoplace ? mtd->oobavail : mtd->oobsize;
if (eccbuf)
eccbuf += (page - startpage) * ofs;
totalpages -= page - startpage;
numpages = min (totalpages, ppblock);
page &= this->pagemask;
startpage = page;
oobbuf = nand_prepare_oobbuf (mtd, eccbuf, oobsel,
autoplace, numpages);
/* Check, if we cross a chip boundary */
if (!page) {
chipnr++;
this->select_chip(mtd, -1);
this->select_chip(mtd, chipnr);
}
}
}
/* Verify the remaining pages */
cmp:
this->data_poi = bufstart;
ret = nand_verify_pages (mtd, this, startpage, totalpages,
oobbuf, oobsel, chipnr, (eccbuf != NULL));
if (!ret)
*retlen = written;
else
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: verify_pages failed %d\n", ret);
out:
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
return ret;
}
/**
* nand_write_oob - [MTD Interface] NAND write out-of-band
* @mtd: MTD device structure
* @to: offset to write to
* @len: number of bytes to write
* @retlen: pointer to variable to store the number of written bytes
* @buf: the data to write
*
* NAND write out-of-band
*/
static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf)
{
int column, page, status, ret = -EIO, chipnr;
struct nand_chip *this = mtd->priv;
DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len);
/* Shift to get page */
page = (int) (to >> this->page_shift);
chipnr = (int) (to >> this->chip_shift);
/* Mask to get column */
column = to & (mtd->oobsize - 1);
/* Initialize return length value */
*retlen = 0;
/* Do not allow write past end of page */
if ((column + len) > mtd->oobsize) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
/* Reset the chip. Some chips (like the Toshiba TC5832DC found
in one of my DiskOnChip 2000 test units) will clear the whole
data page too if we don't do this. I have no clue why, but
I seem to have 'fixed' it in the doc2000 driver in
August 1999. dwmw2. */
this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1);
/* Check, if it is write protected */
if (nand_check_wp(mtd))
goto out;
/* Invalidate the page cache, if we write to the cached page */
if (page == this->pagebuf)
this->pagebuf = -1;
if (NAND_MUST_PAD(this)) {
/* Write out desired data */
this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page & this->pagemask);
/* prepad 0xff for partial programming */
this->write_buf(mtd, ffchars, column);
/* write data */
this->write_buf(mtd, buf, len);
/* postpad 0xff for partial programming */
this->write_buf(mtd, ffchars, mtd->oobsize - (len+column));
} else {
/* Write out desired data */
this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock + column, page & this->pagemask);
/* write data */
this->write_buf(mtd, buf, len);
}
/* Send command to program the OOB data */
this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1);
status = this->waitfunc (mtd, this, FL_WRITING);
/* See if device thinks it succeeded */
if (status & 0x01) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page);
ret = -EIO;
goto out;
}
/* Return happy */
*retlen = len;
#ifdef CONFIG_MTD_NAND_VERIFY_WRITE
/* Send command to read back the data */
this->cmdfunc (mtd, NAND_CMD_READOOB, column, page & this->pagemask);
if (this->verify_buf(mtd, buf, len)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page);
ret = -EIO;
goto out;
}
#endif
ret = 0;
out:
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
return ret;
}
/**
* nand_writev - [MTD Interface] compabilty function for nand_writev_ecc
* @mtd: MTD device structure
* @vecs: the iovectors to write
* @count: number of vectors
* @to: offset to write to
* @retlen: pointer to variable to store the number of written bytes
*
* NAND write with kvec. This just calls the ecc function
*/
static int nand_writev (struct mtd_info *mtd, const struct kvec *vecs, unsigned long count,
loff_t to, size_t * retlen)
{
return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, NULL));
}
/**
* nand_writev_ecc - [MTD Interface] write with iovec with ecc
* @mtd: MTD device structure
* @vecs: the iovectors to write
* @count: number of vectors
* @to: offset to write to
* @retlen: pointer to variable to store the number of written bytes
* @eccbuf: filesystem supplied oob data buffer
* @oobsel: oob selection structure
*
* NAND write with iovec with ecc
*/
static int nand_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)
{
int i, page, len, total_len, ret = -EIO, written = 0, chipnr;
int oob, numpages, autoplace = 0, startpage;
struct nand_chip *this = mtd->priv;
int ppblock = (1 << (this->phys_erase_shift - this->page_shift));
u_char *oobbuf, *bufstart;
/* Preset written len for early exit */
*retlen = 0;
/* Calculate total length of data */
total_len = 0;
for (i = 0; i < count; i++)
total_len += (int) vecs[i].iov_len;
DEBUG (MTD_DEBUG_LEVEL3,
"nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count);
/* Do not allow write past end of page */
if ((to + total_len) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n");
return -EINVAL;
}
/* reject writes, which are not page aligned */
if (NOTALIGNED (to) || NOTALIGNED(total_len)) {
printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n");
return -EINVAL;
}
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_WRITING);
/* Get the current chip-nr */
chipnr = (int) (to >> this->chip_shift);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
/* Check, if it is write protected */
if (nand_check_wp(mtd))
goto out;
/* if oobsel is NULL, use chip defaults */
if (oobsel == NULL)
oobsel = &mtd->oobinfo;
/* Autoplace of oob data ? Use the default placement scheme */
if (oobsel->useecc == MTD_NANDECC_AUTOPLACE) {
oobsel = this->autooob;
autoplace = 1;
}
/* Setup start page */
page = (int) (to >> this->page_shift);
/* Invalidate the page cache, if we write to the cached page */
if (page <= this->pagebuf && this->pagebuf < ((to + total_len) >> this->page_shift))
this->pagebuf = -1;
startpage = page & this->pagemask;
/* Loop until all kvec' data has been written */
len = 0;
while (count) {
/* If the given tuple is >= pagesize then
* write it out from the iov
*/
if ((vecs->iov_len - len) >= mtd->oobblock) {
/* Calc number of pages we can write
* out of this iov in one go */
numpages = (vecs->iov_len - len) >> this->page_shift;
/* Do not cross block boundaries */
numpages = min (ppblock - (startpage & (ppblock - 1)), numpages);
oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
bufstart = (u_char *)vecs->iov_base;
bufstart += len;
this->data_poi = bufstart;
oob = 0;
for (i = 1; i <= numpages; i++) {
/* Write one page. If this is the last page to write
* then use the real pageprogram command, else select
* cached programming if supported by the chip.
*/
ret = nand_write_page (mtd, this, page & this->pagemask,
&oobbuf[oob], oobsel, i != numpages);
if (ret)
goto out;
this->data_poi += mtd->oobblock;
len += mtd->oobblock;
oob += mtd->oobsize;
page++;
}
/* Check, if we have to switch to the next tuple */
if (len >= (int) vecs->iov_len) {
vecs++;
len = 0;
count--;
}
} else {
/* We must use the internal buffer, read data out of each
* tuple until we have a full page to write
*/
int cnt = 0;
while (cnt < mtd->oobblock) {
if (vecs->iov_base != NULL && vecs->iov_len)
this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++];
/* Check, if we have to switch to the next tuple */
if (len >= (int) vecs->iov_len) {
vecs++;
len = 0;
count--;
}
}
this->pagebuf = page;
this->data_poi = this->data_buf;
bufstart = this->data_poi;
numpages = 1;
oobbuf = nand_prepare_oobbuf (mtd, NULL, oobsel, autoplace, numpages);
ret = nand_write_page (mtd, this, page & this->pagemask,
oobbuf, oobsel, 0);
if (ret)
goto out;
page++;
}
this->data_poi = bufstart;
ret = nand_verify_pages (mtd, this, startpage, numpages, oobbuf, oobsel, chipnr, 0);
if (ret)
goto out;
written += mtd->oobblock * numpages;
/* All done ? */
if (!count)
break;
startpage = page & this->pagemask;
/* Check, if we cross a chip boundary */
if (!startpage) {
chipnr++;
this->select_chip(mtd, -1);
this->select_chip(mtd, chipnr);
}
}
ret = 0;
out:
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
*retlen = written;
return ret;
}
/**
* single_erease_cmd - [GENERIC] NAND standard block erase command function
* @mtd: MTD device structure
* @page: the page address of the block which will be erased
*
* Standard erase command for NAND chips
*/
static void single_erase_cmd (struct mtd_info *mtd, int page)
{
struct nand_chip *this = mtd->priv;
/* Send commands to erase a block */
this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
}
/**
* multi_erease_cmd - [GENERIC] AND specific block erase command function
* @mtd: MTD device structure
* @page: the page address of the block which will be erased
*
* AND multi block erase command function
* Erase 4 consecutive blocks
*/
static void multi_erase_cmd (struct mtd_info *mtd, int page)
{
struct nand_chip *this = mtd->priv;
/* Send commands to erase a block */
this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page++);
this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page);
this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1);
}
/**
* nand_erase - [MTD Interface] erase block(s)
* @mtd: MTD device structure
* @instr: erase instruction
*
* Erase one ore more blocks
*/
static int nand_erase (struct mtd_info *mtd, struct erase_info *instr)
{
return nand_erase_nand (mtd, instr, 0);
}
/**
* nand_erase_intern - [NAND Interface] erase block(s)
* @mtd: MTD device structure
* @instr: erase instruction
* @allowbbt: allow erasing the bbt area
*
* Erase one ore more blocks
*/
int nand_erase_nand (struct mtd_info *mtd, struct erase_info *instr, int allowbbt)
{
int page, len, status, pages_per_block, ret, chipnr;
struct nand_chip *this = mtd->priv;
DEBUG (MTD_DEBUG_LEVEL3,
"nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len);
/* Start address must align on block boundary */
if (instr->addr & ((1 << this->phys_erase_shift) - 1)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n");
return -EINVAL;
}
/* Length must align on block boundary */
if (instr->len & ((1 << this->phys_erase_shift) - 1)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n");
return -EINVAL;
}
/* Do not allow erase past end of device */
if ((instr->len + instr->addr) > mtd->size) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n");
return -EINVAL;
}
instr->fail_addr = 0xffffffff;
/* Grab the lock and see if the device is available */
nand_get_chip (this, mtd, FL_ERASING);
/* Shift to get first page */
page = (int) (instr->addr >> this->page_shift);
chipnr = (int) (instr->addr >> this->chip_shift);
/* Calculate pages in each block */
pages_per_block = 1 << (this->phys_erase_shift - this->page_shift);
/* Select the NAND device */
this->select_chip(mtd, chipnr);
/* Check the WP bit */
/* Check, if it is write protected */
if (nand_check_wp(mtd)) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n");
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
}
/* Loop through the pages */
len = instr->len;
instr->state = MTD_ERASING;
while (len) {
/* Check if we have a bad block, we do not erase bad blocks ! */
if (nand_block_checkbad(mtd, ((loff_t) page) << this->page_shift, 0, allowbbt)) {
printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page);
instr->state = MTD_ERASE_FAILED;
goto erase_exit;
}
/* Invalidate the page cache, if we erase the block which contains
the current cached page */
if (page <= this->pagebuf && this->pagebuf < (page + pages_per_block))
this->pagebuf = -1;
this->erase_cmd (mtd, page & this->pagemask);
status = this->waitfunc (mtd, this, FL_ERASING);
/* See if block erase succeeded */
if (status & 0x01) {
DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page);
instr->state = MTD_ERASE_FAILED;
instr->fail_addr = (page << this->page_shift);
goto erase_exit;
}
/* Increment page address and decrement length */
len -= (1 << this->phys_erase_shift);
page += pages_per_block;
/* Check, if we cross a chip boundary */
if (len && !(page & this->pagemask)) {
chipnr++;
this->select_chip(mtd, -1);
this->select_chip(mtd, chipnr);
}
}
instr->state = MTD_ERASE_DONE;
erase_exit:
ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;
/* Do call back function */
if (!ret && instr->callback)
instr->callback (instr);
/* Deselect and wake up anyone waiting on the device */
nand_release_chip(mtd);
/* Return more or less happy */
return ret;
}
/**
* nand_sync - [MTD Interface] sync
* @mtd: MTD device structure
*
* Sync is actually a wait for chip ready function
*/
static void nand_sync (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
DECLARE_WAITQUEUE (wait, current);
DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n");
retry:
/* Grab the spinlock */
spin_lock_bh (&this->chip_lock);
/* See what's going on */
switch (this->state) {
case FL_READY:
case FL_SYNCING:
this->state = FL_SYNCING;
spin_unlock_bh (&this->chip_lock);
break;
default:
/* Not an idle state */
add_wait_queue (&this->wq, &wait);
spin_unlock_bh (&this->chip_lock);
schedule ();
remove_wait_queue (&this->wq, &wait);
goto retry;
}
/* Lock the device */
spin_lock_bh (&this->chip_lock);
/* Set the device to be ready again */
if (this->state == FL_SYNCING) {
this->state = FL_READY;
wake_up (&this->wq);
}
/* Unlock the device */
spin_unlock_bh (&this->chip_lock);
}
/**
* nand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
* @mtd: MTD device structure
* @ofs: offset relative to mtd start
*/
static int nand_block_isbad (struct mtd_info *mtd, loff_t ofs)
{
/* Check for invalid offset */
if (ofs > mtd->size)
return -EINVAL;
return nand_block_checkbad (mtd, ofs, 1, 0);
}
/**
* nand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
* @mtd: MTD device structure
* @ofs: offset relative to mtd start
*/
static int nand_block_markbad (struct mtd_info *mtd, loff_t ofs)
{
struct nand_chip *this = mtd->priv;
int ret;
if ((ret = nand_block_isbad(mtd, ofs))) {
/* If it was bad already, return success and do nothing. */
if (ret > 0)
return 0;
return ret;
}
return this->block_markbad(mtd, ofs);
}
/**
* nand_scan - [NAND Interface] Scan for the NAND device
* @mtd: MTD device structure
* @maxchips: Number of chips to scan for
*
* This fills out all the not initialized function pointers
* with the defaults.
* The flash ID is read and the mtd/chip structures are
* filled with the appropriate values. Buffers are allocated if
* they are not provided by the board driver
*
*/
int nand_scan (struct mtd_info *mtd, int maxchips)
{
int i, j, nand_maf_id, nand_dev_id, busw;
struct nand_chip *this = mtd->priv;
/* Get buswidth to select the correct functions*/
busw = this->options & NAND_BUSWIDTH_16;
/* check for proper chip_delay setup, set 20us if not */
if (!this->chip_delay)
this->chip_delay = 20;
/* check, if a user supplied command function given */
if (this->cmdfunc == NULL)
this->cmdfunc = nand_command;
/* check, if a user supplied wait function given */
if (this->waitfunc == NULL)
this->waitfunc = nand_wait;
if (!this->select_chip)
this->select_chip = nand_select_chip;
if (!this->write_byte)
this->write_byte = busw ? nand_write_byte16 : nand_write_byte;
if (!this->read_byte)
this->read_byte = busw ? nand_read_byte16 : nand_read_byte;
if (!this->write_word)
this->write_word = nand_write_word;
if (!this->read_word)
this->read_word = nand_read_word;
if (!this->block_bad)
this->block_bad = nand_block_bad;
if (!this->block_markbad)
this->block_markbad = nand_default_block_markbad;
if (!this->write_buf)
this->write_buf = busw ? nand_write_buf16 : nand_write_buf;
if (!this->read_buf)
this->read_buf = busw ? nand_read_buf16 : nand_read_buf;
if (!this->verify_buf)
this->verify_buf = busw ? nand_verify_buf16 : nand_verify_buf;
if (!this->scan_bbt)
this->scan_bbt = nand_default_bbt;
/* Select the device */
this->select_chip(mtd, 0);
/* Send the command for reading device ID */
this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
nand_maf_id = this->read_byte(mtd);
nand_dev_id = this->read_byte(mtd);
/* Print and store flash device information */
for (i = 0; nand_flash_ids[i].name != NULL; i++) {
if (nand_dev_id != nand_flash_ids[i].id)
continue;
if (!mtd->name) mtd->name = nand_flash_ids[i].name;
this->chipsize = nand_flash_ids[i].chipsize << 20;
/* New devices have all the information in additional id bytes */
if (!nand_flash_ids[i].pagesize) {
int extid;
/* The 3rd id byte contains non relevant data ATM */
extid = this->read_byte(mtd);
/* The 4th id byte is the important one */
extid = this->read_byte(mtd);
/* Calc pagesize */
mtd->oobblock = 1024 << (extid & 0x3);
extid >>= 2;
/* Calc oobsize */
mtd->oobsize = (8 << (extid & 0x03)) * (mtd->oobblock / 512);
extid >>= 2;
/* Calc blocksize. Blocksize is multiples of 64KiB */
mtd->erasesize = (64 * 1024) << (extid & 0x03);
extid >>= 2;
/* Get buswidth information */
busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0;
} else {
/* Old devices have this data hardcoded in the
* device id table */
mtd->erasesize = nand_flash_ids[i].erasesize;
mtd->oobblock = nand_flash_ids[i].pagesize;
mtd->oobsize = mtd->oobblock / 32;
busw = nand_flash_ids[i].options & NAND_BUSWIDTH_16;
}
/* Check, if buswidth is correct. Hardware drivers should set
* this correct ! */
if (busw != (this->options & NAND_BUSWIDTH_16)) {
printk (KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
nand_manuf_ids[i].name , mtd->name);
printk (KERN_WARNING
"NAND bus width %d instead %d bit\n",
(this->options & NAND_BUSWIDTH_16) ? 16 : 8,
busw ? 16 : 8);
this->select_chip(mtd, -1);
return 1;
}
/* Calculate the address shift from the page size */
this->page_shift = ffs(mtd->oobblock) - 1;
this->bbt_erase_shift = this->phys_erase_shift = ffs(mtd->erasesize) - 1;
this->chip_shift = ffs(this->chipsize) - 1;
/* Set the bad block position */
this->badblockpos = mtd->oobblock > 512 ?
NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS;
/* Get chip options, preserve non chip based options */
this->options &= ~NAND_CHIPOPTIONS_MSK;
this->options |= nand_flash_ids[i].options & NAND_CHIPOPTIONS_MSK;
/* Set this as a default. Board drivers can override it, if neccecary */
this->options |= NAND_NO_AUTOINCR;
/* Check if this is a not a samsung device. Do not clear the options
* for chips which are not having an extended id.
*/
if (nand_maf_id != NAND_MFR_SAMSUNG && !nand_flash_ids[i].pagesize)
this->options &= ~NAND_SAMSUNG_LP_OPTIONS;
/* Check for AND chips with 4 page planes */
if (this->options & NAND_4PAGE_ARRAY)
this->erase_cmd = multi_erase_cmd;
else
this->erase_cmd = single_erase_cmd;
/* Do not replace user supplied command function ! */
if (mtd->oobblock > 512 && this->cmdfunc == nand_command)
this->cmdfunc = nand_command_lp;
/* Try to identify manufacturer */
for (j = 0; nand_manuf_ids[j].id != 0x0; j++) {
if (nand_manuf_ids[j].id == nand_maf_id)
break;
}
printk (KERN_INFO "NAND device: Manufacturer ID:"
" 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id,
nand_manuf_ids[j].name , nand_flash_ids[i].name);
break;
}
if (!nand_flash_ids[i].name) {
printk (KERN_WARNING "No NAND device found!!!\n");
this->select_chip(mtd, -1);
return 1;
}
for (i=1; i < maxchips; i++) {
this->select_chip(mtd, i);
/* Send the command for reading device ID */
this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1);
/* Read manufacturer and device IDs */
if (nand_maf_id != this->read_byte(mtd) ||
nand_dev_id != this->read_byte(mtd))
break;
}
if (i > 1)
printk(KERN_INFO "%d NAND chips detected\n", i);
/* Allocate buffers, if neccecary */
if (!this->oob_buf) {
size_t len;
len = mtd->oobsize << (this->phys_erase_shift - this->page_shift);
this->oob_buf = kmalloc (len, GFP_KERNEL);
if (!this->oob_buf) {
printk (KERN_ERR "nand_scan(): Cannot allocate oob_buf\n");
return -ENOMEM;
}
this->options |= NAND_OOBBUF_ALLOC;
}
if (!this->data_buf) {
size_t len;
len = mtd->oobblock + mtd->oobsize;
this->data_buf = kmalloc (len, GFP_KERNEL);
if (!this->data_buf) {
if (this->options & NAND_OOBBUF_ALLOC)
kfree (this->oob_buf);
printk (KERN_ERR "nand_scan(): Cannot allocate data_buf\n");
return -ENOMEM;
}
this->options |= NAND_DATABUF_ALLOC;
}
/* Store the number of chips and calc total size for mtd */
this->numchips = i;
mtd->size = i * this->chipsize;
/* Convert chipsize to number of pages per chip -1. */
this->pagemask = (this->chipsize >> this->page_shift) - 1;
/* Preset the internal oob buffer */
memset(this->oob_buf, 0xff, mtd->oobsize << (this->phys_erase_shift - this->page_shift));
/* If no default placement scheme is given, select an
* appropriate one */
if (!this->autooob) {
/* Select the appropriate default oob placement scheme for
* placement agnostic filesystems */
switch (mtd->oobsize) {
case 8:
this->autooob = &nand_oob_8;
break;
case 16:
this->autooob = &nand_oob_16;
break;
case 64:
this->autooob = &nand_oob_64;
break;
default:
printk (KERN_WARNING "No oob scheme defined for oobsize %d\n",
mtd->oobsize);
BUG();
}
}
/* The number of bytes available for the filesystem to place fs dependend
* oob data */
if (this->options & NAND_BUSWIDTH_16) {
mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 2);
if (this->autooob->eccbytes & 0x01)
mtd->oobavail--;
} else
mtd->oobavail = mtd->oobsize - (this->autooob->eccbytes + 1);
/*
* check ECC mode, default to software
* if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize
* fallback to software ECC
*/
this->eccsize = 256; /* set default eccsize */
switch (this->eccmode) {
case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512:
case NAND_ECC_HW8_512:
if (mtd->oobblock == 256) {
printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n");
this->eccmode = NAND_ECC_SOFT;
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
break;
} else
this->eccsize = 512; /* set eccsize to 512 and fall through for function check */
case NAND_ECC_HW3_256:
if (this->calculate_ecc && this->correct_data && this->enable_hwecc)
break;
printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n");
BUG();
case NAND_ECC_NONE:
printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n");
this->eccmode = NAND_ECC_NONE;
break;
case NAND_ECC_SOFT:
this->calculate_ecc = nand_calculate_ecc;
this->correct_data = nand_correct_data;
break;
default:
printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode);
BUG();
}
mtd->eccsize = this->eccsize;
/* Set the number of read / write steps for one page to ensure ECC generation */
switch (this->eccmode) {
case NAND_ECC_HW3_512:
case NAND_ECC_HW6_512:
case NAND_ECC_HW8_512:
this->eccsteps = mtd->oobblock / 512;
break;
case NAND_ECC_HW3_256:
case NAND_ECC_SOFT:
this->eccsteps = mtd->oobblock / 256;
break;
case NAND_ECC_NONE:
this->eccsteps = 1;
break;
}
/* Initialize state, waitqueue and spinlock */
this->state = FL_READY;
init_waitqueue_head (&this->wq);
spin_lock_init (&this->chip_lock);
/* De-select the device */
this->select_chip(mtd, -1);
/* Invalidate the pagebuffer reference */
this->pagebuf = -1;
/* Fill in remaining MTD driver data */
mtd->type = MTD_NANDFLASH;
mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC;
mtd->ecctype = MTD_ECC_SW;
mtd->erase = nand_erase;
mtd->point = NULL;
mtd->unpoint = NULL;
mtd->read = nand_read;
mtd->write = nand_write;
mtd->read_ecc = nand_read_ecc;
mtd->write_ecc = nand_write_ecc;
mtd->read_oob = nand_read_oob;
mtd->write_oob = nand_write_oob;
mtd->readv = NULL;
mtd->writev = nand_writev;
mtd->writev_ecc = nand_writev_ecc;
mtd->sync = nand_sync;
mtd->lock = NULL;
mtd->unlock = NULL;
mtd->suspend = NULL;
mtd->resume = NULL;
mtd->block_isbad = nand_block_isbad;
mtd->block_markbad = nand_block_markbad;
/* and make the autooob the default one */
memcpy(&mtd->oobinfo, this->autooob, sizeof(mtd->oobinfo));
mtd->owner = THIS_MODULE;
/* Build bad block table */
return this->scan_bbt (mtd);
}
/**
* nand_release - [NAND Interface] Free resources held by the NAND device
* @mtd: MTD device structure
*/
void nand_release (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
#if defined(CONFIG_MTD_PARTITIONS) || defined(CONFIG_MTD_PARTITIONS_MODULE)
/* Unregister partitions */
del_mtd_partitions (mtd);
#endif
/* Unregister the device */
del_mtd_device (mtd);
/* Free bad block table memory, if allocated */
if (this->bbt)
kfree (this->bbt);
/* Buffer allocated by nand_scan ? */
if (this->options & NAND_OOBBUF_ALLOC)
kfree (this->oob_buf);
/* Buffer allocated by nand_scan ? */
if (this->options & NAND_DATABUF_ALLOC)
kfree (this->data_buf);
}
EXPORT_SYMBOL (nand_scan);
EXPORT_SYMBOL (nand_release);
MODULE_LICENSE ("GPL");
MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>");
MODULE_DESCRIPTION ("Generic NAND flash driver code");
/*
* drivers/mtd/nand_bbt.c
*
* Overview:
* Bad block table support for the NAND driver
*
* Copyright (C) 2004 Thomas Gleixner (tglx@linutronix.de)
*
* $Id: nand_bbt.c,v 1.24 2004/06/28 08:25:35 gleixner Exp $
*
* 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
* published by the Free Software Foundation.
*
* Description:
*
* When nand_scan_bbt is called, then it tries to find the bad block table
* depending on the options in the bbt descriptor(s). If a bbt is found
* then the contents are read and the memory based bbt is created. If a
* mirrored bbt is selected then the mirror is searched too and the
* versions are compared. If the mirror has a greater version number
* than the mirror bbt is used to build the memory based bbt.
* If the tables are not versioned, then we "or" the bad block information.
* If one of the bbt's is out of date or does not exist it is (re)created.
* If no bbt exists at all then the device is scanned for factory marked
* good / bad blocks and the bad block tables are created.
*
* For manufacturer created bbts like the one found on M-SYS DOC devices
* the bbt is searched and read but never created
*
* The autogenerated bad block table is located in the last good blocks
* of the device. The table is mirrored, so it can be updated eventually.
* The table is marked in the oob area with an ident pattern and a version
* number which indicates which of both tables is more up to date.
*
* The table uses 2 bits per block
* 11b: block is good
* 00b: block is factory marked bad
* 01b, 10b: block is marked bad due to wear
*
* The memory bad block table uses the following scheme:
* 00b: block is good
* 01b: block is marked bad due to wear
* 10b: block is reserved (to protect the bbt area)
* 11b: block is factory marked bad
*
* Multichip devices like DOC store the bad block info per floor.
*
* Following assumptions are made:
* - 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
*
*/
#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>
/**
* check_pattern - [GENERIC] check if a pattern is in the buffer
* @buf: the buffer to search
* @len: the length of buffer to search
* @paglen: the pagelength
* @td: search pattern descriptor
*
* Check for a pattern at the given place. Used to search bad block
* tables and good / bad block identifiers.
* If the SCAN_EMPTY option is set then check, if all bytes except the
* pattern area contain 0xff
*
*/
static int check_pattern (uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
{
int i, end;
uint8_t *p = buf;
end = paglen + td->offs;
if (td->options & NAND_BBT_SCANEMPTY) {
for (i = 0; i < end; i++) {
if (p[i] != 0xff)
return -1;
}
}
p += end;
/* Compare the pattern */
for (i = 0; i < td->len; i++) {
if (p[i] != td->pattern[i])
return -1;
}
p += td->len;
end += td->len;
if (td->options & NAND_BBT_SCANEMPTY) {
for (i = end; i < len; i++) {
if (*p++ != 0xff)
return -1;
}
}
return 0;
}
/**
* read_bbt - [GENERIC] Read the bad block table starting from page
* @mtd: MTD device structure
* @buf: temporary buffer
* @page: the starting page
* @num: the number of bbt descriptors to read
* @bits: number of bits per block
* @offs: offset in the memory table
*
* Read the bad block table starting from page.
*
*/
static int read_bbt (struct mtd_info *mtd, uint8_t *buf, int page, int num,
int bits, int offs, int reserved_block_code)
{
int res, i, j, act = 0;
struct nand_chip *this = mtd->priv;
size_t retlen, len, totlen;
loff_t from;
uint8_t msk = (uint8_t) ((1 << bits) - 1);
totlen = (num * bits) >> 3;
from = ((loff_t)page) << this->page_shift;
while (totlen) {
len = min (totlen, (size_t) (1 << this->bbt_erase_shift));
res = mtd->read_ecc (mtd, from, len, &retlen, buf, NULL, this->autooob);
if (res < 0) {
if (retlen != len) {
printk (KERN_INFO "nand_bbt: Error reading bad block table\n");
return res;
}
printk (KERN_WARNING "nand_bbt: ECC error while reading bad block table\n");
}
/* Analyse data */
for (i = 0; i < len; i++) {
uint8_t dat = buf[i];
for (j = 0; j < 8; j += bits, act += 2) {
uint8_t tmp = (dat >> j) & msk;
if (tmp == msk)
continue;
if (reserved_block_code &&
(tmp == reserved_block_code)) {
printk (KERN_DEBUG "nand_read_bbt: Reserved block at 0x%08x\n",
((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
this->bbt[offs + (act >> 3)] |= 0x2 << (act & 0x06);
continue;
}
/* Leave it for now, if its matured we can move this
* message to MTD_DEBUG_LEVEL0 */
printk (KERN_DEBUG "nand_read_bbt: Bad block at 0x%08x\n",
((offs << 2) + (act >> 1)) << this->bbt_erase_shift);
/* Factory marked bad or worn out ? */
if (tmp == 0)
this->bbt[offs + (act >> 3)] |= 0x3 << (act & 0x06);
else
this->bbt[offs + (act >> 3)] |= 0x1 << (act & 0x06);
}
}
totlen -= len;
from += len;
}
return 0;
}
/**
* read_abs_bbt - [GENERIC] Read the bad block table starting at a given page
* @mtd: MTD device structure
* @buf: temporary buffer
* @td: descriptor for the bad block table
* @chip: read the table for a specific chip, -1 read all chips.
* Applies only if NAND_BBT_PERCHIP option is set
*
* Read the bad block table for all chips starting at a given page
* We assume that the bbt bits are in consecutive order.
*/
static int read_abs_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td, int chip)
{
struct nand_chip *this = mtd->priv;
int res = 0, i;
int bits;
bits = td->options & NAND_BBT_NRBITS_MSK;
if (td->options & NAND_BBT_PERCHIP) {
int offs = 0;
for (i = 0; i < this->numchips; i++) {
if (chip == -1 || chip == i)
res = read_bbt (mtd, buf, td->pages[i], this->chipsize >> this->bbt_erase_shift, bits, offs, td->reserved_block_code);
if (res)
return res;
offs += this->chipsize >> (this->bbt_erase_shift + 2);
}
} else {
res = read_bbt (mtd, buf, td->pages[0], mtd->size >> this->bbt_erase_shift, bits, 0, td->reserved_block_code);
if (res)
return res;
}
return 0;
}
/**
* read_abs_bbts - [GENERIC] Read the bad block table(s) for all chips starting at a given page
* @mtd: MTD device structure
* @buf: temporary buffer
* @td: descriptor for the bad block table
* @md: descriptor for the bad block table mirror
*
* Read the bad block table(s) for all chips starting at a given page
* 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,
struct nand_bbt_descr *md)
{
struct nand_chip *this = mtd->priv;
/* Read the primary version, if available */
if (td->options & NAND_BBT_VERSION) {
nand_read_raw (mtd, buf, td->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
td->version[0] = buf[mtd->oobblock + 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 */
if (md && (md->options & NAND_BBT_VERSION)) {
nand_read_raw (mtd, buf, md->pages[0] << this->page_shift, mtd->oobblock, mtd->oobsize);
md->version[0] = buf[mtd->oobblock + md->veroffs];
printk (KERN_DEBUG "Bad block table at page %d, version 0x%02X\n", md->pages[0], md->version[0]);
}
return 1;
}
/**
* create_bbt - [GENERIC] Create a bad block table by scanning the device
* @mtd: MTD device structure
* @buf: temporary buffer
* @bd: descriptor for the good/bad block search pattern
* @chip: create the table for a specific chip, -1 read all chips.
* Applies only if NAND_BBT_PERCHIP option is set
*
* Create a bad block table by scanning the device
* 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)
{
struct nand_chip *this = mtd->priv;
int i, j, numblocks, len, scanlen;
int startblock;
loff_t from;
size_t readlen, ooblen;
printk (KERN_INFO "Scanning device for bad blocks\n");
if (bd->options & NAND_BBT_SCANALLPAGES)
len = 1 << (this->bbt_erase_shift - this->page_shift);
else {
if (bd->options & NAND_BBT_SCAN2NDPAGE)
len = 2;
else
len = 1;
}
scanlen = mtd->oobblock + mtd->oobsize;
readlen = len * mtd->oobblock;
ooblen = len * mtd->oobsize;
if (chip == -1) {
/* Note that numblocks is 2 * (real numblocks) here, see i+=2 below as it
* makes shifting and masking less painful */
numblocks = mtd->size >> (this->bbt_erase_shift - 1);
startblock = 0;
from = 0;
} else {
if (chip >= this->numchips) {
printk (KERN_WARNING "create_bbt(): chipnr (%d) > available chips (%d)\n",
chip + 1, this->numchips);
return;
}
numblocks = this->chipsize >> (this->bbt_erase_shift - 1);
startblock = chip * numblocks;
numblocks += startblock;
from = startblock << (this->bbt_erase_shift - 1);
}
for (i = startblock; i < numblocks;) {
nand_read_raw (mtd, buf, from, readlen, ooblen);
for (j = 0; j < len; j++) {
if (check_pattern (&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
this->bbt[i >> 3] |= 0x03 << (i & 0x6);
printk (KERN_WARNING "Bad eraseblock %d at 0x%08x\n",
i >> 1, (unsigned int) from);
break;
}
}
i += 2;
from += (1 << this->bbt_erase_shift);
}
}
/**
* search_bbt - [GENERIC] scan the device for a specific bad block table
* @mtd: MTD device structure
* @buf: temporary buffer
* @td: descriptor for the bad block table
*
* Read the bad block table by searching for a given ident pattern.
* Search is preformed either from the beginning up or from the end of
* the device downwards. The search starts always at the start of a
* block.
* If the option NAND_BBT_PERCHIP is given, each chip is searched
* for a bbt, which contains the bad block information of this chip.
* This is neccecary to provide support for certain DOC devices.
*
* The bbt ident pattern resides in the oob area of the first page
* in a block.
*/
static int search_bbt (struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *td)
{
struct nand_chip *this = mtd->priv;
int i, chips;
int bits, startblock, block, dir;
int scanlen = mtd->oobblock + mtd->oobsize;
int bbtblocks;
/* Search direction top -> down ? */
if (td->options & NAND_BBT_LASTBLOCK) {
startblock = (mtd->size >> this->bbt_erase_shift) -1;
dir = -1;
} else {
startblock = 0;
dir = 1;
}
/* Do we have a bbt per chip ? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
bbtblocks = this->chipsize >> this->bbt_erase_shift;
startblock &= bbtblocks - 1;
} else {
chips = 1;
bbtblocks = mtd->size >> this->bbt_erase_shift;
}
/* Number of bits for each erase block in the bbt */
bits = td->options & NAND_BBT_NRBITS_MSK;
for (i = 0; i < chips; i++) {
/* Reset version information */
td->version[i] = 0;
td->pages[i] = -1;
/* Scan the maximum number of blocks */
for (block = 0; block < td->maxblocks; block++) {
int actblock = startblock + dir * block;
/* Read first page */
nand_read_raw (mtd, buf, actblock << this->bbt_erase_shift, mtd->oobblock, mtd->oobsize);
if (!check_pattern(buf, scanlen, mtd->oobblock, td)) {
td->pages[i] = actblock << (this->bbt_erase_shift - this->page_shift);
if (td->options & NAND_BBT_VERSION) {
td->version[i] = buf[mtd->oobblock + td->veroffs];
}
break;
}
}
startblock += this->chipsize >> this->bbt_erase_shift;
}
/* Check, if we found a bbt for each requested chip */
for (i = 0; i < chips; i++) {
if (td->pages[i] == -1)
printk (KERN_WARNING "Bad block table not found for chip %d\n", i);
else
printk (KERN_DEBUG "Bad block table found at page %d, version 0x%02X\n", td->pages[i], td->version[i]);
}
return 0;
}
/**
* search_read_bbts - [GENERIC] scan the device for bad block table(s)
* @mtd: MTD device structure
* @buf: temporary buffer
* @td: descriptor for the bad block table
* @md: descriptor for the bad block table mirror
*
* Search and read the bad block table(s)
*/
static int search_read_bbts (struct mtd_info *mtd, uint8_t *buf,
struct nand_bbt_descr *td, struct nand_bbt_descr *md)
{
/* Search the primary table */
search_bbt (mtd, buf, td);
/* Search the mirror table */
if (md)
search_bbt (mtd, buf, md);
/* Force result check */
return 1;
}
/**
* write_bbt - [GENERIC] (Re)write the bad block table
*
* @mtd: MTD device structure
* @buf: temporary buffer
* @td: descriptor for the bad block table
* @md: descriptor for the bad block table mirror
* @chipsel: selector for a specific chip, -1 for all
*
* (Re)write the bad block table
*
*/
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_chip *this = mtd->priv;
struct nand_oobinfo oobinfo;
struct erase_info einfo;
int i, j, res, chip = 0;
int bits, startblock, dir, page, offs, numblocks, sft, sftmsk;
int nrchips, bbtoffs, pageoffs;
uint8_t msk[4];
uint8_t rcode = td->reserved_block_code;
size_t retlen, len = 0;
loff_t to;
if (!rcode)
rcode = 0xff;
/* Write bad block table per chip rather than per device ? */
if (td->options & NAND_BBT_PERCHIP) {
numblocks = (int) (this->chipsize >> this->bbt_erase_shift);
/* Full device write or specific chip ? */
if (chipsel == -1) {
nrchips = this->numchips;
} else {
nrchips = chipsel + 1;
chip = chipsel;
}
} else {
numblocks = (int) (mtd->size >> this->bbt_erase_shift);
nrchips = 1;
}
/* Loop through the chips */
for (; chip < nrchips; chip++) {
/* There was already a version of the table, reuse the page
* This applies for absolute placement too, as we have the
* page nr. in td->pages.
*/
if (td->pages[chip] != -1) {
page = td->pages[chip];
goto write;
}
/* Automatic placement of the bad block table */
/* Search direction top -> down ? */
if (td->options & NAND_BBT_LASTBLOCK) {
startblock = numblocks * (chip + 1) - 1;
dir = -1;
} else {
startblock = chip * numblocks;
dir = 1;
}
for (i = 0; i < td->maxblocks; i++) {
int block = startblock + dir * i;
/* Check, if the block is bad */
switch ((this->bbt[block >> 2] >> (2 * (block & 0x03))) & 0x03) {
case 0x01:
case 0x03:
continue;
}
page = block << (this->bbt_erase_shift - this->page_shift);
/* Check, if the block is used by the mirror table */
if (!md || md->pages[chip] != page)
goto write;
}
printk (KERN_ERR "No space left to write bad block table\n");
return -ENOSPC;
write:
/* Set up shift count and masks for the flash table */
bits = td->options & NAND_BBT_NRBITS_MSK;
switch (bits) {
case 1: sft = 3; sftmsk = 0x07; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x01; break;
case 2: sft = 2; sftmsk = 0x06; msk[0] = 0x00; msk[1] = 0x01; msk[2] = ~rcode; msk[3] = 0x03; break;
case 4: sft = 1; sftmsk = 0x04; msk[0] = 0x00; msk[1] = 0x0C; msk[2] = ~rcode; msk[3] = 0x0f; break;
case 8: sft = 0; sftmsk = 0x00; msk[0] = 0x00; msk[1] = 0x0F; msk[2] = ~rcode; msk[3] = 0xff; break;
default: return -EINVAL;
}
bbtoffs = chip * (numblocks >> 2);
to = ((loff_t) page) << this->page_shift;
memcpy (&oobinfo, this->autooob, sizeof(oobinfo));
oobinfo.useecc = MTD_NANDECC_PLACEONLY;
/* Must we save the block contents ? */
if (td->options & NAND_BBT_SAVECONTENT) {
/* Make it block aligned */
to &= ~((loff_t) ((1 << this->bbt_erase_shift) - 1));
len = 1 << this->bbt_erase_shift;
res = mtd->read_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
if (res < 0) {
if (retlen != len) {
printk (KERN_INFO "nand_bbt: Error reading block for writing the bad block table\n");
return res;
}
printk (KERN_WARNING "nand_bbt: ECC error while reading block for writing bad block table\n");
}
/* Calc the byte offset in the buffer */
pageoffs = page - (int)(to >> this->page_shift);
offs = pageoffs << this->page_shift;
/* Preset the bbt area with 0xff */
memset (&buf[offs], 0xff, (size_t)(numblocks >> sft));
/* Preset the bbt's oob area with 0xff */
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 {
/* Calc length */
len = (size_t) (numblocks >> sft);
/* Make it page aligned ! */
len = (len + (mtd->oobblock-1)) & ~(mtd->oobblock-1);
/* Preset the buffer with 0xff */
memset (buf, 0xff, len + (len >> this->page_shift) * mtd->oobsize);
offs = 0;
/* Pattern is located in oob area of first page */
memcpy (&buf[len + td->offs], td->pattern, td->len);
if (td->options & NAND_BBT_VERSION) {
buf[len + td->veroffs] = td->version[chip];
}
}
/* walk through the memory table */
for (i = 0; i < numblocks; ) {
uint8_t dat;
dat = this->bbt[bbtoffs + (i >> 2)];
for (j = 0; j < 4; j++ , i++) {
int sftcnt = (i << (3 - sft)) & sftmsk;
/* Do not store the reserved bbt blocks ! */
buf[offs + (i >> sft)] &= ~(msk[dat & 0x03] << sftcnt);
dat >>= 2;
}
}
memset (&einfo, 0, sizeof (einfo));
einfo.mtd = mtd;
einfo.addr = (unsigned long) to;
einfo.len = 1 << this->bbt_erase_shift;
res = nand_erase_nand (mtd, &einfo, 1);
if (res < 0) {
printk (KERN_WARNING "nand_bbt: Error during block erase: %d\n", res);
return res;
}
res = mtd->write_ecc (mtd, to, len, &retlen, buf, &buf[len], &oobinfo);
if (res < 0) {
printk (KERN_WARNING "nand_bbt: Error while writing bad block table %d\n", res);
return res;
}
printk (KERN_DEBUG "Bad block table written to 0x%08x, version 0x%02X\n",
(unsigned int) to, td->version[chip]);
/* Mark it as used */
td->pages[chip] = page;
}
return 0;
}
/**
* nand_memory_bbt - [GENERIC] create a memory based bad block table
* @mtd: MTD device structure
* @bd: descriptor for the good/bad block search pattern
*
* The function creates a memory based bbt by scanning the device
* for manufacturer / software marked good / bad blocks
*/
static int nand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
/* Ensure that we only scan for the pattern and nothing else */
bd->options = 0;
create_bbt (mtd, this->data_buf, bd, -1);
return 0;
}
/**
* check_create - [GENERIC] create and write bbt(s) if neccecary
* @mtd: MTD device structure
* @buf: temporary buffer
* @bd: descriptor for the good/bad block search pattern
*
* The function checks the results of the previous call to read_bbt
* and creates / updates the bbt(s) if neccecary
* Creation is neccecary if no bbt was found for the chip/device
* Update is neccecary if one of the tables is missing or the
* 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)
{
int i, chips, writeops, chipsel, res;
struct nand_chip *this = mtd->priv;
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
struct nand_bbt_descr *rd, *rd2;
/* Do we have a bbt per chip ? */
if (td->options & NAND_BBT_PERCHIP)
chips = this->numchips;
else
chips = 1;
for (i = 0; i < chips; i++) {
writeops = 0;
rd = NULL;
rd2 = NULL;
/* Per chip or per device ? */
chipsel = (td->options & NAND_BBT_PERCHIP) ? i : -1;
/* Mirrored table avilable ? */
if (md) {
if (td->pages[i] == -1 && md->pages[i] == -1) {
writeops = 0x03;
goto create;
}
if (td->pages[i] == -1) {
rd = md;
td->version[i] = md->version[i];
writeops = 1;
goto writecheck;
}
if (md->pages[i] == -1) {
rd = td;
md->version[i] = td->version[i];
writeops = 2;
goto writecheck;
}
if (td->version[i] == md->version[i]) {
rd = td;
if (!(td->options & NAND_BBT_VERSION))
rd2 = md;
goto writecheck;
}
if (((int8_t) (td->version[i] - md->version[i])) > 0) {
rd = td;
md->version[i] = td->version[i];
writeops = 2;
} else {
rd = md;
td->version[i] = md->version[i];
writeops = 1;
}
goto writecheck;
} else {
if (td->pages[i] == -1) {
writeops = 0x01;
goto create;
}
rd = td;
goto writecheck;
}
create:
/* Create the bad block table by scanning the device ? */
if (!(td->options & NAND_BBT_CREATE))
continue;
/* Create the table in memory by scanning the chip(s) */
create_bbt (mtd, buf, bd, chipsel);
td->version[i] = 1;
if (md)
md->version[i] = 1;
writecheck:
/* read back first ? */
if (rd)
read_abs_bbt (mtd, buf, rd, chipsel);
/* If they weren't versioned, read both. */
if (rd2)
read_abs_bbt (mtd, buf, rd2, chipsel);
/* Write the bad block table to the device ? */
if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
res = write_bbt (mtd, buf, td, md, chipsel);
if (res < 0)
return res;
}
/* Write the mirror bad block table to the device ? */
if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
res = write_bbt (mtd, buf, md, td, chipsel);
if (res < 0)
return res;
}
}
return 0;
}
/**
* mark_bbt_regions - [GENERIC] mark the bad block table regions
* @mtd: MTD device structure
* @td: bad block table descriptor
*
* The bad block table regions are marked as "bad" to prevent
* accidental erasures / writes. The regions are identified by
* the mark 0x02.
*/
static void mark_bbt_region (struct mtd_info *mtd, struct nand_bbt_descr *td)
{
struct nand_chip *this = mtd->priv;
int i, j, chips, block, nrblocks, update;
uint8_t oldval, newval;
/* Do we have a bbt per chip ? */
if (td->options & NAND_BBT_PERCHIP) {
chips = this->numchips;
nrblocks = (int)(this->chipsize >> this->bbt_erase_shift);
} else {
chips = 1;
nrblocks = (int)(mtd->size >> this->bbt_erase_shift);
}
for (i = 0; i < chips; i++) {
if ((td->options & NAND_BBT_ABSPAGE) ||
!(td->options & NAND_BBT_WRITE)) {
if (td->pages[i] == -1) continue;
block = td->pages[i] >> (this->bbt_erase_shift - this->page_shift);
block <<= 1;
oldval = this->bbt[(block >> 3)];
newval = oldval | (0x2 << (block & 0x06));
this->bbt[(block >> 3)] = newval;
if ((oldval != newval) && td->reserved_block_code)
nand_update_bbt(mtd, block << (this->bbt_erase_shift - 1));
continue;
}
update = 0;
if (td->options & NAND_BBT_LASTBLOCK)
block = ((i + 1) * nrblocks) - td->maxblocks;
else
block = i * nrblocks;
block <<= 1;
for (j = 0; j < td->maxblocks; j++) {
oldval = this->bbt[(block >> 3)];
newval = oldval | (0x2 << (block & 0x06));
this->bbt[(block >> 3)] = newval;
if (oldval != newval) update = 1;
block += 2;
}
/* If we want reserved blocks to be recorded to flash, and some
new ones have been marked, then we need to update the stored
bbts. This should only happen once. */
if (update && td->reserved_block_code)
nand_update_bbt(mtd, (block - 2) << (this->bbt_erase_shift - 1));
}
}
/**
* nand_scan_bbt - [NAND Interface] scan, find, read and maybe create bad block table(s)
* @mtd: MTD device structure
* @bd: descriptor for the good/bad block search pattern
*
* The function checks, if a bad block table(s) is/are already
* available. If not it scans the device for manufacturer
* marked good / bad blocks and writes the bad block table(s) to
* the selected place.
*
* The bad block table memory is allocated here. It must be freed
* by calling the nand_free_bbt function.
*
*/
int nand_scan_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
{
struct nand_chip *this = mtd->priv;
int len, res = 0;
uint8_t *buf;
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
len = mtd->size >> (this->bbt_erase_shift + 2);
/* Allocate memory (2bit per block) */
this->bbt = (uint8_t *) kmalloc (len, GFP_KERNEL);
if (!this->bbt) {
printk (KERN_ERR "nand_scan_bbt: Out of memory\n");
return -ENOMEM;
}
/* Clear the memory bad block table */
memset (this->bbt, 0x00, len);
/* If no primary table decriptor is given, scan the device
* to build a memory based bad block table
*/
if (!td)
return nand_memory_bbt(mtd, bd);
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift);
len += (len >> this->page_shift) * mtd->oobsize;
buf = kmalloc (len, GFP_KERNEL);
if (!buf) {
printk (KERN_ERR "nand_bbt: Out of memory\n");
kfree (this->bbt);
this->bbt = NULL;
return -ENOMEM;
}
/* Is the bbt at a given page ? */
if (td->options & NAND_BBT_ABSPAGE) {
res = read_abs_bbts (mtd, buf, td, md);
} else {
/* Search the bad block table using a pattern in oob */
res = search_read_bbts (mtd, buf, td, md);
}
if (res)
res = check_create (mtd, buf, bd);
/* Prevent the bbt regions from erasing / writing */
mark_bbt_region (mtd, td);
if (md)
mark_bbt_region (mtd, md);
kfree (buf);
return res;
}
/**
* nand_update_bbt - [NAND Interface] update bad block table(s)
* @mtd: MTD device structure
* @offs: the offset of the newly marked block
*
* The function updates the bad block table(s)
*/
int nand_update_bbt (struct mtd_info *mtd, loff_t offs)
{
struct nand_chip *this = mtd->priv;
int len, res = 0, writeops = 0;
int chip, chipsel;
uint8_t *buf;
struct nand_bbt_descr *td = this->bbt_td;
struct nand_bbt_descr *md = this->bbt_md;
if (!this->bbt || !td)
return -EINVAL;
len = mtd->size >> (this->bbt_erase_shift + 2);
/* Allocate a temporary buffer for one eraseblock incl. oob */
len = (1 << this->bbt_erase_shift);
len += (len >> this->page_shift) * mtd->oobsize;
buf = kmalloc (len, GFP_KERNEL);
if (!buf) {
printk (KERN_ERR "nand_update_bbt: Out of memory\n");
return -ENOMEM;
}
writeops = md != NULL ? 0x03 : 0x01;
/* Do we have a bbt per chip ? */
if (td->options & NAND_BBT_PERCHIP) {
chip = (int) (offs >> this->chip_shift);
chipsel = chip;
} else {
chip = 0;
chipsel = -1;
}
td->version[chip]++;
if (md)
md->version[chip]++;
/* Write the bad block table to the device ? */
if ((writeops & 0x01) && (td->options & NAND_BBT_WRITE)) {
res = write_bbt (mtd, buf, td, md, chipsel);
if (res < 0)
goto out;
}
/* Write the mirror bad block table to the device ? */
if ((writeops & 0x02) && md && (md->options & NAND_BBT_WRITE)) {
res = write_bbt (mtd, buf, md, td, chipsel);
}
out:
kfree (buf);
return res;
}
/* Define some generic bad / good block scan pattern which are used
* while scanning a device for factory marked good / bad blocks
*
* The memory based patterns just
*/
static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
static struct nand_bbt_descr smallpage_memorybased = {
.options = 0,
.offs = 5,
.len = 1,
.pattern = scan_ff_pattern
};
static struct nand_bbt_descr largepage_memorybased = {
.options = 0,
.offs = 0,
.len = 2,
.pattern = scan_ff_pattern
};
static struct nand_bbt_descr smallpage_flashbased = {
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
.offs = 5,
.len = 1,
.pattern = scan_ff_pattern
};
static struct nand_bbt_descr largepage_flashbased = {
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
.offs = 0,
.len = 2,
.pattern = scan_ff_pattern
};
static uint8_t scan_agand_pattern[] = { 0x1C, 0x71, 0xC7, 0x1C, 0x71, 0xC7 };
static struct nand_bbt_descr agand_flashbased = {
.options = NAND_BBT_SCANEMPTY | NAND_BBT_SCANALLPAGES,
.offs = 0x20,
.len = 6,
.pattern = scan_agand_pattern
};
/* Generic flash bbt decriptors
*/
static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' };
static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' };
static struct nand_bbt_descr bbt_main_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 8,
.len = 4,
.veroffs = 12,
.maxblocks = 4,
.pattern = bbt_pattern
};
static struct nand_bbt_descr bbt_mirror_descr = {
.options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE
| NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP,
.offs = 8,
.len = 4,
.veroffs = 12,
.maxblocks = 4,
.pattern = mirror_pattern
};
/**
* nand_default_bbt - [NAND Interface] Select a default bad block table for the device
* @mtd: MTD device structure
*
* This function selects the default bad block table
* support for the device and calls the nand_scan_bbt function
*
*/
int nand_default_bbt (struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
/* Default for AG-AND. We must use a flash based
* bad block table as the devices have factory marked
* _good_ blocks. Erasing those blocks leads to loss
* of the good / bad information, so we _must_ store
* this information in a good / bad table during
* startup
*/
if (this->options & NAND_IS_AND) {
/* Use the default pattern descriptors */
if (!this->bbt_td) {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
this->options |= NAND_USE_FLASH_BBT;
return nand_scan_bbt (mtd, &agand_flashbased);
}
/* Is a flash based bad block table requested ? */
if (this->options & NAND_USE_FLASH_BBT) {
/* Use the default pattern descriptors */
if (!this->bbt_td) {
this->bbt_td = &bbt_main_descr;
this->bbt_md = &bbt_mirror_descr;
}
if (mtd->oobblock > 512)
return nand_scan_bbt (mtd, &largepage_flashbased);
else
return nand_scan_bbt (mtd, &smallpage_flashbased);
} else {
this->bbt_td = NULL;
this->bbt_md = NULL;
if (mtd->oobblock > 512)
return nand_scan_bbt (mtd, &largepage_memorybased);
else
return nand_scan_bbt (mtd, &smallpage_memorybased);
}
}
/**
* nand_isbad_bbt - [NAND Interface] Check if a block is bad
* @mtd: MTD device structure
* @offs: offset in the device
* @allowbbt: allow access to bad block table region
*
*/
int nand_isbad_bbt (struct mtd_info *mtd, loff_t offs, int allowbbt)
{
struct nand_chip *this = mtd->priv;
int block;
uint8_t res;
/* Get block number * 2 */
block = (int) (offs >> (this->bbt_erase_shift - 1));
res = (this->bbt[block >> 3] >> (block & 0x06)) & 0x03;
DEBUG (MTD_DEBUG_LEVEL2, "nand_isbad_bbt(): bbt info for offs 0x%08x: (block %d) 0x%02x\n",
(unsigned int)offs, res, block >> 1);
switch ((int)res) {
case 0x00: return 0;
case 0x01: return 1;
case 0x02: return allowbbt ? 0 : 1;
}
return 1;
}
EXPORT_SYMBOL (nand_scan_bbt);
EXPORT_SYMBOL (nand_default_bbt);
/* /*
* drivers/mtd/nand_ecc.c * This file contains an ECC algorithm from Toshiba that detects and
* corrects 1 bit errors in a 256 byte block of data.
* *
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * drivers/mtd/nand/nand_ecc.c
* Toshiba America Electronics Components, Inc.
* *
* $Id: nand_ecc.c,v 1.9 2003/02/20 13:34:19 sjhill Exp $ * Copyright (C) 2000-2004 Steven J. Hill (sjhill@realitydiluted.com)
* Toshiba America Electronics Components, Inc.
* *
* This program is free software; you can redistribute it and/or * $Id: nand_ecc.c,v 1.14 2004/06/16 15:34:37 gleixner Exp $
* modify it under the terms of the GNU Lesser General Public License
* version 2.1 as published by the Free Software Foundation.
* *
* This file contains an ECC algorithm from Toshiba that detects and * This file is free software; you can redistribute it and/or modify it
* corrects 1 bit errors in a 256 byte block of data. * under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 or (at your option) any
* later version.
*
* This file is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
* for more details.
*
* You should have received a copy of the GNU General Public License along
* with this file; if not, write to the Free Software Foundation, Inc.,
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*
* As a special exception, if other files instantiate templates or use
* macros or inline functions from these files, or you compile these
* files and link them with other works to produce a work based on these
* files, these files do not by themselves cause the resulting work to be
* covered by the GNU General Public License. However the source code for
* these files must still be made available in accordance with section (3)
* of the GNU General Public License.
*
* This exception does not invalidate any other reasons why a work based on
* this file might be covered by the GNU General Public License.
*/ */
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mtd/nand_ecc.h>
/* /*
* Pre-calculated 256-way 1 byte column parity * Pre-calculated 256-way 1 byte column parity
...@@ -41,7 +63,12 @@ static const u_char nand_ecc_precalc_table[] = { ...@@ -41,7 +63,12 @@ static const u_char nand_ecc_precalc_table[] = {
}; };
/* /**
* nand_trans_result - [GENERIC] create non-inverted ECC
* @reg2: line parity reg 2
* @reg3: line parity reg 3
* @ecc_code: ecc
*
* Creates non-inverted ECC code from line parity * Creates non-inverted ECC code from line parity
*/ */
static void nand_trans_result(u_char reg2, u_char reg3, static void nand_trans_result(u_char reg2, u_char reg3,
...@@ -81,10 +108,13 @@ static void nand_trans_result(u_char reg2, u_char reg3, ...@@ -81,10 +108,13 @@ static void nand_trans_result(u_char reg2, u_char reg3,
ecc_code[1] = tmp2; ecc_code[1] = tmp2;
} }
/* /**
* Calculate 3 byte ECC code for 256 byte block * nand_calculate_ecc - [NAND Interface] Calculate 3 byte ECC code for 256 byte block
* @mtd: MTD block structure
* @dat: raw data
* @ecc_code: buffer for ECC
*/ */
void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{ {
u_char idx, reg1, reg2, reg3; u_char idx, reg1, reg2, reg3;
int j; int j;
...@@ -114,12 +144,19 @@ void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) ...@@ -114,12 +144,19 @@ void nand_calculate_ecc (const u_char *dat, u_char *ecc_code)
ecc_code[0] = ~ecc_code[0]; ecc_code[0] = ~ecc_code[0];
ecc_code[1] = ~ecc_code[1]; ecc_code[1] = ~ecc_code[1];
ecc_code[2] = ((~reg1) << 2) | 0x03; ecc_code[2] = ((~reg1) << 2) | 0x03;
return 0;
} }
/* /**
* nand_correct_data - [NAND Interface] Detect and correct bit error(s)
* @mtd: MTD block structure
* @dat: raw data read from the chip
* @read_ecc: ECC from the chip
* @calc_ecc: the ECC calculated from raw data
*
* Detect and correct a 1 bit error for 256 byte block * Detect and correct a 1 bit error for 256 byte block
*/ */
int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc) int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc)
{ {
u_char a, b, c, d1, d2, d3, add, bit, i; u_char a, b, c, d1, d2, d3, add, bit, i;
......
...@@ -2,9 +2,8 @@ ...@@ -2,9 +2,8 @@
* drivers/mtd/nandids.c * drivers/mtd/nandids.c
* *
* 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.4 2003/05/21 15:15:08 dwmw2 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
...@@ -13,26 +12,99 @@ ...@@ -13,26 +12,99 @@
*/ */
#include <linux/module.h> #include <linux/module.h>
#include <linux/mtd/nand.h> #include <linux/mtd/nand.h>
/* /*
* Chip ID list * Chip ID list
*
* Name. ID code, pagesize, chipsize in MegaByte, eraseblock size,
* options
*
* Pagesize; 0, 256, 512
* 0 get this information from the extended chip ID
+ 256 256 Byte page size
* 512 512 Byte page size
*/ */
struct nand_flash_dev nand_flash_ids[] = { struct nand_flash_dev nand_flash_ids[] = {
{"NAND 1MiB 5V", 0x6e, 20, 0x1000, 1}, {"NAND 1MiB 5V 8-bit", 0x6e, 256, 1, 0x1000, 0},
{"NAND 2MiB 5V", 0x64, 21, 0x1000, 1}, {"NAND 2MiB 5V 8-bit", 0x64, 256, 2, 0x1000, 0},
{"NAND 4MiB 5V", 0x6b, 22, 0x2000, 0}, {"NAND 4MiB 5V 8-bit", 0x6b, 512, 4, 0x2000, 0},
{"NAND 1MiB 3,3V", 0xe8, 20, 0x1000, 1}, {"NAND 1MiB 3,3V 8-bit", 0xe8, 256, 1, 0x1000, 0},
{"NAND 1MiB 3,3V", 0xec, 20, 0x1000, 1}, {"NAND 1MiB 3,3V 8-bit", 0xec, 256, 1, 0x1000, 0},
{"NAND 2MiB 3,3V", 0xea, 21, 0x1000, 1}, {"NAND 2MiB 3,3V 8-bit", 0xea, 256, 2, 0x1000, 0},
{"NAND 4MiB 3,3V", 0xd5, 22, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xd5, 512, 4, 0x2000, 0},
{"NAND 4MiB 3,3V", 0xe3, 22, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xe3, 512, 4, 0x2000, 0},
{"NAND 4MiB 3,3V", 0xe5, 22, 0x2000, 0}, {"NAND 4MiB 3,3V 8-bit", 0xe5, 512, 4, 0x2000, 0},
{"NAND 8MiB 3,3V", 0xd6, 23, 0x2000, 0}, {"NAND 8MiB 3,3V 8-bit", 0xd6, 512, 8, 0x2000, 0},
{"NAND 8MiB 3,3V", 0xe6, 23, 0x2000, 0},
{"NAND 16MiB 3,3V", 0x73, 24, 0x4000, 0}, {"NAND 8MiB 1,8V 8-bit", 0x39, 512, 8, 0x2000, 0},
{"NAND 32MiB 3,3V", 0x75, 25, 0x4000, 0}, {"NAND 8MiB 3,3V 8-bit", 0xe6, 512, 8, 0x2000, 0},
{"NAND 64MiB 3,3V", 0x76, 26, 0x4000, 0}, {"NAND 8MiB 1,8V 16-bit", 0x49, 512, 8, 0x2000, NAND_BUSWIDTH_16},
{"NAND 128MiB 3,3V", 0x79, 27, 0x4000, 0}, {"NAND 8MiB 3,3V 16-bit", 0x59, 512, 8, 0x2000, NAND_BUSWIDTH_16},
{"NAND 16MiB 1,8V 8-bit", 0x33, 512, 16, 0x4000, 0},
{"NAND 16MiB 3,3V 8-bit", 0x73, 512, 16, 0x4000, 0},
{"NAND 16MiB 1,8V 16-bit", 0x43, 512, 16, 0x4000, NAND_BUSWIDTH_16},
{"NAND 16MiB 3,3V 16-bit", 0x53, 512, 16, 0x4000, NAND_BUSWIDTH_16},
{"NAND 32MiB 1,8V 8-bit", 0x35, 512, 32, 0x4000, 0},
{"NAND 32MiB 3,3V 8-bit", 0x75, 512, 32, 0x4000, 0},
{"NAND 32MiB 1,8V 16-bit", 0x45, 512, 32, 0x4000, NAND_BUSWIDTH_16},
{"NAND 32MiB 3,3V 16-bit", 0x55, 512, 32, 0x4000, NAND_BUSWIDTH_16},
{"NAND 64MiB 1,8V 8-bit", 0x36, 512, 64, 0x4000, 0},
{"NAND 64MiB 3,3V 8-bit", 0x76, 512, 64, 0x4000, 0},
{"NAND 64MiB 1,8V 16-bit", 0x46, 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 3,3V 8-bit", 0x79, 512, 128, 0x4000, 0},
{"NAND 128MiB 1,8V 16-bit", 0x72, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 128MiB 3,3V 16-bit", 0x74, 512, 128, 0x4000, NAND_BUSWIDTH_16},
{"NAND 256MiB 3,3V 8-bit", 0x71, 512, 256, 0x4000, 0},
{"NAND 512MiB 3,3V 8-bit", 0xDC, 512, 512, 0x4000, 0},
/* These are the new chips with large page size. The pagesize
* and the erasesize is determined from the extended id bytes
*/
/* 1 Gigabit */
{"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* 2 Gigabit */
{"NAND 256MiB 1,8V 8-bit", 0xAA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 256MiB 3,3V 8-bit", 0xDA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 256MiB 1,8V 16-bit", 0xBA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 256MiB 3,3V 16-bit", 0xCA, 0, 256, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* 4 Gigabit */
{"NAND 512MiB 1,8V 8-bit", 0xAC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 512MiB 3,3V 8-bit", 0xDC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 512MiB 1,8V 16-bit", 0xBC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 512MiB 3,3V 16-bit", 0xCC, 0, 512, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* 8 Gigabit */
{"NAND 1GiB 1,8V 8-bit", 0xA3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 1GiB 3,3V 8-bit", 0xD3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 1GiB 1,8V 16-bit", 0xB3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 1GiB 3,3V 16-bit", 0xC3, 0, 1024, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* 16 Gigabit */
{"NAND 2GiB 1,8V 8-bit", 0xA5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 2GiB 3,3V 8-bit", 0xD5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_NO_AUTOINCR},
{"NAND 2GiB 1,8V 16-bit", 0xB5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
{"NAND 2GiB 3,3V 16-bit", 0xC5, 0, 2048, 0, NAND_SAMSUNG_LP_OPTIONS | NAND_BUSWIDTH_16 | NAND_NO_AUTOINCR},
/* 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
* 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},
{NULL,} {NULL,}
}; };
...@@ -44,10 +116,11 @@ struct nand_manufacturers nand_manuf_ids[] = { ...@@ -44,10 +116,11 @@ struct nand_manufacturers nand_manuf_ids[] = {
{NAND_MFR_SAMSUNG, "Samsung"}, {NAND_MFR_SAMSUNG, "Samsung"},
{NAND_MFR_FUJITSU, "Fujitsu"}, {NAND_MFR_FUJITSU, "Fujitsu"},
{NAND_MFR_NATIONAL, "National"}, {NAND_MFR_NATIONAL, "National"},
{NAND_MFR_RENESAS, "Renesas"},
{NAND_MFR_STMICRO, "ST Micro"},
{0x0, "Unknown"} {0x0, "Unknown"}
}; };
EXPORT_SYMBOL (nand_manuf_ids); EXPORT_SYMBOL (nand_manuf_ids);
EXPORT_SYMBOL (nand_flash_ids); EXPORT_SYMBOL (nand_flash_ids);
......
/*
* drivers/mtd/nand/ppchameleonevb.c
*
* Copyright (C) 2003 DAVE Srl (info@wawnet.biz)
*
* Derived from drivers/mtd/nand/edb7312.c
*
*
* $Id: ppchameleonevb.c,v 1.2 2004/05/05 22:09:54 gleixner Exp $
*
* 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
* published by the Free Software Foundation.
*
* Overview:
* This is a device driver for the NAND flash devices found on the
* PPChameleon/PPChameleonEVB system.
* PPChameleon options (autodetected):
* - BA model: no NAND
* - ME model: 32MB (Samsung K9F5608U0B)
* - HI model: 128MB (Samsung K9F1G08UOM)
* PPChameleonEVB options:
* - 32MB (Samsung K9F5608U0B)
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <platforms/PPChameleonEVB.h>
#undef USE_READY_BUSY_PIN
#define USE_READY_BUSY_PIN
/* see datasheets (tR) */
#define NAND_BIG_DELAY_US 25
#define NAND_SMALL_DELAY_US 10
/* handy sizes */
#define SZ_4M 0x00400000
#define NAND_SMALL_SIZE 0x02000000
#define NAND_MTD_NAME "ppchameleon-nand"
#define NAND_EVB_MTD_NAME "ppchameleonevb-nand"
/* GPIO pins used to drive NAND chip mounted on processor module */
#define NAND_nCE_GPIO_PIN (0x80000000 >> 1)
#define NAND_CLE_GPIO_PIN (0x80000000 >> 2)
#define NAND_ALE_GPIO_PIN (0x80000000 >> 3)
#define NAND_RB_GPIO_PIN (0x80000000 >> 4)
/* GPIO pins used to drive NAND chip mounted on EVB */
#define NAND_EVB_nCE_GPIO_PIN (0x80000000 >> 14)
#define NAND_EVB_CLE_GPIO_PIN (0x80000000 >> 15)
#define NAND_EVB_ALE_GPIO_PIN (0x80000000 >> 16)
#define NAND_EVB_RB_GPIO_PIN (0x80000000 >> 31)
/*
* MTD structure for PPChameleonEVB board
*/
static struct mtd_info *ppchameleon_mtd = NULL;
static struct mtd_info *ppchameleonevb_mtd = NULL;
/*
* Module stuff
*/
static int ppchameleon_fio_pbase = CFG_NAND0_PADDR;
static int ppchameleonevb_fio_pbase = CFG_NAND1_PADDR;
#ifdef MODULE
MODULE_PARM(ppchameleon_fio_pbase, "i");
__setup("ppchameleon_fio_pbase=",ppchameleon_fio_pbase);
MODULE_PARM(ppchameleonevb_fio_pbase, "i");
__setup("ppchameleonevb_fio_pbase=",ppchameleonevb_fio_pbase);
#endif
/* Internal buffers. Page buffer and oob buffer for one block */
static u_char data_buf[2048 + 64];
static u_char oob_buf[64 * 64];
static u_char data_buf_evb[512 + 16];
static u_char oob_buf_evb[16 * 32];
#ifdef CONFIG_MTD_PARTITIONS
/*
* Define static partitions for flash devices
*/
static struct mtd_partition partition_info_hi[] = {
{ name: "PPChameleon HI Nand Flash",
offset: 0,
size: 128*1024*1024 }
};
static struct mtd_partition partition_info_me[] = {
{ name: "PPChameleon ME Nand Flash",
offset: 0,
size: 32*1024*1024 }
};
static struct mtd_partition partition_info_evb[] = {
{ name: "PPChameleonEVB Nand Flash",
offset: 0,
size: 32*1024*1024 }
};
#define NUM_PARTITIONS 1
extern int parse_cmdline_partitions(struct mtd_info *master,
struct mtd_partition **pparts,
const char *mtd_id);
#endif
/*
* hardware specific access to control-lines
*/
static void ppchameleon_hwcontrol(struct mtd_info *mtdinfo, int cmd)
{
switch(cmd) {
case NAND_CTL_SETCLE:
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_CLRCLE:
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_SETALE:
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_CLRALE:
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_SETNCE:
MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND0_PADDR);
break;
case NAND_CTL_CLRNCE:
MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND0_PADDR);
break;
}
}
static void ppchameleonevb_hwcontrol(struct mtd_info *mtdinfo, int cmd)
{
switch(cmd) {
case NAND_CTL_SETCLE:
MACRO_NAND_CTL_SETCLE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_CLRCLE:
MACRO_NAND_CTL_CLRCLE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_SETALE:
MACRO_NAND_CTL_SETALE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_CLRALE:
MACRO_NAND_CTL_CLRALE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_SETNCE:
MACRO_NAND_ENABLE_CE((unsigned long)CFG_NAND1_PADDR);
break;
case NAND_CTL_CLRNCE:
MACRO_NAND_DISABLE_CE((unsigned long)CFG_NAND1_PADDR);
break;
}
}
#ifdef USE_READY_BUSY_PIN
/*
* read device ready pin
*/
static int ppchameleon_device_ready(struct mtd_info *minfo)
{
if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_RB_GPIO_PIN)
return 1;
return 0;
}
static int ppchameleonevb_device_ready(struct mtd_info *minfo)
{
if (in_be32((volatile unsigned*)GPIO0_IR) & NAND_EVB_RB_GPIO_PIN)
return 1;
return 0;
}
#endif
#ifdef CONFIG_MTD_PARTITIONS
const char *part_probes[] = { "cmdlinepart", NULL };
const char *part_probes_evb[] = { "cmdlinepart", NULL };
#endif
/*
* Main initialization routine
*/
static int __init ppchameleonevb_init (void)
{
struct nand_chip *this;
const char *part_type = 0;
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
int ppchameleon_fio_base;
int ppchameleonevb_fio_base;
/*********************************
* Processor module NAND (if any) *
*********************************/
/* Allocate memory for MTD device structure and private data */
ppchameleon_mtd = kmalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip),
GFP_KERNEL);
if (!ppchameleon_mtd) {
printk("Unable to allocate PPChameleon NAND MTD device structure.\n");
return -ENOMEM;
}
/* map physical address */
ppchameleon_fio_base = (unsigned long)ioremap(ppchameleon_fio_pbase, SZ_4M);
if(!ppchameleon_fio_base) {
printk("ioremap PPChameleon NAND flash failed\n");
kfree(ppchameleon_mtd);
return -EIO;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&ppchameleon_mtd[1]);
/* Initialize structures */
memset((char *) ppchameleon_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
ppchameleon_mtd->priv = this;
/* Initialize GPIOs */
/* Pin mapping for NAND chip */
/*
CE GPIO_01
CLE GPIO_02
ALE GPIO_03
R/B GPIO_04
*/
/* output select */
out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xC0FFFFFF);
/* three-state select */
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xC0FFFFFF);
/* enable output driver */
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_nCE_GPIO_PIN | NAND_CLE_GPIO_PIN | NAND_ALE_GPIO_PIN);
#ifdef USE_READY_BUSY_PIN
/* three-state select */
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFF3FFFFF);
/* high-impedecence */
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_RB_GPIO_PIN));
/* input select */
out_be32((volatile unsigned*)GPIO0_ISR1H, (in_be32((volatile unsigned*)GPIO0_ISR1H) & 0xFF3FFFFF) | 0x00400000);
#endif
/* insert callbacks */
this->IO_ADDR_R = ppchameleon_fio_base;
this->IO_ADDR_W = ppchameleon_fio_base;
this->hwcontrol = ppchameleon_hwcontrol;
#ifdef USE_READY_BUSY_PIN
this->dev_ready = ppchameleon_device_ready;
#endif
this->chip_delay = NAND_BIG_DELAY_US;
/* ECC mode */
this->eccmode = NAND_ECC_SOFT;
/* Set internal data buffer */
this->data_buf = data_buf;
this->oob_buf = oob_buf;
/* Scan to find existence of the device (it could not be mounted) */
if (nand_scan (ppchameleon_mtd, 1)) {
iounmap((void *)ppchameleon_fio_base);
kfree (ppchameleon_mtd);
goto nand_evb_init;
}
#ifndef USE_READY_BUSY_PIN
/* Adjust delay if necessary */
if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
this->chip_delay = NAND_SMALL_DELAY_US;
#endif
#ifdef CONFIG_MTD_PARTITIONS
ppchameleon_mtd->name = "ppchameleon-nand";
mtd_parts_nb = parse_mtd_partitions(ppchameleon_mtd, part_probes, &mtd_parts, 0);
if (mtd_parts_nb > 0)
part_type = "command line";
else
mtd_parts_nb = 0;
#endif
if (mtd_parts_nb == 0)
{
if (ppchameleon_mtd->size == NAND_SMALL_SIZE)
mtd_parts = partition_info_me;
else
mtd_parts = partition_info_hi;
mtd_parts_nb = NUM_PARTITIONS;
part_type = "static";
}
/* Register the partitions */
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
add_mtd_partitions(ppchameleon_mtd, mtd_parts, mtd_parts_nb);
nand_evb_init:
/****************************
* EVB NAND (always present) *
****************************/
/* Allocate memory for MTD device structure and private data */
ppchameleonevb_mtd = kmalloc(sizeof(struct mtd_info) +
sizeof(struct nand_chip),
GFP_KERNEL);
if (!ppchameleonevb_mtd) {
printk("Unable to allocate PPChameleonEVB NAND MTD device structure.\n");
return -ENOMEM;
}
/* map physical address */
ppchameleonevb_fio_base = (unsigned long)ioremap(ppchameleonevb_fio_pbase, SZ_4M);
if(!ppchameleonevb_fio_base) {
printk("ioremap PPChameleonEVB NAND flash failed\n");
kfree(ppchameleonevb_mtd);
return -EIO;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&ppchameleonevb_mtd[1]);
/* Initialize structures */
memset((char *) ppchameleonevb_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
ppchameleonevb_mtd->priv = this;
/* Initialize GPIOs */
/* Pin mapping for NAND chip */
/*
CE GPIO_14
CLE GPIO_15
ALE GPIO_16
R/B GPIO_31
*/
/* output select */
out_be32((volatile unsigned*)GPIO0_OSRH, in_be32((volatile unsigned*)GPIO0_OSRH) & 0xFFFFFFF0);
out_be32((volatile unsigned*)GPIO0_OSRL, in_be32((volatile unsigned*)GPIO0_OSRL) & 0x3FFFFFFF);
/* three-state select */
out_be32((volatile unsigned*)GPIO0_TSRH, in_be32((volatile unsigned*)GPIO0_TSRH) & 0xFFFFFFF0);
out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0x3FFFFFFF);
/* enable output driver */
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) | NAND_EVB_nCE_GPIO_PIN | NAND_EVB_CLE_GPIO_PIN | NAND_EVB_ALE_GPIO_PIN);
#ifdef USE_READY_BUSY_PIN
/* three-state select */
out_be32((volatile unsigned*)GPIO0_TSRL, in_be32((volatile unsigned*)GPIO0_TSRL) & 0xFFFFFFFC);
/* high-impedecence */
out_be32((volatile unsigned*)GPIO0_TCR, in_be32((volatile unsigned*)GPIO0_TCR) & (~NAND_EVB_RB_GPIO_PIN));
/* input select */
out_be32((volatile unsigned*)GPIO0_ISR1L, (in_be32((volatile unsigned*)GPIO0_ISR1L) & 0xFFFFFFFC) | 0x00000001);
#endif
/* insert callbacks */
this->IO_ADDR_R = ppchameleonevb_fio_base;
this->IO_ADDR_W = ppchameleonevb_fio_base;
this->hwcontrol = ppchameleonevb_hwcontrol;
#ifdef USE_READY_BUSY_PIN
this->dev_ready = ppchameleonevb_device_ready;
#endif
this->chip_delay = NAND_SMALL_DELAY_US;
/* ECC mode */
this->eccmode = NAND_ECC_SOFT;
/* Set internal data buffer */
this->data_buf = data_buf_evb;
this->oob_buf = oob_buf_evb;
/* Scan to find existence of the device */
if (nand_scan (ppchameleonevb_mtd, 1)) {
iounmap((void *)ppchameleonevb_fio_base);
kfree (ppchameleonevb_mtd);
return -ENXIO;
}
#ifdef CONFIG_MTD_PARTITIONS
ppchameleonevb_mtd->name = NAND_EVB_MTD_NAME;
mtd_parts_nb = parse_mtd_partitions(ppchameleonevb_mtd, part_probes_evb, &mtd_parts, 0);
if (mtd_parts_nb > 0)
part_type = "command line";
else
mtd_parts_nb = 0;
#endif
if (mtd_parts_nb == 0)
{
mtd_parts = partition_info_evb;
mtd_parts_nb = NUM_PARTITIONS;
part_type = "static";
}
/* Register the partitions */
printk(KERN_NOTICE "Using %s partition definition\n", part_type);
add_mtd_partitions(ppchameleonevb_mtd, mtd_parts, mtd_parts_nb);
/* Return happy */
return 0;
}
module_init(ppchameleonevb_init);
/*
* Clean up routine
*/
static void __exit ppchameleonevb_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &ppchameleonevb_mtd[1];
/* Unregister the device */
del_mtd_device (ppchameleonevb_mtd);
/* Free internal data buffer */
kfree (this->data_buf);
/* Free the MTD device structure */
kfree (ppchameleonevb_mtd);
}
module_exit(ppchameleonevb_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("DAVE Srl <support-ppchameleon@dave-tech.it>");
MODULE_DESCRIPTION("MTD map driver for DAVE Srl PPChameleonEVB board");
...@@ -8,7 +8,7 @@ ...@@ -8,7 +8,7 @@
* to controllines (due to change in nand.c) * to controllines (due to change in nand.c)
* page_cache added * page_cache added
* *
* $Id: spia.c,v 1.19 2003/04/20 07:24:40 gleixner Exp $ * $Id: spia.c,v 1.21 2003/07/11 15:12:29 dwmw2 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
...@@ -20,6 +20,8 @@ ...@@ -20,6 +20,8 @@
* a 64Mibit (8MiB x 8 bits) NAND flash device. * a 64Mibit (8MiB x 8 bits) NAND flash device.
*/ */
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
...@@ -35,14 +37,14 @@ static struct mtd_info *spia_mtd = NULL; ...@@ -35,14 +37,14 @@ static struct mtd_info *spia_mtd = NULL;
/* /*
* Values specific to the SPIA board (used with EP7212 processor) * Values specific to the SPIA board (used with EP7212 processor)
*/ */
#define SPIA_IO_ADDR = 0xd0000000 /* Start of EP7212 IO address space */ #define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */
#define SPIA_FIO_ADDR = 0xf0000000 /* Address where flash is mapped */ #define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */
#define SPIA_PEDR = 0x0080 /* #define SPIA_PEDR 0x0080 /*
* IO offset to Port E data register * IO offset to Port E data register
* where the CLE, ALE and NCE pins * where the CLE, ALE and NCE pins
* are wired to. * are wired to.
*/ */
#define SPIA_PEDDR = 0x00c0 /* #define SPIA_PEDDR 0x00c0 /*
* IO offset to Port E data direction * IO offset to Port E data direction
* register so we can control the IO * register so we can control the IO
* lines. * lines.
...@@ -62,11 +64,6 @@ MODULE_PARM(spia_fio_base, "i"); ...@@ -62,11 +64,6 @@ MODULE_PARM(spia_fio_base, "i");
MODULE_PARM(spia_pedr, "i"); MODULE_PARM(spia_pedr, "i");
MODULE_PARM(spia_peddr, "i"); MODULE_PARM(spia_peddr, "i");
__setup("spia_io_base=",spia_io_base);
__setup("spia_fio_base=",spia_fio_base);
__setup("spia_pedr=",spia_pedr);
__setup("spia_peddr=",spia_peddr);
/* /*
* Define partitions for flash device * Define partitions for flash device
*/ */
...@@ -88,7 +85,7 @@ const static struct mtd_partition partition_info[] = { ...@@ -88,7 +85,7 @@ const static struct mtd_partition partition_info[] = {
/* /*
* hardware specific access to control-lines * hardware specific access to control-lines
*/ */
void spia_hwcontrol(int cmd){ static void spia_hwcontrol(struct mtd_info *mtd, int cmd){
switch(cmd){ switch(cmd){
...@@ -143,7 +140,7 @@ int __init spia_init (void) ...@@ -143,7 +140,7 @@ int __init spia_init (void)
this->chip_delay = 15; this->chip_delay = 15;
/* Scan to find existence of the device */ /* Scan to find existence of the device */
if (nand_scan (spia_mtd)) { if (nand_scan (spia_mtd, 1)) {
kfree (spia_mtd); kfree (spia_mtd);
return -ENXIO; return -ENXIO;
} }
......
/*
* drivers/mtd/nand/toto.c
*
* Copyright (c) 2003 Texas Instruments
*
* Derived from drivers/mtd/autcpu12.c
*
* Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de>
*
* 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
* published by the Free Software Foundation.
*
* Overview:
* This is a device driver for the NAND flash device found on the
* TI fido board. It supports 32MiB and 64MiB cards
*
* $Id: toto.c,v 1.2 2003/10/21 10:04:58 dwmw2 Exp $
*/
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/delay.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/arch/hardware.h>
#include <asm/sizes.h>
#include <asm/arch/toto.h>
#include <asm/arch-omap1510/hardware.h>
#include <asm/arch/gpio.h>
/*
* MTD structure for TOTO board
*/
static struct mtd_info *toto_mtd = NULL;
static int toto_io_base = OMAP_FLASH_1_BASE;
#define CONFIG_NAND_WORKAROUND 1
#define NAND_NCE 0x4000
#define NAND_CLE 0x1000
#define NAND_ALE 0x0002
#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE)
#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0)
#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE)
#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */
#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2)
#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0)
#else
#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0)
#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE)
#endif
#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0)
#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE)
/*
* Define partitions for flash devices
*/
static struct mtd_partition partition_info64M[] = {
{ .name = "toto kernel partition 1",
.offset = 0,
.size = 2 * SZ_1M },
{ .name = "toto file sys partition 2",
.offset = 2 * SZ_1M,
.size = 14 * SZ_1M },
{ .name = "toto user partition 3",
.offset = 16 * SZ_1M,
.size = 16 * SZ_1M },
{ .name = "toto devboard extra partition 4",
.offset = 32 * SZ_1M,
.size = 32 * SZ_1M },
};
static struct mtd_partition partition_info32M[] = {
{ .name = "toto kernel partition 1",
.offset = 0,
.size = 2 * SZ_1M },
{ .name = "toto file sys partition 2",
.offset = 2 * SZ_1M,
.size = 14 * SZ_1M },
{ .name = "toto user partition 3",
.offset = 16 * SZ_1M,
.size = 16 * SZ_1M },
};
#define NUM_PARTITIONS32M 3
#define NUM_PARTITIONS64M 4
/*
* hardware specific access to control-lines
*/
static void toto_hwcontrol(struct mtd_info *mtd, int cmd)
{
udelay(1); /* hopefully enough time for tc make proceding write to clear */
switch(cmd){
case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break;
case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break;
case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break;
case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break;
case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break;
case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break;
}
udelay(1); /* allow time to ensure gpio state to over take memory write */
}
/*
* Main initialization routine
*/
int __init toto_init (void)
{
struct nand_chip *this;
int err = 0;
/* Allocate memory for MTD device structure and private data */
toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!toto_mtd) {
printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n");
err = -ENOMEM;
goto out;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&toto_mtd[1]);
/* Initialize structures */
memset((char *) toto_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
toto_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = toto_io_base;
this->IO_ADDR_W = toto_io_base;
this->hwcontrol = toto_hwcontrol;
this->dev_ready = NULL;
/* 25 us command delay time */
this->chip_delay = 30;
this->eccmode = NAND_ECC_SOFT;
/* Scan to find existance of the device */
if (nand_scan (toto_mtd, 1)) {
err = -ENXIO;
goto out_mtd;
}
/* Allocate memory for internal data buffer */
this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL);
if (!this->data_buf) {
printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n");
err = -ENOMEM;
goto out_mtd;
}
/* Register the partitions */
switch(toto_mtd->size){
case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break;
case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break;
default: {
printk (KERN_WARNING "Unsupported Nand device\n");
err = -ENXIO;
goto out_buf;
}
}
gpioreserve(NAND_MASK); /* claim our gpios */
archflashwp(0,0); /* open up flash for writing */
goto out;
out_buf:
kfree (this->data_buf);
out_mtd:
kfree (toto_mtd);
out:
return err;
}
module_init(toto_init);
/*
* Clean up routine
*/
static void __exit toto_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &toto_mtd[1];
/* Unregister partitions */
del_mtd_partitions(toto_mtd);
/* Unregister the device */
del_mtd_device (toto_mtd);
/* Free internal data buffers */
kfree (this->data_buf);
/* Free the MTD device structure */
kfree (toto_mtd);
/* stop flash writes */
archflashwp(0,1);
/* release gpios to system */
gpiorelease(NAND_MASK);
}
module_exit(toto_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Richard Woodruff <r-woodruff2@ti.com>");
MODULE_DESCRIPTION("Glue layer for NAND flash on toto board");
/*
* drivers/mtd/tx4925ndfmc.c
*
* Overview:
* This is a device driver for the NAND flash device found on the
* Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports
* 16MiB, 32MiB and 64MiB cards.
*
* Author: MontaVista Software, Inc. source@mvista.com
*
* Derived from drivers/mtd/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
* $Id: tx4925ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $
*
* Copyright (C) 2001 Toshiba Corporation
*
* 2003 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*
*/
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/partitions.h>
#include <linux/delay.h>
#include <asm/io.h>
#include <asm/tx4925/tx4925_nand.h>
extern struct nand_oobinfo jffs2_oobinfo;
/*
* MTD structure for RBTX4925 board
*/
static struct mtd_info *tx4925ndfmc_mtd = NULL;
/*
* Module stuff
*/
#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE)
#define tx4925ndfmc_init init_module
#define tx4925ndfmc_cleanup cleanup_module
#endif
/*
* Define partitions for flash devices
*/
static struct mtd_partition partition_info16k[] = {
{ .name = "RBTX4925 flash partition 1",
.offset = 0,
.size = 8 * 0x00100000 },
{ .name = "RBTX4925 flash partition 2",
.offset = 8 * 0x00100000,
.size = 8 * 0x00100000 },
};
static struct mtd_partition partition_info32k[] = {
{ .name = "RBTX4925 flash partition 1",
.offset = 0,
.size = 8 * 0x00100000 },
{ .name = "RBTX4925 flash partition 2",
.offset = 8 * 0x00100000,
.size = 24 * 0x00100000 },
};
static struct mtd_partition partition_info64k[] = {
{ .name = "User FS",
.offset = 0,
.size = 16 * 0x00100000 },
{ .name = "RBTX4925 flash partition 2",
.offset = 16 * 0x00100000,
.size = 48 * 0x00100000},
};
static struct mtd_partition partition_info128k[] = {
{ .name = "Skip bad section",
.offset = 0,
.size = 16 * 0x00100000 },
{ .name = "User FS",
.offset = 16 * 0x00100000,
.size = 112 * 0x00100000 },
};
#define NUM_PARTITIONS16K 2
#define NUM_PARTITIONS32K 2
#define NUM_PARTITIONS64K 2
#define NUM_PARTITIONS128K 2
/*
* hardware specific access to control-lines
*/
static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
{
switch(cmd){
case NAND_CTL_SETCLE:
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE;
break;
case NAND_CTL_CLRCLE:
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE;
break;
case NAND_CTL_SETALE:
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE;
break;
case NAND_CTL_CLRALE:
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE;
break;
case NAND_CTL_SETNCE:
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE;
break;
case NAND_CTL_CLRNCE:
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE;
break;
case NAND_CTL_SETWP:
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE;
break;
case NAND_CTL_CLRWP:
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE;
break;
}
}
/*
* read device ready pin
*/
static int tx4925ndfmc_device_ready(struct mtd_info *mtd)
{
int ready;
ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1;
return ready;
}
void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
{
/* reset first */
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK;
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB;
}
static void tx4925ndfmc_disable_ecc(void)
{
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
}
static void tx4925ndfmc_enable_read_ecc(void)
{
tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK;
tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ;
}
void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){
int i;
u_char *ecc = ecc_code;
tx4925ndfmc_enable_read_ecc();
for (i = 0;i < 6;i++,ecc++)
*ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr));
tx4925ndfmc_disable_ecc();
}
void tx4925ndfmc_device_setup(void)
{
*(unsigned char *)0xbb005000 &= ~0x08;
/* reset NDFMC */
tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST;
while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST);
/* setup BusSeparete, Hold Time, Strobe Pulse Width */
tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0;
tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW;
}
static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
return tx4925_read_nfmc(this->IO_ADDR_R);
}
static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd->priv;
tx4925_write_nfmc(byte, this->IO_ADDR_W);
}
static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
tx4925_write_nfmc(buf[i], this->IO_ADDR_W);
}
static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
buf[i] = tx4925_read_nfmc(this->IO_ADDR_R);
}
static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
if (buf[i] != tx4925_read_nfmc(this->IO_ADDR_R))
return -EFAULT;
return 0;
}
/*
* Send command to NAND device
*/
static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
/* Begin command latch cycle */
this->hwcontrol(mtd, NAND_CTL_SETCLE);
/*
* Write out the command to the device.
*/
if (command == NAND_CMD_SEQIN) {
int readcmd;
if (column >= mtd->oobblock) {
/* OOB area */
column -= mtd->oobblock;
readcmd = NAND_CMD_READOOB;
} else if (column < 256) {
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0;
} else {
column -= 256;
readcmd = NAND_CMD_READ1;
}
this->write_byte(mtd, readcmd);
}
this->write_byte(mtd, command);
/* Set ALE and clear CLE to start address cycle */
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
if (column != -1 || page_addr != -1) {
this->hwcontrol(mtd, NAND_CTL_SETALE);
/* Serially input address */
if (column != -1)
this->write_byte(mtd, column);
if (page_addr != -1) {
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
/* One more address cycle for higher density devices */
if (mtd->size & 0x0c000000)
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
this->hwcontrol(mtd, NAND_CTL_CLRALE);
}
/*
* program and erase have their own busy handlers
* status and sequential in needs no delay
*/
switch (command) {
case NAND_CMD_PAGEPROG:
/* Turn off WE */
this->hwcontrol (mtd, NAND_CTL_CLRWP);
return;
case NAND_CMD_SEQIN:
/* Turn on WE */
this->hwcontrol (mtd, NAND_CTL_SETWP);
return;
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET:
if (this->dev_ready)
break;
this->hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_STATUS);
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
while ( !(this->read_byte(mtd) & 0x40));
return;
/* This applies to read commands */
default:
/*
* If we don't have access to the busy pin, we apply the given
* command delay
*/
if (!this->dev_ready) {
udelay (this->chip_delay);
return;
}
}
/* wait until command is processed */
while (!this->dev_ready(mtd));
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio
n **pparts, char *);
#endif
/*
* Main initialization routine
*/
extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
int __init tx4925ndfmc_init (void)
{
struct nand_chip *this;
int err = 0;
/* Allocate memory for MTD device structure and private data */
tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!tx4925ndfmc_mtd) {
printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n");
err = -ENOMEM;
goto out;
}
tx4925ndfmc_device_setup();
/* io is indirect via a register so don't need to ioremap address */
/* Get pointer to private data */
this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]);
/* Initialize structures */
memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
tx4925ndfmc_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr);
this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr);
this->hwcontrol = tx4925ndfmc_hwcontrol;
this->enable_hwecc = tx4925ndfmc_enable_hwecc;
this->calculate_ecc = tx4925ndfmc_readecc;
this->correct_data = nand_correct_data;
this->eccmode = NAND_ECC_HW6_512;
this->dev_ready = tx4925ndfmc_device_ready;
/* 20 us command delay time */
this->chip_delay = 20;
this->read_byte = tx4925ndfmc_nand_read_byte;
this->write_byte = tx4925ndfmc_nand_write_byte;
this->cmdfunc = tx4925ndfmc_nand_command;
this->write_buf = tx4925ndfmc_nand_write_buf;
this->read_buf = tx4925ndfmc_nand_read_buf;
this->verify_buf = tx4925ndfmc_nand_verify_buf;
/* Scan to find existance of the device */
if (nand_scan (tx4925ndfmc_mtd, 1)) {
err = -ENXIO;
goto out_ior;
}
/* Allocate memory for internal data buffer */
this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL);
if (!this->data_buf) {
printk ("Unable to allocate NAND data buffer for RBTX4925.\n");
err = -ENOMEM;
goto out_ior;
}
/* Register the partitions */
#ifdef CONFIG_MTD_CMDLINE_PARTS
{
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc");
if (mtd_parts_nb > 0)
add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb);
else
add_mtd_device(tx4925ndfmc_mtd);
}
#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */
switch(tx4925ndfmc_mtd->size){
case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break;
case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break;
case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break;
case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break;
default: {
printk ("Unsupported SmartMedia device\n");
err = -ENXIO;
goto out_buf;
}
}
#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */
goto out;
out_buf:
kfree (this->data_buf);
out_ior:
out:
return err;
}
module_init(tx4925ndfmc_init);
/*
* Clean up routine
*/
#ifdef MODULE
static void __exit tx4925ndfmc_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1];
/* Unregister partitions */
del_mtd_partitions(tx4925ndfmc_mtd);
/* Unregister the device */
del_mtd_device (tx4925ndfmc_mtd);
/* Free internal data buffers */
kfree (this->data_buf);
/* Free the MTD device structure */
kfree (tx4925ndfmc_mtd);
}
module_exit(tx4925ndfmc_cleanup);
#endif
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925");
/*
* drivers/mtd/nand/tx4938ndfmc.c
*
* Overview:
* This is a device driver for the NAND flash device connected to
* TX4938 internal NAND Memory Controller.
* TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit.
*
* Author: source@mvista.com
*
* Based on spia.c by Steven J. Hill
*
* $Id: tx4938ndfmc.c,v 1.2 2004/03/27 19:55:53 gleixner Exp $
*
* Copyright (C) 2000-2001 Toshiba Corporation
*
* 2003 (c) MontaVista Software, Inc. This file is licensed under the
* terms of the GNU General Public License version 2. This program is
* licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/config.h>
#include <linux/slab.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/nand.h>
#include <linux/mtd/nand_ecc.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/bootinfo.h>
#include <linux/delay.h>
#include <asm/tx4938/rbtx4938.h>
extern struct nand_oobinfo jffs2_oobinfo;
/*
* MTD structure for TX4938 NDFMC
*/
static struct mtd_info *tx4938ndfmc_mtd;
/*
* Define partitions for flash device
*/
#define flush_wb() (void)tx4938_ndfmcptr->mcr;
#define NUM_PARTITIONS 3
#define NUMBER_OF_CIS_BLOCKS 24
#define SIZE_OF_BLOCK 0x00004000
#define NUMBER_OF_BLOCK_PER_ZONE 1024
#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK)
#ifndef CONFIG_MTD_CMDLINE_PARTS
/*
* You can use the following sample of MTD partitions
* on the NAND Flash Memory 32MB or more.
*
* The following figure shows the image of the sample partition on
* the 32MB NAND Flash Memory.
*
* Block No.
* 0 +-----------------------------+ ------
* | CIS | ^
* 24 +-----------------------------+ |
* | kernel image | | Zone 0
* | | |
* +-----------------------------+ |
* 1023 | unused area | v
* +-----------------------------+ ------
* 1024 | JFFS2 | ^
* | | |
* | | | Zone 1
* | | |
* | | |
* | | v
* 2047 +-----------------------------+ ------
*
*/
static struct mtd_partition partition_info[NUM_PARTITIONS] = {
{
.name = "RBTX4938 CIS Area",
.offset = 0,
.size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK),
.mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
},
{
.name = "RBTX4938 kernel image",
.offset = MTDPART_OFS_APPEND,
.size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */
.mask_flags = MTD_WRITEABLE /* This partition is NOT writable */
},
{
.name = "Root FS (JFFS2)",
.offset = (0 + SIZE_OF_ZONE), /* start address of next zone */
.size = MTDPART_SIZ_FULL
},
};
#endif
static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd)
{
switch (cmd) {
case NAND_CTL_SETCLE:
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE;
break;
case NAND_CTL_CLRCLE:
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE;
break;
case NAND_CTL_SETALE:
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE;
break;
case NAND_CTL_CLRALE:
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE;
break;
/* TX4938_NDFMCR_CE bit is 0:high 1:low */
case NAND_CTL_SETNCE:
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE;
break;
case NAND_CTL_CLRNCE:
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE;
break;
case NAND_CTL_SETWP:
tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE;
break;
case NAND_CTL_CLRWP:
tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE;
break;
}
}
static int tx4938ndfmc_dev_ready(struct mtd_info *mtd)
{
flush_wb();
return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY);
}
static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code)
{
u32 mcr = tx4938_ndfmcptr->mcr;
mcr &= ~TX4938_NDFMCR_ECC_ALL;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ;
ecc_code[1] = tx4938_ndfmcptr->dtr;
ecc_code[0] = tx4938_ndfmcptr->dtr;
ecc_code[2] = tx4938_ndfmcptr->dtr;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
}
static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
{
u32 mcr = tx4938_ndfmcptr->mcr;
mcr &= ~TX4938_NDFMCR_ECC_ALL;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF;
tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON;
}
static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd)
{
struct nand_chip *this = mtd->priv;
return tx4938_read_nfmc(this->IO_ADDR_R);
}
static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte)
{
struct nand_chip *this = mtd->priv;
tx4938_write_nfmc(byte, this->IO_ADDR_W);
}
static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
tx4938_write_nfmc(buf[i], this->IO_ADDR_W);
}
static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
buf[i] = tx4938_read_nfmc(this->IO_ADDR_R);
}
static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len)
{
int i;
struct nand_chip *this = mtd->priv;
for (i=0; i<len; i++)
if (buf[i] != tx4938_read_nfmc(this->IO_ADDR_R))
return -EFAULT;
return 0;
}
/*
* Send command to NAND device
*/
static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
register struct nand_chip *this = mtd->priv;
/* Begin command latch cycle */
this->hwcontrol(mtd, NAND_CTL_SETCLE);
/*
* Write out the command to the device.
*/
if (command == NAND_CMD_SEQIN) {
int readcmd;
if (column >= mtd->oobblock) {
/* OOB area */
column -= mtd->oobblock;
readcmd = NAND_CMD_READOOB;
} else if (column < 256) {
/* First 256 bytes --> READ0 */
readcmd = NAND_CMD_READ0;
} else {
column -= 256;
readcmd = NAND_CMD_READ1;
}
this->write_byte(mtd, readcmd);
}
this->write_byte(mtd, command);
/* Set ALE and clear CLE to start address cycle */
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
if (column != -1 || page_addr != -1) {
this->hwcontrol(mtd, NAND_CTL_SETALE);
/* Serially input address */
if (column != -1)
this->write_byte(mtd, column);
if (page_addr != -1) {
this->write_byte(mtd, (unsigned char) (page_addr & 0xff));
this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff));
/* One more address cycle for higher density devices */
if (mtd->size & 0x0c000000)
this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f));
}
/* Latch in address */
this->hwcontrol(mtd, NAND_CTL_CLRALE);
}
/*
* program and erase have their own busy handlers
* status and sequential in needs no delay
*/
switch (command) {
case NAND_CMD_PAGEPROG:
/* Turn off WE */
this->hwcontrol (mtd, NAND_CTL_CLRWP);
return;
case NAND_CMD_SEQIN:
/* Turn on WE */
this->hwcontrol (mtd, NAND_CTL_SETWP);
return;
case NAND_CMD_ERASE1:
case NAND_CMD_ERASE2:
case NAND_CMD_STATUS:
return;
case NAND_CMD_RESET:
if (this->dev_ready)
break;
this->hwcontrol(mtd, NAND_CTL_SETCLE);
this->write_byte(mtd, NAND_CMD_STATUS);
this->hwcontrol(mtd, NAND_CTL_CLRCLE);
while ( !(this->read_byte(mtd) & 0x40));
return;
/* This applies to read commands */
default:
/*
* If we don't have access to the busy pin, we apply the given
* command delay
*/
if (!this->dev_ready) {
udelay (this->chip_delay);
return;
}
}
/* wait until command is processed */
while (!this->dev_ready(mtd));
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *);
#endif
/*
* Main initialization routine
*/
int __init tx4938ndfmc_init (void)
{
struct nand_chip *this;
int bsprt = 0, hold = 0xf, spw = 0xf;
int protected = 0;
if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) {
printk("TX4938 NDFMC: disabled by IOC PIOSEL\n");
return -ENODEV;
}
bsprt = 1;
hold = 2;
spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */
if ((tx4938_ccfgptr->pcfg &
(TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL))
!= TX4938_PCFG_NDF_SEL) {
printk("TX4938 NDFMC: disabled by PCFG.\n");
return -ENODEV;
}
/* reset NDFMC */
tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST;
while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST)
;
/* setup BusSeparete, Hold Time, Strobe Pulse Width */
tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0;
tx4938_ndfmcptr->spr = hold << 4 | spw;
/* Allocate memory for MTD device structure and private data */
tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip),
GFP_KERNEL);
if (!tx4938ndfmc_mtd) {
printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n");
return -ENOMEM;
}
/* Get pointer to private data */
this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]);
/* Initialize structures */
memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info));
memset((char *) this, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
tx4938ndfmc_mtd->priv = this;
/* Set address of NAND IO lines */
this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr;
this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr;
this->hwcontrol = tx4938ndfmc_hwcontrol;
this->dev_ready = tx4938ndfmc_dev_ready;
this->calculate_ecc = tx4938ndfmc_calculate_ecc;
this->correct_data = nand_correct_data;
this->enable_hwecc = tx4938ndfmc_enable_hwecc;
this->eccmode = NAND_ECC_HW3_256;
this->chip_delay = 100;
this->read_byte = tx4938ndfmc_nand_read_byte;
this->write_byte = tx4938ndfmc_nand_write_byte;
this->cmdfunc = tx4938ndfmc_nand_command;
this->write_buf = tx4938ndfmc_nand_write_buf;
this->read_buf = tx4938ndfmc_nand_read_buf;
this->verify_buf = tx4938ndfmc_nand_verify_buf;
/* Scan to find existance of the device */
if (nand_scan (tx4938ndfmc_mtd, 1)) {
kfree (tx4938ndfmc_mtd);
return -ENXIO;
}
/* Allocate memory for internal data buffer */
this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL);
if (!this->data_buf) {
printk ("Unable to allocate NAND data buffer for TX4938.\n");
kfree (tx4938ndfmc_mtd);
return -ENOMEM;
}
if (protected) {
printk(KERN_INFO "TX4938 NDFMC: write protected.\n");
tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE);
}
#ifdef CONFIG_MTD_CMDLINE_PARTS
{
int mtd_parts_nb = 0;
struct mtd_partition *mtd_parts = 0;
mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc");
if (mtd_parts_nb > 0)
add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb);
else
add_mtd_device(tx4938ndfmc_mtd);
}
#else
add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS );
#endif
return 0;
}
module_init(tx4938ndfmc_init);
/*
* Clean up routine
*/
static void __exit tx4938ndfmc_cleanup (void)
{
struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv;
/* Unregister the device */
#ifdef CONFIG_MTD_CMDLINE_PARTS
del_mtd_partitions(tx4938ndfmc_mtd);
#endif
del_mtd_device (tx4938ndfmc_mtd);
/* Free the MTD device structure */
kfree (tx4938ndfmc_mtd);
/* Free internal data buffer */
kfree (this->data_buf);
}
module_exit(tx4938ndfmc_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>");
MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC");
...@@ -5,7 +5,7 @@ ...@@ -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.25 2003/05/21 15:15:02 dwmw2 Exp $ * $Id: nand.h,v 1.63 2004/07/07 16:29:43 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
...@@ -44,6 +44,10 @@ ...@@ -44,6 +44,10 @@
* NAND_YAFFS_OOB * NAND_YAFFS_OOB
* 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL * 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL
* Split manufacturer and device ID structures * 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
...@@ -51,22 +55,46 @@ ...@@ -51,22 +55,46 @@
#include <linux/config.h> #include <linux/config.h>
#include <linux/wait.h> #include <linux/wait.h>
#include <linux/spinlock.h> #include <linux/spinlock.h>
#include <linux/mtd/mtd.h>
struct mtd_info; struct mtd_info;
/* /* Scan and identify a NAND device */
* Searches for a NAND device extern int nand_scan (struct mtd_info *mtd, int max_chips);
/* Free resources held by the NAND device */
extern void nand_release (struct mtd_info *mtd);
/* Read raw data from the device without ECC */
extern int nand_read_raw (struct mtd_info *mtd, uint8_t *buf, loff_t from, size_t len, size_t ooblen);
/* The maximum number of NAND chips in an array */
#define NAND_MAX_CHIPS 8
/* This constant declares the max. oobsize / page, which
* is supported now. If you add a chip with bigger oobsize/page
* adjust this accordingly.
*/ */
extern int nand_scan (struct mtd_info *mtd); #define NAND_MAX_OOBSIZE 64
/* /*
* Constants for hardware specific CLE/ALE/NCE function * Constants for hardware specific CLE/ALE/NCE function
*/ */
/* Select the chip by setting nCE to low */
#define NAND_CTL_SETNCE 1 #define NAND_CTL_SETNCE 1
/* Deselect the chip by setting nCE to high */
#define NAND_CTL_CLRNCE 2 #define NAND_CTL_CLRNCE 2
/* Select the command latch by setting CLE to high */
#define NAND_CTL_SETCLE 3 #define NAND_CTL_SETCLE 3
/* Deselect the command latch by setting CLE to low */
#define NAND_CTL_CLRCLE 4 #define NAND_CTL_CLRCLE 4
/* Select the address latch by setting ALE to high */
#define NAND_CTL_SETALE 5 #define NAND_CTL_SETALE 5
/* Deselect the address latch by setting ALE to low */
#define NAND_CTL_CLRALE 6 #define NAND_CTL_CLRALE 6
/* Set write protection by setting WP to high. Not used! */
#define NAND_CTL_SETWP 7
/* Clear write protection by setting WP to low. Not used! */
#define NAND_CTL_CLRWP 8
/* /*
* Standard NAND flash commands * Standard NAND flash commands
...@@ -77,35 +105,102 @@ extern int nand_scan (struct mtd_info *mtd); ...@@ -77,35 +105,102 @@ extern int nand_scan (struct mtd_info *mtd);
#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_SEQIN 0x80 #define NAND_CMD_SEQIN 0x80
#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 */
#define NAND_CMD_READSTART 0x30
#define NAND_CMD_CACHEDPROG 0x15
/* Status bits */
#define NAND_STATUS_FAIL 0x01
#define NAND_STATUS_FAIL_N1 0x02
#define NAND_STATUS_TRUE_READY 0x20
#define NAND_STATUS_READY 0x40
#define NAND_STATUS_WP 0x80
/* /*
* Constants for ECC_MODES * Constants for ECC_MODES
* */
* NONE: No ECC
* SOFT: Software ECC 3 byte ECC per 256 Byte data /* No ECC. Usage is not recommended ! */
* HW3_256: Hardware ECC 3 byte ECC per 256 Byte data
* HW3_512: Hardware ECC 3 byte ECC per 512 Byte data
*
*
*/
#define NAND_ECC_NONE 0 #define NAND_ECC_NONE 0
/* Software ECC 3 byte ECC per 256 Byte data */
#define NAND_ECC_SOFT 1 #define NAND_ECC_SOFT 1
/* Hardware ECC 3 byte ECC per 256 Byte data */
#define NAND_ECC_HW3_256 2 #define NAND_ECC_HW3_256 2
/* Hardware ECC 3 byte ECC per 512 Byte data */
#define NAND_ECC_HW3_512 3 #define NAND_ECC_HW3_512 3
/* Hardware ECC 3 byte ECC per 512 Byte data */
#define NAND_ECC_HW6_512 4 #define NAND_ECC_HW6_512 4
#define NAND_ECC_DISKONCHIP 5 /* Hardware ECC 8 byte ECC per 512 Byte data */
#define NAND_ECC_HW8_512 6
/* /*
* Constants for Hardware ECC * Constants for Hardware ECC
*/ */
/* Reset Hardware ECC for read */
#define NAND_ECC_READ 0 #define NAND_ECC_READ 0
/* Reset Hardware ECC for write */
#define NAND_ECC_WRITE 1 #define NAND_ECC_WRITE 1
/* Enable Hardware ECC before syndrom is read back from flash */
#define NAND_ECC_READSYN 2
/* Option constants for bizarre disfunctionality and real
* features
*/
/* Chip can not auto increment pages */
#define NAND_NO_AUTOINCR 0x00000001
/* Buswitdh is 16 bit */
#define NAND_BUSWIDTH_16 0x00000002
/* Device supports partial programming without padding */
#define NAND_NO_PADDING 0x00000004
/* Chip has cache program function */
#define NAND_CACHEPRG 0x00000008
/* Chip has copy back function */
#define NAND_COPYBACK 0x00000010
/* AND Chip which has 4 banks and a confusing page / block
* assignment. See Renesas datasheet for further information */
#define NAND_IS_AND 0x00000020
/* Chip has a array of 4 pages which can be read without
* additional ready /busy waits */
#define NAND_4PAGE_ARRAY 0x00000040
/* Options valid for Samsung large page devices */
#define NAND_SAMSUNG_LP_OPTIONS \
(NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK)
/* Macros to identify the above */
#define NAND_CANAUTOINCR(chip) (!(chip->options & NAND_NO_AUTOINCR))
#define NAND_MUST_PAD(chip) (!(chip->options & NAND_NO_PADDING))
#define NAND_HAS_CACHEPROG(chip) ((chip->options & NAND_CACHEPRG))
#define NAND_HAS_COPYBACK(chip) ((chip->options & NAND_COPYBACK))
/* Mask to zero out the chip options, which come from the id table */
#define NAND_CHIPOPTIONS_MSK (0x0000ffff & ~NAND_NO_AUTOINCR)
/* Non chip related options */
/* Use a flash based bad block table. This option is passed to the
* default bad block table function. */
#define NAND_USE_FLASH_BBT 0x00010000
/* The hw ecc generator provides a syndrome instead a ecc value on read
* This can only work if we have the ecc bytes directly behind the
* data bytes. Applies for DOC and AG-AND Renesas HW Reed Solomon generators */
#define NAND_HWECC_SYNDROME 0x00020000
/* Options set by nand scan */
/* Nand scan has allocated oob_buf */
#define NAND_OOBBUF_ALLOC 0x40000000
/* Nand scan has allocated data_buf */
#define NAND_DATABUF_ALLOC 0x80000000
/* /*
* nand_state_t - chip states
* Enumeration for NAND flash chip state * Enumeration for NAND flash chip state
*/ */
typedef enum { typedef enum {
...@@ -113,71 +208,116 @@ typedef enum { ...@@ -113,71 +208,116 @@ typedef enum {
FL_READING, FL_READING,
FL_WRITING, FL_WRITING,
FL_ERASING, FL_ERASING,
FL_SYNCING FL_SYNCING,
FL_CACHEDPRG,
} nand_state_t; } nand_state_t;
/* /**
* 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
* Structure overview: * @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
* IO_ADDR_R - address to read the 8 I/O lines of the flash device * @write_byte: [REPLACEABLE] write one byte to the chip
* * @read_word: [REPLACEABLE] read one word from the chip
* IO_ADDR_W - address to write the 8 I/O lines of the flash device * @write_word: [REPLACEABLE] write one word to the chip
* * @write_buf: [REPLACEABLE] write data from the buffer to the chip
* hwcontrol - hardwarespecific function for accesing control-lines * @read_buf: [REPLACEABLE] read data from the chip into the buffer
* * @verify_buf: [REPLACEABLE] verify buffer contents against the chip data
* dev_ready - hardwarespecific function for accesing device ready/busy line * @select_chip: [REPLACEABLE] select chip nr
* * @block_bad: [REPLACEABLE] check, if the block is bad
* waitfunc - hardwarespecific function for wait on ready * @block_markbad: [REPLACEABLE] mark the block bad
* * @hwcontrol: [BOARDSPECIFIC] hardwarespecific function for accesing control-lines
* calculate_ecc - function for ecc calculation or readback from ecc hardware * @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
* correct_data - function for ecc correction, matching to ecc generator (sw/hw) * is read from the chip status register
* * @cmdfunc: [REPLACEABLE] hardwarespecific function for writing commands to the chip
* enable_hwecc - function to enable (reset) hardware ecc generator * @waitfunc: [REPLACEABLE] hardwarespecific function for wait on ready
* * @calculate_ecc: [REPLACEABLE] function for ecc calculation or readback from ecc hardware
* eccmod - mode of ecc: see constants * @correct_data: [REPLACEABLE] function for ecc correction, matching to ecc generator (sw/hw)
* * @enable_hwecc: [BOARDSPECIFIC] function to enable (reset) hardware ecc generator. Must only
* eccsize - databytes used per ecc-calculation * be provided if a hardware ECC is available
* * @erase_cmd: [INTERN] erase command write function, selectable due to AND support
* chip_delay - chip dependent delay for transfering data from array to read regs (tR) * @scan_bbt: [REPLACEABLE] function to scan bad block table
* * @eccmode: [BOARDSPECIFIC] mode of ecc, see defines
* chip_lock - spinlock used to protect access to this structure * @eccsize: [INTERN] databytes used per ecc-calculation
* * @eccsteps: [INTERN] number of ecc calculation steps per page
* wq - wait queue to sleep on if a NAND operation is in progress * @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
* state - give the current state of the NAND device * @wq: [INTERN] wait queue to sleep on if a NAND operation is in progress
* * @state: [INTERN] the current state of the NAND device
* page_shift - 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
* data_buf - data buffer passed to/from MTD user modules * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry
* * @chip_shift: [INTERN] number of address bits in one chip
* data_cache - data cache for redundant page access and shadow for * @data_buf: [INTERN] internal buffer for one page + oob
* ECC failure * @oob_buf: [INTERN] oob buffer for one eraseblock
* * @oobdirty: [INTERN] indicates that oob_buf must be reinitialized
* cache_page - number of last valid page in page_cache * @data_poi: [INTERN] pointer to a data buffer
* @options: [BOARDSPECIFIC] various chip options. They can partly be set to inform nand_scan about
* special functionality. See the defines for further explanation
* @badblockpos: [INTERN] position of the bad block marker in the oob area
* @numchips: [INTERN] number of physical chips
* @chipsize: [INTERN] the size of one chip for multichip arrays
* @pagemask: [INTERN] page number mask = number of (pages / chip) - 1
* @pagebuf: [INTERN] holds the pagenumber which is currently in data_buf
* @autooob: [REPLACEABLE] the default (auto)placement scheme
* @bbt: [INTERN] bad block table pointer
* @bbt_td: [REPLACEABLE] bad block table descriptor for flash lookup
* @bbt_md: [REPLACEABLE] bad block table mirror descriptor
* @priv: [OPTIONAL] pointer to private chip date
*/ */
struct nand_chip { struct nand_chip {
unsigned long IO_ADDR_R; unsigned long IO_ADDR_R;
unsigned long IO_ADDR_W; unsigned long IO_ADDR_W;
void (*hwcontrol)(int cmd);
int (*dev_ready)(void); u_char (*read_byte)(struct mtd_info *mtd);
void (*write_byte)(struct mtd_info *mtd, u_char byte);
u16 (*read_word)(struct mtd_info *mtd);
void (*write_word)(struct mtd_info *mtd, u16 word);
void (*write_buf)(struct mtd_info *mtd, const u_char *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);
int (*block_bad)(struct mtd_info *mtd, loff_t ofs, int getchip);
int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
void (*hwcontrol)(struct mtd_info *mtd, int cmd);
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 state);
void (*calculate_ecc)(const u_char *dat, u_char *ecc_code); int (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
int (*correct_data)(u_char *dat, u_char *read_ecc, u_char *calc_ecc); int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
void (*enable_hwecc)(int mode); void (*enable_hwecc)(struct mtd_info *mtd, int mode);
void (*erase_cmd)(struct mtd_info *mtd, int page);
int (*scan_bbt)(struct mtd_info *mtd);
int eccmode; int eccmode;
int eccsize; int eccsize;
int eccsteps;
int chip_delay; int chip_delay;
spinlock_t chip_lock; spinlock_t chip_lock;
wait_queue_head_t wq; wait_queue_head_t wq;
nand_state_t state; nand_state_t state;
int page_shift; int page_shift;
int phys_erase_shift;
int bbt_erase_shift;
int chip_shift;
u_char *data_buf; u_char *data_buf;
u_char *oob_buf;
int oobdirty;
u_char *data_poi; u_char *data_poi;
unsigned int options;
int badblockpos;
int numchips;
unsigned long chipsize;
int pagemask;
int pagebuf;
struct nand_oobinfo *autooob;
uint8_t *bbt;
struct nand_bbt_descr *bbt_td;
struct nand_bbt_descr *bbt_md;
void *priv;
}; };
/* /*
...@@ -187,46 +327,35 @@ struct nand_chip { ...@@ -187,46 +327,35 @@ struct nand_chip {
#define NAND_MFR_SAMSUNG 0xec #define NAND_MFR_SAMSUNG 0xec
#define NAND_MFR_FUJITSU 0x04 #define NAND_MFR_FUJITSU 0x04
#define NAND_MFR_NATIONAL 0x8f #define NAND_MFR_NATIONAL 0x8f
#define NAND_MFR_RENESAS 0x07
#define NAND_MFR_STMICRO 0x20
/* /**
* NAND Flash Device ID Structure * struct nand_flash_dev - NAND Flash Device ID Structure
* *
* Structure overview: * @name: Identify the device type
* * @id: device ID code
* name - Identify the device type * @pagesize: Pagesize in bytes. Either 256 or 512 or 0
* * If the pagesize is 0, then the real pagesize
* id - device ID code * and the eraseize are determined from the
* * extended id bytes in the chip
* chipshift - total number of address bits for the device which * @erasesize: Size of an erase block in the flash device.
* is used to calculate address offsets and the total * @chipsize: Total chipsize in Mega Bytes
* number of bytes the device is capable of. * @options: Bitfield to store chip relevant options
*
* 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 { struct nand_flash_dev {
char * name; char *name;
int id; int id;
int chipshift; unsigned long pagesize;
unsigned long chipsize;
unsigned long erasesize; unsigned long erasesize;
char page256; unsigned long options;
}; };
/* /**
* NAND Flash Manufacturer ID Structure * struct nand_manufacturers - NAND Flash Manufacturer ID Structure
* * @name: Manufacturer name
* name - Manufacturer name * @id: manufacturer ID code of device.
*
* id - manufacturer ID code of device.
*/ */
struct nand_manufacturers { struct nand_manufacturers {
int id; int id;
...@@ -236,9 +365,85 @@ struct nand_manufacturers { ...@@ -236,9 +365,85 @@ struct nand_manufacturers {
extern struct nand_flash_dev nand_flash_ids[]; extern struct nand_flash_dev nand_flash_ids[];
extern struct nand_manufacturers nand_manuf_ids[]; extern struct nand_manufacturers nand_manuf_ids[];
/**
* struct nand_bbt_descr - bad block table descriptor
* @options: options for this descriptor
* @pages: the page(s) where we find the bbt, used with option BBT_ABSPAGE
* when bbt is searched, then we store the found bbts pages here.
* Its an array and supports up to 8 chips now
* @offs: offset of the pattern in the oob area of the page
* @veroffs: offset of the bbt version counter in the oob are of the page
* @version: version read from the bbt page during scan
* @len: length of the pattern, if 0 no pattern check is performed
* @maxblocks: maximum number of blocks to search for a bbt. This number of
* blocks is reserved at the end of the device where the tables are
* written.
* @reserved_block_code: if non-0, this pattern denotes a reserved (rather than
* bad) block in the stored bbt
* @pattern: pattern to identify bad block table or factory marked good /
* bad blocks, can be NULL, if len = 0
*
* Descriptor for the bad block table marker and the descriptor for the
* pattern which identifies good and bad blocks. The assumption is made
* that the pattern and the version count are always located in the oob area
* of the first block.
*/
struct nand_bbt_descr {
int options;
int pages[NAND_MAX_CHIPS];
int offs;
int veroffs;
uint8_t version[NAND_MAX_CHIPS];
int len;
int maxblocks;
int reserved_block_code;
uint8_t *pattern;
};
/* Options for the bad block table descriptors */
/* The number of bits used per block in the bbt on the device */
#define NAND_BBT_NRBITS_MSK 0x0000000F
#define NAND_BBT_1BIT 0x00000001
#define NAND_BBT_2BIT 0x00000002
#define NAND_BBT_4BIT 0x00000004
#define NAND_BBT_8BIT 0x00000008
/* The bad block table is in the last good block of the device */
#define NAND_BBT_LASTBLOCK 0x00000010
/* The bbt is at the given page, else we must scan for the bbt */
#define NAND_BBT_ABSPAGE 0x00000020
/* The bbt is at the given page, else we must scan for the bbt */
#define NAND_BBT_SEARCH 0x00000040
/* bbt is stored per chip on multichip devices */
#define NAND_BBT_PERCHIP 0x00000080
/* bbt has a version counter at offset veroffs */
#define NAND_BBT_VERSION 0x00000100
/* Create a bbt if none axists */
#define NAND_BBT_CREATE 0x00000200
/* Search good / bad pattern through all pages of a block */
#define NAND_BBT_SCANALLPAGES 0x00000400
/* Scan block empty during good / bad block scan */
#define NAND_BBT_SCANEMPTY 0x00000800
/* Write bbt if neccecary */
#define NAND_BBT_WRITE 0x00001000
/* Read and write back block contents when writing bbt */
#define NAND_BBT_SAVECONTENT 0x00002000
/* Search good / bad pattern on the first and the second page */
#define NAND_BBT_SCAN2NDPAGE 0x00004000
/* The maximum number of blocks to scan for a bbt */
#define NAND_BBT_SCAN_MAXBLOCKS 4
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_default_bbt (struct mtd_info *mtd);
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);
/* /*
* Constants for oob configuration * Constants for oob configuration
*/ */
#define NAND_BADBLOCK_POS 5 #define NAND_SMALL_BADBLOCK_POS 5
#define NAND_LARGE_BADBLOCK_POS 0
#endif /* __LINUX_MTD_NAND_H */ #endif /* __LINUX_MTD_NAND_H */
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
* *
* Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com)
* *
* $Id: nand_ecc.h,v 1.2 2003/02/20 13:34:20 sjhill Exp $ * $Id: nand_ecc.h,v 1.4 2004/06/17 02:35:02 dbrown 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
...@@ -12,17 +12,19 @@ ...@@ -12,17 +12,19 @@
* This file is the header for the ECC algorithm. * This file is the header for the ECC algorithm.
*/ */
/* #ifndef __MTD_NAND_ECC_H__
* Creates non-inverted ECC code from line parity #define __MTD_NAND_ECC_H__
*/
void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code); struct mtd_info;
/* /*
* Calculate 3 byte ECC code for 256 byte block * Calculate 3 byte ECC code for 256 byte block
*/ */
void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); int nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code);
/* /*
* Detect and correct a 1 bit error for 256 byte block * Detect and correct a 1 bit error for 256 byte block
*/ */
int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc);
#endif /* __MTD_NAND_ECC_H__ */
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment