Thomas Gleixner wrote:
> Hmm, can you verify that the chip is erased correctly, when you do an
> flash_eraseall ? Dump it and check for nonzero pages.
I used:
# flash_eraseall /dev/mtd/4
# nanddump /dev/mtd/4 dump.out
No, not all spages are erased after doing a flash_eraseall. i.e. they were not
all 0xff.
The very first 8MB Flash partition that I started using for development is
missing about 60% of its space due to "bad eraseblocks". The second 8MB Flash
partition is missing only 1%. Luckily I make my own Smartmedia cards, so I can
simply solder in a new Flash chip for further development. From now on I think
I will make my partitions much smaller so I don't "lose" as many. ;)
y2 # df
Filesystem 1k-blocks Used Available Use% Mounted on
/dev/root 102901816 77308708 20365960 79% /
tmpfs 15428 152 15276 1% /var
/dev/mtdblock/4 16368 9904 6464 61% /mnt/y1
/dev/mtdblock/5 16368 176 16192 1% /mnt/y2
y2 # ll /mnt/y1
yaffs_readdir: starting at 0
yaffs_readdir: starting at 3
drw-rw-rw- 1 root root 512 Dec 6 11:12 ./
drwxrwxr-x 9 root 100 4096 Nov 11 10:17 ../
drw-rw-rw- 1 root root 512 Dec 6 11:12 lost+found/
y2 # ll /mnt/y2
yaffs_readdir: starting at 0
yaffs_readdir: starting at 3
drw-rw-rw- 1 root root 512 Dec 6 11:12 ./
drwxrwxr-x 9 root 100 4096 Nov 11 10:17 ../
drw-rw-rw- 1 root root 512 Dec 6 11:12 lost+found/
>>Surely it's not the easy to "break" a NAND chip? Can my original NAND be
recovered?
>>
> It's quite easy, if the timing is wrong or if the rdy/busy check is not
> reliable.
Is there a utility that writes to every single page, reads back the data and
then marks the pages correctly? Or are those "bad eraseblocks" permanently lost?
> Can I have a look at your board driver ?
Sure, see attached.
I simply modified the existing at91_nand.c file to compile with the latest MTD
drivers. see
http://lists.infradead.org/pipermail/linux-mtd/2004-December/011039.html for
details on the changes I made to at91_nand.c
regards,
Aras Vaichas
/*
* drivers/at91/mtd/at91_nand.c
*
* Copyright (c) 2003 Rick Bronson
*
* Derived from drivers/mtd/nand/autcpu12.c
* Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de)
*
* Derived from drivers/mtd/spia.c
* Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com)
*
* 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/module.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 <linux/init.h>
#include <asm/arch/pio.h>
#include "at91_nand.h"
/*
* MTD structure for AT91 board
*/
static struct mtd_info *at91_mtd = NULL;
static struct nand_chip *my_nand_chip = NULL;
static int at91_fio_base;
#ifdef CONFIG_MTD_PARTITIONS
/*
* Define partitions for flash devices
*/
static struct mtd_partition partition_info32k[] = {
{ name: "NAND partition 1",
offset: 0,
size: 16 * SZ_1M },
{ name: "NAND partition 2",
offset: 16 * SZ_1M,
size: 16 * SZ_1M },
// { name: "AT91 NAND partition 1, kernel",
// offset: 0,
// size: 1 * SZ_1M },
// { name: "AT91 NAND partition 2, filesystem",
// offset: 1 * SZ_1M,
// size: 16 * SZ_1M },
// { name: "AT91 NAND partition 3a, storage",
// offset: (1 * SZ_1M) + (16 * SZ_1M),
// size: 1 * SZ_1M },
// { name: "AT91 NAND partition 3b, storage",
// offset: (2 * SZ_1M) + (16 * SZ_1M),
// size: 1 * SZ_1M },
// { name: "AT91 NAND partition 3c, storage",
// offset: (3 * SZ_1M) + (16 * SZ_1M),
// size: 1 * SZ_1M },
// { name: "AT91 NAND partition 3d, storage",
// offset: (4 * SZ_1M) + (16 * SZ_1M),
// size: 1 * SZ_1M },
};
static struct mtd_partition partition_info64k[] = {
{ name: "AT91 NAND partition 1, kernel",
offset: 0,
size: 1 * SZ_1M },
{ name: "AT91 NAND partition 2, filesystem",
offset: 1 * SZ_1M,
size: 16 * SZ_1M },
{ name: "AT91 NAND partition 3, storage",
offset: (1 * SZ_1M) + (16 * SZ_1M),
size: 47 * SZ_1M },
};
#endif
/*
* Hardware specific access to control-lines
*/
// ARASV
static void at91_hwcontrol(struct mtd_info *mtd, int cmd)
{
struct nand_chip *my_nand = my_nand_chip;
switch(cmd)
{
case NAND_CTL_SETCLE:
my_nand->IO_ADDR_W = at91_fio_base + AT91_SMART_MEDIA_CLE;
break;
case NAND_CTL_CLRCLE:
my_nand->IO_ADDR_W = at91_fio_base;
break;
case NAND_CTL_SETALE:
my_nand->IO_ADDR_W = at91_fio_base + AT91_SMART_MEDIA_ALE;
break;
case NAND_CTL_CLRALE:
my_nand->IO_ADDR_W = at91_fio_base;
break;
case NAND_CTL_SETNCE:
break;
case NAND_CTL_CLRNCE:
break;
}
}
/*
* Send command to NAND device
*/
static void at91_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr)
{
register struct nand_chip *my_nand = mtd->priv;
/* Begin command latch cycle */
register unsigned long NAND_IO_ADDR = my_nand->IO_ADDR_W + AT91_SMART_MEDIA_CLE;
/*
* 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_RESET, NAND_IO_ADDR);
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 */
NAND_IO_ADDR = at91_fio_base;
if (column != -1 || page_addr != -1)
NAND_IO_ADDR += AT91_SMART_MEDIA_ALE;
/* 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);
}
}
/* wait until command is processed */
while (!my_nand->dev_ready(at91_mtd))
;
}
/*
* Read the Device Ready pin.
*/
static int at91_device_ready(struct mtd_info *mtd)
{
return AT91_PIO_SmartMedia_RDY();
}
/*
* Main initialization routine
*/
static int __init at91_init (void)
{
struct nand_chip *my_nand;
int err = 0;
printk( "at91_init enterred\n" );
/* Allocate memory for MTD device structure and private data */
at91_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), GFP_KERNEL);
if (!at91_mtd) {
printk ("Unable to allocate AT91 NAND MTD device structure.\n");
err = -ENOMEM;
goto out;
}
/* map physical adress */
at91_fio_base = (unsigned long) ioremap(AT91_SMARTMEDIA_BASE, SZ_8M);
if(!at91_fio_base) {
printk("ioremap AT91 NAND failed\n");
err = -EIO;
goto out_mtd;
}
/* Get pointer to private data */
my_nand_chip = my_nand = (struct nand_chip *) (&at91_mtd[1]);
/* Initialize structures */
memset((char *) at91_mtd, 0, sizeof(struct mtd_info));
memset((char *) my_nand, 0, sizeof(struct nand_chip));
/* Link the private data with the MTD structure */
at91_mtd->priv = my_nand;
/* Set address of NAND IO lines */
my_nand->IO_ADDR_R = at91_fio_base;
my_nand->IO_ADDR_W = at91_fio_base;
my_nand->hwcontrol = at91_hwcontrol;
my_nand->dev_ready = at91_device_ready;
my_nand->cmdfunc = at91_nand_command; /* we need our own */
my_nand->eccmode = NAND_ECC_SOFT; /* enable ECC */
/* 20 us command delay time */
my_nand->chip_delay = 20;
/* Setup Smart Media, first enable the address range of CS3 */
AT91_SYS->EBI_CSA |= AT91C_EBI_CS3A_SMC_SmartMedia;
/* set the bus interface characteristics based on
tDS Data Set up Time 30 - ns
tDH Data Hold Time 20 - ns
tALS ALE Set up Time 20 - ns
16ns at 60 MHz ~= 3 */
#define AT91C_SM_ID_RWH (5 << 28) /* orig = 5 */
#define AT91C_SM_RWH (1 << 28) /* orig = 1 */
#define AT91C_SM_RWS (0 << 24) /* orig = 0 */
#define AT91C_SM_TDF (1 << 8) /* orig = 1 */
#define AT91C_SM_NWS (5) /* orig = 3 */
AT91_SYS->EBI_SMC2_CSR[3] = ( AT91C_SM_RWH | AT91C_SM_RWS |
AT91C_SMC2_ACSS_STANDARD |
AT91C_SMC2_DBW_8 | AT91C_SM_TDF |
AT91C_SMC2_WSEN | AT91C_SM_NWS);
AT91_CfgPIO_SmartMedia();
if (AT91_PIO_SmartMedia_CardDetect())
printk ("No ");
printk ("SmartMedia card inserted.\n");
/* Scan to find existance of the device */
// ARASV
#define CHIPS_TO_SCAN 1
if (nand_scan (at91_mtd, CHIPS_TO_SCAN)) {
err = -ENXIO;
goto out_ior;
}
/* Allocate memory for internal data buffer */
my_nand->data_buf = kmalloc (sizeof(u_char) * (at91_mtd->oobblock + at91_mtd->oobsize), GFP_KERNEL);
if (!my_nand->data_buf) {
printk ("Unable to allocate AT91 NAND data buffer.\n");
err = -ENOMEM;
goto out_ior;
}
/* Allocate memory for internal data buffer */
// ARASV
// my_nand->data_cache = kmalloc (sizeof(u_char) * (at91_mtd->oobblock + at91_mtd->oobsize), GFP_KERNEL);
// if (!my_nand->data_cache) {
// printk ("Unable to allocate AT91 NAND data cache.\n");
// err = -ENOMEM;
// goto out_buf;
// }
// my_nand->cache_page = -1;
#ifdef CONFIG_MTD_PARTITIONS
/* Register the partitions */
switch(at91_mtd->size)
{
case SZ_32M:
err = add_mtd_partitions(at91_mtd, partition_info32k,
ARRAY_SIZE (partition_info32k));
break;
case SZ_64M:
err = add_mtd_partitions(at91_mtd, partition_info64k,
ARRAY_SIZE (partition_info64k));
break;
default:
printk ("Unsupported SmartMedia device\n");
err = -ENXIO;
goto out_cac;
}
#else
err = add_mtd_device(at91_mtd);
#endif
goto out;
out_cac:
// ARASV
// kfree (my_nand->data_cache);
out_buf:
kfree (my_nand->data_buf);
out_ior:
iounmap((void *)at91_fio_base);
out_mtd:
kfree (at91_mtd);
out:
return err;
}
/*
* Clean up routine
*/
static void __exit at91_cleanup (void)
{
struct nand_chip *my_nand = (struct nand_chip *) &at91_mtd[1];
/* Unregister partitions */
del_mtd_partitions(at91_mtd);
/* Unregister the device */
del_mtd_device (at91_mtd);
/* Free internal data buffers */
kfree (my_nand->data_buf);
// ARASV
// kfree (my_nand->data_cache);
/* unmap physical adress */
iounmap((void *)at91_fio_base);
/* Free the MTD device structure */
kfree (at91_mtd);
}
module_init(at91_init);
module_exit(at91_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Rick Bronson");
MODULE_DESCRIPTION("Glue layer for SmartMediaCard on ATMEL AT91RM9200");