RE: [PATCH] OneNAND MTD support

Kyungmin Park
2005-08-25T23:38:05Z

[-- Attachment #1: Type: text/plain, Size: 1749 bytes --]

I'm sorry to bother you

It's my mistake. I'm not delete block, and page parameters.
At first implementation I used the block and page address scheme instead of
virtual address.

Simply delete block and page parameters.

And here's one total patch including previous three patches plus some minor
patches.

If do you have any question. Please let me know

Thanks

Kyungmin Park

> -----Original Message-----
> From: Bernhard Priewasser [mailto:bernhard.priewasser@in2soft.de]
> Sent: Thursday, August 25, 2005 5:54 PM
> To: kyungmin.park@samsung.com; linux-mtd@lists.infradead.org
> Subject: Re: [PATCH] OneNAND MTD support
>
> Hi Kyungmin,
>
> when turning the "Verify OneNAND page write" on (make
> xconfig), OneNAND
> module does not compile:
>
> drivers/mtd/onenand/onenand_base.c: In function `onenand_write_ecc':
> drivers/mtd/onenand/onenand_base.c:819: error: `block'
> undeclared (first
> use in this function)
> drivers/mtd/onenand/onenand_base.c:819: error: (Each undeclared
> identifier is reported only once
> drivers/mtd/onenand/onenand_base.c:819: error: for each function it
> appears in.)
> drivers/mtd/onenand/onenand_base.c:819: error: `page'
> undeclared (first
> use in this function)
> drivers/mtd/onenand/onenand_base.c: In function `onenand_writev_ecc':
> drivers/mtd/onenand/onenand_base.c:1028: error: `block' undeclared
> (first use in this function)
> drivers/mtd/onenand/onenand_base.c:1028: error: `page'
> undeclared (first
> use in this function)
> make[3]: *** [drivers/mtd/onenand/onenand_base.o] Error 1
> make[2]: *** [drivers/mtd/onenand] Error 2
> make[1]: *** [drivers/mtd] Error 2
> make: *** [drivers] Error 2
>
> Where should onenand_write_ecc() get block and page from?
>
> Bernhard
>
>

[-- Attachment #2: onenand-050826.patch --]
[-- Type: application/octet-stream, Size: 33687 bytes --]

diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig
--- a/drivers/mtd/onenand/Kconfig
+++ b/drivers/mtd/onenand/Kconfig
@@ -29,4 +29,10 @@ config MTD_ONENAND_OMAP
help
Support for OneNAND flash on TI OMAP board.

+config MTD_ONENAND_SYNC_READ
+ bool "OneNAND Sync. Burst Read Support"
+ depends on ARCH_OMAP
+ help
+ This enables support for Sync. Burst Read.
+
endmenu
diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile
--- a/drivers/mtd/onenand/Makefile
+++ b/drivers/mtd/onenand/Makefile
@@ -3,7 +3,9 @@
#

# Core functionality.
-obj-$(CONFIG_MTD_ONENAND) += onenand_base.o
+obj-$(CONFIG_MTD_ONENAND) += onenand.o

# Board specific.
obj-$(CONFIG_MTD_ONENAND_OMAP) += omap-onenand.o
+
+onenand-objs = onenand_base.o onenand_bbt.o
diff --git a/drivers/mtd/onenand/omap-onenand.c b/drivers/mtd/onenand/omap-onenand.c
--- a/drivers/mtd/onenand/omap-onenand.c
+++ b/drivers/mtd/onenand/omap-onenand.c
@@ -4,174 +4,143 @@
* Copyright (c) 2005 Samsung Electronics
* Kyungmin Park <kyungmin.park@samsung.com>
*
- * Derived from linux/drivers/mtd/nand/omap-nand-flash.c
- *
* 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 OneNAND flash device for TI OMAP boards.
+ * This is a device driver for the OneNAND flash for OMAP boards.
*/

-#include <linux/slab.h>
-#include <linux/init.h>
+#include <linux/device.h>
#include <linux/module.h>
+#include <linux/init.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/onenand.h>
#include <linux/mtd/partitions.h>

#include <asm/io.h>
-#include <asm/arch/hardware.h>
-#include <asm/arch/tc.h>
-#include <asm/sizes.h>
-
-#define OMAP_ONENAND_FLASH_START1 OMAP_CS2A_PHYS
-#define OMAP_ONENAND_FLASH_START2 OMAP_CS0_PHYS
-/*
- * MTD structure for OMAP board
- */
-static struct mtd_info *omap_onenand_mtd = NULL;
+#include <asm/mach/flash.h>

-/*
- * Define partitions for flash devices
- */
+#define DRIVER_NAME "onenand"

-#ifdef CONFIG_MTD_PARTITIONS
-static struct mtd_partition static_partition[] = {
- {
- .name = "X-Loader + U-Boot",
- .offset = 0,
- .size = SZ_128K,
- .mask_flags = MTD_WRITEABLE /* force read-only */
- },
- {
- .name = "U-Boot Environment",
- .offset = MTDPART_OFS_APPEND,
- .size = SZ_128K,
- .mask_flags = MTD_WRITEABLE /* force read-only */
- },
- {
- .name = "kernel",
- .offset = MTDPART_OFS_APPEND,
- .size = 2 * SZ_1M
- },
- {
- .name = "filesystem0",
- .offset = MTDPART_OFS_APPEND,
- .size = SZ_16M,
- },
- {
- .name = "filesystem1",
- .offset = MTDPART_OFS_APPEND,
- .size = MTDPART_SIZ_FULL,
- },
-};
-
-const char *part_probes[] = { "cmdlinepart", NULL, };

+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL, };
#endif

-/* Scan to find existance of the device at base.
- This also allocates oob and data internal buffers */
-static char onenand_name[] = "onenand";
+struct omap_onenand_info {
+ struct mtd_info mtd;
+ struct mtd_partition *parts;
+ struct onenand_chip onenand;
+};

-/*
- * Main initialization routine
- */
-static int __init omap_onenand_init (void)
+static int __devinit omap_onenand_probe(struct device *dev)
{
- struct onenand_chip *this;
- struct mtd_partition *dynamic_partition = 0;
- int err = 0;
-
- /* Allocate memory for MTD device structure and private data */
- omap_onenand_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct onenand_chip),
- GFP_KERNEL);
- if (!omap_onenand_mtd) {
- printk (KERN_WARNING "Unable to allocate OneNAND MTD device structure.\n");
- err = -ENOMEM;
- goto out;
+ struct omap_onenand_info *info;
+ struct platform_device *pdev = to_platform_device(dev);
+ struct onenand_platform_data *pdata = pdev->dev.platform_data;
+ struct resource *res = pdev->resource;
+ unsigned long size = res->end - res->start + 1;
+ int err;
+
+ info = kmalloc(sizeof(struct omap_onenand_info), GFP_KERNEL);
+ if (!info)
+ return -ENOMEM;
+
+ memset(info, 0, sizeof(struct omap_onenand_info));
+
+ if (!request_mem_region(res->start, size, dev->driver->name)) {
+ err = -EBUSY;
+ goto out_free_info;
}

- /* Get pointer to private data */
- this = (struct onenand_chip *) (&omap_onenand_mtd[1]);
+ info->onenand.base = ioremap(res->start, size);
+ if (!info->onenand.base) {
+ err = -ENOMEM;
+ goto out_release_mem_region;
+ }

- /* Initialize structures */
- memset((char *) omap_onenand_mtd, 0, sizeof(struct mtd_info) + sizeof(struct onenand_chip));
+ info->onenand.mmcontrol = pdata->mmcontrol;

- /* Link the private data with the MTD structure */
- omap_onenand_mtd->priv = this;
+ info->mtd.name = pdev->dev.bus_id;
+ info->mtd.priv = &info->onenand;
+ info->mtd.owner = THIS_MODULE;

- /* try the first address */
- this->base = ioremap(OMAP_ONENAND_FLASH_START1, SZ_128K);
- omap_onenand_mtd->name = onenand_name;
- if (onenand_scan(omap_onenand_mtd, 1)){
- /* try the second address */
- iounmap(this->base);
- this->base = ioremap(OMAP_ONENAND_FLASH_START2, SZ_128K);
- if (onenand_scan(omap_onenand_mtd, 1)) {
- iounmap(this->base);
- err = -ENXIO;
- goto out_mtd;
- }
+ if (onenand_scan(&info->mtd, 1)) {
+ err = -ENXIO;
+ goto out_iounmap;
}

- /* Register the partitions */
- switch (omap_onenand_mtd->size) {
- case SZ_128M:
- case SZ_64M:
- case SZ_32M:
#ifdef CONFIG_MTD_PARTITIONS
- err = parse_mtd_partitions(omap_onenand_mtd, part_probes,
- &dynamic_partition, 0);
- if (err > 0)
- err = add_mtd_partitions(omap_onenand_mtd,
- dynamic_partition, err);
- else if (1)
- err = add_mtd_partitions(omap_onenand_mtd,
- static_partition,
- ARRAY_SIZE(static_partition));
- else
+ err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0);
+ if (err > 0)
+ add_mtd_partitions(&info->mtd, info->parts, err);
+ else if (err < 0 && pdata->parts)
+ add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts);
+ else
#endif
- err = add_mtd_device(omap_onenand_mtd);
- if (err)
- goto out_buf;
- break;
+ err = add_mtd_device(&info->mtd);

- default:
- printk(KERN_WARNING "Unsupported OneNAND device\n");
- err = -ENXIO;
- goto out_buf;
- }
+ dev_set_drvdata(&pdev->dev, info);

return 0;

-out_buf:
- onenand_release(omap_onenand_mtd);
- iounmap(this->base);
-out_mtd:
- kfree(omap_onenand_mtd);
-out:
+out_iounmap:
+ iounmap(info->onenand.base);
+out_release_mem_region:
+ release_mem_region(res->start, size);
+out_free_info:
+ kfree(info);
+
return err;
}

-/*
- * Clean up routine
- */
-static void __exit omap_onenand_cleanup (void)
+static int __devexit omap_onenand_remove(struct device *dev)
+{
+ struct platform_device *pdev = to_platform_device(dev);
+ struct omap_onenand_info *info = dev_get_drvdata(&pdev->dev);
+ struct resource *res = pdev->resource;
+ unsigned long size = res->end - res->start + 1;
+
+ dev_set_drvdata(&pdev->dev, NULL);
+
+ if (info) {
+ if (info->parts)
+ del_mtd_partitions(&info->mtd);
+ else
+ del_mtd_device(&info->mtd);
+
+ onenand_release(&info->mtd);
+ release_mem_region(res->start, size);
+ iounmap(info->onenand.base);
+ kfree(info);
+ }
+
+ return 0;
+}
+
+static struct device_driver omap_onenand_driver = {
+ .name = DRIVER_NAME,
+ .bus = &platform_bus_type,
+ .probe = omap_onenand_probe,
+ .remove = __devexit_p(omap_onenand_remove),
+};
+
+MODULE_ALIAS(DRIVER_NAME);
+
+static int __init omap_onenand_init(void)
{
- struct onenand_chip *this = omap_onenand_mtd->priv;
+ return driver_register(&omap_onenand_driver);
+}

- /* onenand_release frees MTD partitions, MTD structure
- and onenand internal buffers */
- onenand_release(omap_onenand_mtd);
- iounmap(this->base);
- kfree(omap_onenand_mtd);
+static void __exit omap_onenand_exit(void)
+{
+ driver_unregister(&omap_onenand_driver);
}

module_init(omap_onenand_init);
-module_exit(omap_onenand_cleanup);
+module_exit(omap_onenand_exit);

MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kyungmin Park <kyungmin.park@samsung.com>");
diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c
--- a/drivers/mtd/onenand/onenand_base.c
+++ b/drivers/mtd/onenand/onenand_base.c
@@ -311,19 +311,21 @@ static int onenand_wait(struct mtd_info
ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS);

if (ctrl & ONENAND_CTRL_ERROR) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x", ctrl);
- return -EIO;
+ /* It maybe occur at initial bad block */
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: controller error = 0x%04x\n", ctrl);
+ /* Clear other interrupt bits for preventing ECC error */
+ interrupt &= ONENAND_INT_MASTER;
}

if (ctrl & ONENAND_CTRL_LOCK) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x", ctrl);
- return -EIO;
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error = 0x%04x\n", ctrl);
+ return -EACCES;
}

if (interrupt & ONENAND_INT_READ) {
ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS);
if (ecc & ONENAND_ECC_2BIT_ALL) {
- DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x", ecc);
+ DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc);
return -EBADMSG;
}
}
@@ -379,6 +381,35 @@ static int onenand_read_bufferram(struct
}

/**
+ * onenand_sync_read_bufferram - [OneNAND Interface] Read the bufferram area with Sync. Burst mode
+ * @param mtd MTD data structure
+ * @param area BufferRAM area
+ * @param buffer the databuffer to put/get data
+ * @param offset offset to read from or write to
+ * @param count number of bytes to read/write
+ *
+ * Read the BufferRAM area with Sync. Burst Mode
+ */
+static int onenand_sync_read_bufferram(struct mtd_info *mtd, int area,
+ unsigned char *buffer, int offset, size_t count)
+{
+ struct onenand_chip *this = mtd->priv;
+ void __iomem *bufferram;
+
+ bufferram = this->base + area;
+
+ bufferram += onenand_bufferram_offset(mtd, area);
+
+ this->mmcontrol(mtd, ONENAND_SYS_CFG1_SYNC_READ);
+
+ memcpy(buffer, bufferram + offset, count);
+
+ this->mmcontrol(mtd, 0);
+
+ return 0;
+}
+
+/**
* onenand_write_bufferram - [OneNAND Interface] Write the bufferram area
* @param mtd MTD data structure
* @param area BufferRAM area
@@ -688,13 +719,11 @@ out:
* onenand_verify_page - [GENERIC] verify the chip contents after a write
* @param mtd MTD device structure
* @param buf the databuffer to verify
- * @param block block address
- * @param page page address
*
* Check DataRAM area directly
*/
static int onenand_verify_page(struct mtd_info *mtd, u_char *buf,
- loff_t addr, int block, int page)
+ loff_t addr)
{
struct onenand_chip *this = mtd->priv;
void __iomem *dataram0, *dataram1;
@@ -785,7 +814,7 @@ static int onenand_write_ecc(struct mtd_
written += thislen;

/* Only check verify write turn on */
- ret = onenand_verify_page(mtd, (u_char *) buf, to, block, page);
+ ret = onenand_verify_page(mtd, (u_char *) buf, to);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_ecc: verify failed %d\n", ret);
goto out;
@@ -909,7 +938,7 @@ static int onenand_writev_ecc(struct mtd
u_char *eccbuf, struct nand_oobinfo *oobsel)
{
struct onenand_chip *this = mtd->priv;
- unsigned char buffer[mtd->oobblock], *pbuf;
+ unsigned char buffer[MAX_ONENAND_PAGESIZE], *pbuf;
size_t total_len, len;
int i, written = 0;
int ret = 0;
@@ -994,7 +1023,7 @@ static int onenand_writev_ecc(struct mtd

/* Only check verify write turn on */
- ret = onenand_verify_page(mtd, (u_char *) pbuf, to, block, page);
+ ret = onenand_verify_page(mtd, (u_char *) pbuf, to);
if (ret) {
DEBUG(MTD_DEBUG_LEVEL0, "onenand_writev_ecc: verify failed %d\n", ret);
goto out;
@@ -1031,6 +1060,25 @@ static int onenand_writev(struct mtd_inf
}

/**
+ * onenand_block_checkbad - [GENERIC] Check if a block is marked bad
+ * @param mtd MTD device structure
+ * @param ofs offset from device start
+ * @param getchip 0, if the chip is already selected
+ * @param 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 onenand_block_checkbad(struct mtd_info *mtd, loff_t ofs, int getchip, int allowbbt)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+
+ /* Return info from the table */
+ return bbm->isbad_bbt(mtd, ofs, allowbbt);
+}
+
+/**
* onenand_erase - [MTD Interface] erase block(s)
* @param mtd MTD device structure
* @param instr erase instruction
@@ -1080,7 +1128,12 @@ static int onenand_erase(struct mtd_info

while (len) {

- /* TODO Check badblock */
+ /* Check if we have a bad block, we do not erase bad blocks */
+ if (onenand_block_checkbad(mtd, addr, 0, 0)) {
+ printk (KERN_WARNING "onenand_erase: attempt to erase a bad block at addr 0x%08x\n", (unsigned int) addr);
+ instr->state = MTD_ERASE_FAILED;
+ goto erase_exit;
+ }

this->command(mtd, ONENAND_CMD_ERASE, addr, block_size);

@@ -1132,34 +1185,70 @@ static void onenand_sync(struct mtd_info
onenand_release_device(mtd);
}

+
/**
* onenand_block_isbad - [MTD Interface] Check whether the block at the given offset is bad
* @param mtd MTD device structure
* @param ofs offset relative to mtd start
+ *
+ * Check whether the block is bad
*/
static int onenand_block_isbad(struct mtd_info *mtd, loff_t ofs)
{
- /*
- * TODO
- * 1. Bad block table (BBT)
- * -> using NAND BBT to support JFFS2
- * 2. Bad block management (BBM)
- * -> bad block replace scheme
- *
- * Currently we do nothing
- */
- return 0;
+ /* Check for invalid offset */
+ if (ofs > mtd->size)
+ return -EINVAL;
+
+ return onenand_block_checkbad(mtd, ofs, 1, 0);
+}
+
+/**
+ * onenand_default_block_markbad - [DEFAULT] mark a block bad
+ * @param mtd MTD device structure
+ * @param ofs offset from device start
+ *
+ * This is the default implementation, which can be overridden by
+ * a hardware specific driver.
+ */
+static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+ u_char buf[2] = {0, 0};
+ size_t retlen;
+ int block;
+
+ /* Get block number */
+ block = ((int) ofs) >> bbm->bbt_erase_shift;
+ if (bbm->bbt)
+ bbm->bbt[block >> 2] |= 0x01 << ((block & 0x03) << 1);
+
+ /* We write two bytes, so we dont have to mess with 16 bit access */
+ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01);
+ return mtd->write_oob(mtd, ofs , 2, &retlen, buf);
}

/**
* onenand_block_markbad - [MTD Interface] Mark the block at the given offset as bad
* @param mtd MTD device structure
* @param ofs offset relative to mtd start
+ *
+ * Mark the block as bad
*/
static int onenand_block_markbad(struct mtd_info *mtd, loff_t ofs)
{
- /* see above */
- return 0;
+ struct onenand_chip *this = mtd->priv;
+ int ret;
+
+ ret = onenand_block_isbad(mtd, ofs);
+ if (ret) {
+ /* If it was bad already, return success and do nothing */
+ if (ret > 0)
+ return 0;
+ return ret;
+ }
+
+ return this->block_markbad(mtd, ofs);
}

/**
@@ -1273,8 +1362,8 @@ static int onenand_check_maf(int manuf)
break;
}

- printk(KERN_DEBUG "OneNAND Manufacturer: %s\n",
- onenand_manuf_ids[i].name);
+ printk(KERN_DEBUG "OneNAND Manufacturer: %s (0x%0x)\n",
+ onenand_manuf_ids[i].name, manuf);

return (i != ONENAND_MFR_UNKNOWN);
}
@@ -1382,9 +1471,20 @@ int onenand_scan(struct mtd_info *mtd, i
if (!this->write_bufferram)
this->write_bufferram = onenand_write_bufferram;

+ if (!this->block_markbad)
+ this->block_markbad = onenand_default_block_markbad;
+ if (!this->scan_bbt)
+ this->scan_bbt = onenand_default_bbt;
+
if (onenand_probe(mtd))
return -ENXIO;

+ /* Set Sync. Burst Read after probing */
+ if (this->mmcontrol) {
+ printk(KERN_INFO "OneNAND Sync. Burst Read support\n");
+ this->read_bufferram = onenand_sync_read_bufferram;
+ }
+
this->state = FL_READY;
init_waitqueue_head(&this->wq);
spin_lock_init(&this->chip_lock);
@@ -1437,7 +1537,7 @@ int onenand_scan(struct mtd_info *mtd, i
/* Unlock whole block */
mtd->unlock(mtd, 0x0, this->chipsize);

- return 0;
+ return this->scan_bbt(mtd);
}

/**
diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c
new file mode 100644
--- /dev/null
+++ b/drivers/mtd/onenand/onenand_bbt.c
@@ -0,0 +1,246 @@
+/*
+ * linux/drivers/mtd/onenand/onenand_bbt.c
+ *
+ * Bad Block Table support for the OneNAND driver
+ *
+ * Copyright(c) 2005 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Derived from nand_bbt.c
+ *
+ * TODO:
+ * Split BBT core and chip specific BBT.
+ */
+
+#include <linux/slab.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/onenand.h>
+#include <linux/mtd/compatmac.h>
+
+/**
+ * check_short_pattern - [GENERIC] check if a pattern is in the buffer
+ * @param buf the buffer to search
+ * @param len the length of buffer to search
+ * @param paglen the pagelength
+ * @param td search pattern descriptor
+ *
+ * Check for a pattern at the given place. Used to search bad block
+ * tables and good / bad block identifiers. Same as check_pattern, but
+ * no optional empty check and the pattern is expected to start
+ * at offset 0.
+ *
+ */
+static int check_short_pattern(uint8_t *buf, int len, int paglen, struct nand_bbt_descr *td)
+{
+ int i;
+ uint8_t *p = buf;
+
+ /* Compare the pattern */
+ for (i = 0; i < td->len; i++) {
+ if (p[i] != td->pattern[i])
+ return -1;
+ }
+ return 0;
+}
+
+/**
+ * create_bbt - [GENERIC] Create a bad block table by scanning the device
+ * @param mtd MTD device structure
+ * @param buf temporary buffer
+ * @param bd descriptor for the good/bad block search pattern
+ * @param 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 int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr *bd, int chip)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+ int i, j, numblocks, len, scanlen;
+ int startblock;
+ loff_t from;
+ size_t readlen, ooblen;
+
+ printk(KERN_INFO "Scanning device for bad blocks\n");
+
+ len = 1;
+
+ /* We need only read few bytes from the OOB area */
+ scanlen = ooblen = 0;
+ readlen = bd->len;
+
+ /* chip == -1 case only */
+ /* Note that numblocks is 2 * (real numblocks) here;
+ * see i += 2 below as it makses shifting and masking less painful
+ */
+ numblocks = mtd->size >> (bbm->bbt_erase_shift - 1);
+ startblock = 0;
+ from = 0;
+
+ for (i = startblock; i < numblocks; ) {
+ int ret;
+
+ for (j = 0; j < len; j++) {
+ size_t retlen;
+
+ /* No need to read pages fully,
+ * just read required OOB bytes */
+ ret = mtd->read_oob(mtd, from + j * mtd->oobblock + bd->offs,
+ readlen, &retlen, &buf[0]);
+
+ if (ret)
+ return ret;
+
+ if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->oobblock, bd)) {
+ bbm->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 << bbm->bbt_erase_shift);
+ }
+
+ return 0;
+}
+
+
+/**
+ * onenand_memory_bbt - [GENERIC] create a memory based bad block table
+ * @param mtd MTD device structure
+ * @param 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 inline int onenand_memory_bbt (struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ unsigned char data_buf[MAX_ONENAND_PAGESIZE];
+
+ bd->options &= ~NAND_BBT_SCANEMPTY;
+ return create_bbt(mtd, data_buf, bd, -1);
+}
+
+/**
+ * onenand_isbad_bbt - [OneNAND Interface] Check if a block is bad
+ * @param mtd MTD device structure
+ * @param offs offset in the device
+ * @param allowbbt allow access to bad block table region
+ */
+static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+ int block;
+ uint8_t res;
+
+ /* Get block number * 2 */
+ block = (int) (offs >> (bbm->bbt_erase_shift - 1));
+ res = (bbm->bbt[block >> 3] >> (block & 0x06)) & 0x03;
+
+ DEBUG(MTD_DEBUG_LEVEL2, "onenand_isbad_bbt: bbt info for offs 0x%08x: (block %d) 0x%02x\n",
+ (unsigned int) offs, block >> 1, res);
+
+ switch ((int) res) {
+ case 0x00: return 0;
+ case 0x01: return 1;
+ case 0x02: return allowbbt ? 0 : 1;
+ }
+
+ return 1;
+}
+
+/**
+ * onenand_scan_bbt - [OneNAND Interface] scan, find, read and maybe create bad block table(s)
+ * @param mtd MTD device structure
+ * @param 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 onenand_free_bbt function.
+ *
+ */
+int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm = this->bbm;
+ int len, ret = 0;
+
+ len = mtd->size >> (this->erase_shift + 2);
+ /* Allocate memory (2bit per block) */
+ bbm->bbt = kmalloc(len, GFP_KERNEL);
+ if (!bbm->bbt) {
+ printk(KERN_ERR "onenand_scan_bbt: Out of memory\n");
+ return -ENOMEM;
+ }
+ /* Clear the memory bad block table */
+ memset(bbm->bbt, 0x00, len);
+
+ /* Set the bad block position */
+ bbm->badblockpos = ONENAND_BADBLOCK_POS;
+
+ /* Set erase shift */
+ bbm->bbt_erase_shift = this->erase_shift;
+
+ if (!bbm->isbad_bbt)
+ bbm->isbad_bbt = onenand_isbad_bbt;
+
+ /* Scan the device to build a memory based bad block table */
+ if ((ret = onenand_memory_bbt(mtd, bd))) {
+ printk(KERN_ERR "onenand_scan_bbt: Can't scan flash and build the RAM-based BBT\n");
+ kfree(bbm->bbt);
+ bbm->bbt = NULL;
+ }
+
+ return ret;
+}
+
+/*
+ * Define some generic bad / good block scan pattern which are used
+ * while scanning a device for factory marked good / bad blocks.
+ */
+static uint8_t scan_ff_pattern[] = { 0xff, 0xff };
+
+static struct nand_bbt_descr largepage_memorybased = {
+ .options = 0,
+ .offs = 0,
+ .len = 2,
+ .pattern = scan_ff_pattern,
+};
+
+/**
+ * onenand_default_bbt - [OneNAND Interface] Select a default bad block table for the device
+ * @param mtd MTD device structure
+ *
+ * This function selects the default bad block table
+ * support for the device and calls the onenand_scan_bbt function
+ */
+int onenand_default_bbt(struct mtd_info *mtd)
+{
+ struct onenand_chip *this = mtd->priv;
+ struct bbm_info *bbm;
+
+ this->bbm = kmalloc(sizeof(struct bbm_info), GFP_KERNEL);
+ if (!this->bbm)
+ return -ENOMEM;
+
+ bbm = this->bbm;
+
+ memset(bbm, 0, sizeof(struct bbm_info));
+
+ /* 1KB page has same configuration as 2KB page */
+ if (!bbm->badblock_pattern)
+ bbm->badblock_pattern = &largepage_memorybased;
+
+ return onenand_scan_bbt(mtd, bbm->badblock_pattern);
+}
+
+EXPORT_SYMBOL(onenand_scan_bbt);
+EXPORT_SYMBOL(onenand_default_bbt);
diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h
new file mode 100644
--- /dev/null
+++ b/include/linux/mtd/bbm.h
@@ -0,0 +1,122 @@
+/*
+ * linux/include/linux/mtd/bbm.h
+ *
+ * NAND family Bad Block Management (BBM) header file
+ * - Bad Block Table (BBT) implementation
+ *
+ * Copyright (c) 2005 Samsung Electronics
+ * Kyungmin Park <kyungmin.park@samsung.com>
+ *
+ * Copyright (c) 2000-2005
+ * Thomas Gleixner <tglx@linuxtronix.de>
+ *
+ */
+#ifndef __LINUX_MTD_BBM_H
+#define __LINUX_MTD_BBM_H
+
+/* The maximum number of NAND chips in an array */
+#define NAND_MAX_CHIPS 8
+
+/**
+ * struct nand_bbt_descr - bad block table descriptor
+ * @param options options for this descriptor
+ * @param 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
+ * @param offs offset of the pattern in the oob area of the page
+ * @param veroffs offset of the bbt version counter in the oob are of the page
+ * @param version version read from the bbt page during scan
+ * @param len length of the pattern, if 0 no pattern check is performed
+ * @param 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.
+ * @param reserved_block_code if non-0, this pattern denotes a reserved
+ * (rather than bad) block in the stored bbt
+ * @param 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
+
+/*
+ * Constants for oob configuration
+ */
+#define ONENAND_BADBLOCK_POS 0
+
+/**
+ * struct bbt_info - [GENERIC] Bad Block Table data structure
+ * @param bbt_erase_shift [INTERN] number of address bits in a bbt entry
+ * @param badblockpos [INTERN] position of the bad block marker in the oob area
+ * @param bbt [INTERN] bad block table pointer
+ * @param badblock_pattern [REPLACEABLE] bad block scan pattern used for initial bad block scan
+ * @param priv [OPTIONAL] pointer to private bbm date
+ */
+struct bbm_info {
+ int bbt_erase_shift;
+ int badblockpos;
+ int options;
+
+ uint8_t *bbt;
+
+ int (*isbad_bbt)(struct mtd_info *mtd, loff_t ofs, int allowbbt);
+
+ /* TODO Add more NAND specific fileds */
+ struct nand_bbt_descr *badblock_pattern;
+
+ void *priv;
+};
+
+/* OneNAND BBT interface */
+extern int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd);
+extern int onenand_default_bbt(struct mtd_info *mtd);
+
+#endif /* __LINUX_MTD_BBM_H */
diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h
--- a/include/linux/mtd/onenand.h
+++ b/include/linux/mtd/onenand.h
@@ -14,8 +14,10 @@

#include <linux/spinlock.h>
#include <linux/mtd/onenand_regs.h>
+#include <linux/mtd/bbm.h>

#define MAX_BUFFERRAM 2
+#define MAX_ONENAND_PAGESIZE (2048 + 64)

/* Scan and identify a OneNAND device */
extern int onenand_scan(struct mtd_info *mtd, int max_chips);
@@ -67,10 +69,14 @@ struct onenand_bufferram {
* @param wait [REPLACEABLE] hardware specific function for wait on ready
* @param read_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area
* @param write_bufferram [REPLACEABLE] hardware specific function for BufferRAM Area
+ * @param read_word [REPLACEABLE] hardware specific function for read register of OneNAND
+ * @param write_word [REPLACEABLE] hardware specific function for write register of OneNAND
+ * @param scan_bbt [REPLACEALBE] hardware specific function for scaning Bad block Table
* @param chip_lock [INTERN] spinlock used to protect access to this structure and the chip
* @param wq [INTERN] wait queue to sleep on if a OneNAND operation is in progress
* @param state [INTERN] the current state of the OneNAND device
* @param autooob [REPLACEABLE] the default (auto)placement scheme
+ * @param bbm [REPLACEABLE] pointer to Bad Block Management
* @param priv [OPTIONAL] pointer to private chip date
*/
struct onenand_chip {
@@ -95,6 +101,9 @@ struct onenand_chip {
const unsigned char *buffer, int offset, size_t count);
unsigned short (*read_word)(void __iomem *addr);
void (*write_word)(unsigned short value, void __iomem *addr);
+ void (*mmcontrol)(struct mtd_info *mtd, int sync_read);
+ int (*block_markbad)(struct mtd_info *mtd, loff_t ofs);
+ int (*scan_bbt)(struct mtd_info *mtd);

spinlock_t chip_lock;
wait_queue_head_t wq;
@@ -102,13 +111,23 @@ struct onenand_chip {

struct nand_oobinfo *autooob;

+ void *bbm;
+
void *priv;
};

+/*
+ * Helper macros
+ */
#define ONENAND_CURRENT_BUFFERRAM(this) (this->bufferram_index)
#define ONENAND_NEXT_BUFFERRAM(this) (this->bufferram_index ^ 1)
#define ONENAND_SET_NEXT_BUFFERRAM(this) (this->bufferram_index ^= 1)

+#define ONENAND_GET_SYS_CFG1(this) \
+ (this->read_word(this->base + ONENAND_REG_SYS_CFG1))
+#define ONENAND_SET_SYS_CFG1(v, this) \
+ (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1))
+
/*
* Options bits
*/
diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h
--- a/include/linux/mtd/onenand_regs.h
+++ b/include/linux/mtd/onenand_regs.h
@@ -121,8 +121,21 @@
* System Configuration 1 Register F221h (R, R/W)
*/
#define ONENAND_SYS_CFG1_SYNC_READ (1 << 15)
-#define ONENAND_SYS_CFG1_BRL (1 << 12)
-#define ONENAND_SYS_CFG1_BL (1 << 9)
+#define ONENAND_SYS_CFG1_BRL_7 (7 << 12)
+#define ONENAND_SYS_CFG1_BRL_6 (6 << 12)
+#define ONENAND_SYS_CFG1_BRL_5 (5 << 12)
+#define ONENAND_SYS_CFG1_BRL_4 (4 << 12)
+#define ONENAND_SYS_CFG1_BRL_3 (3 << 12)
+#define ONENAND_SYS_CFG1_BRL_10 (2 << 12)
+#define ONENAND_SYS_CFG1_BRL_9 (1 << 12)
+#define ONENAND_SYS_CFG1_BRL_8 (0 << 12)
+#define ONENAND_SYS_CFG1_BRL_SHIFT (12)
+#define ONENAND_SYS_CFG1_BL_32 (4 << 9)
+#define ONENAND_SYS_CFG1_BL_16 (3 << 9)
+#define ONENAND_SYS_CFG1_BL_8 (2 << 9)
+#define ONENAND_SYS_CFG1_BL_4 (1 << 9)
+#define ONENAND_SYS_CFG1_BL_CONT (0 << 9)
+#define ONENAND_SYS_CFG1_BL_SHIFT (9)
#define ONENAND_SYS_CFG1_NO_ECC (1 << 8)
#define ONENAND_SYS_CFG1_RDY (1 << 7)
#define ONENAND_SYS_CFG1_INT (1 << 6)
diff --git a/include/asm-arm/mach/flash.h b/include/asm-arm/mach/flash.h
--- a/include/asm-arm/mach/flash.h
+++ b/include/asm-arm/mach/flash.h
@@ -11,6 +11,7 @@
#define ASMARM_MACH_FLASH_H

struct mtd_partition;
+struct mtd_info;

/*
* map_name: the map probe function name
@@ -31,4 +32,16 @@ struct flash_platform_data {
unsigned int nr_parts;
};

+/**
+ * struct onenand_platform_data - platform data describing OneNAND flash banks
+ * @param mmcontrol Handle memroy control for Sync. Burst Read
+ * @param parts optional array of mtd_partitions for static partioning
+ * @param nr_parts number of mtd_partitions for static partitioning
+ */
+struct onenand_platform_data {
+ void (*mmcontrol)(struct mtd_info *mtd, int sync_read);
+ struct mtd_partition *parts;
+ unsigned int nr_parts;
+};
+
#endif