diff options
37 files changed, 1048 insertions, 468 deletions
diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index f69184a..f334959 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -397,9 +397,23 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) cfi_fixup(mtd, fixup_table); for (i=0; i< cfi->numchips; i++) { - cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp; - cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp; - cfi->chips[i].erase_time = 1000<<cfi->cfiq->BlockEraseTimeoutTyp; + if (cfi->cfiq->WordWriteTimeoutTyp) + cfi->chips[i].word_write_time = + 1<<cfi->cfiq->WordWriteTimeoutTyp; + else + cfi->chips[i].word_write_time = 50000; + + if (cfi->cfiq->BufWriteTimeoutTyp) + cfi->chips[i].buffer_write_time = + 1<<cfi->cfiq->BufWriteTimeoutTyp; + /* No default; if it isn't specified, we won't use it */ + + if (cfi->cfiq->BlockEraseTimeoutTyp) + cfi->chips[i].erase_time = + 1000<<cfi->cfiq->BlockEraseTimeoutTyp; + else + cfi->chips[i].erase_time = 2000000; + cfi->chips[i].ref_point_counter = 0; init_waitqueue_head(&(cfi->chips[i].wq)); } @@ -546,13 +560,11 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, struct cfi_intelext_programming_regioninfo *prinfo; prinfo = (struct cfi_intelext_programming_regioninfo *)&extp->extra[offs]; mtd->writesize = cfi->interleave << prinfo->ProgRegShift; - MTD_PROGREGION_CTRLMODE_VALID(mtd) = cfi->interleave * prinfo->ControlValid; - MTD_PROGREGION_CTRLMODE_INVALID(mtd) = cfi->interleave * prinfo->ControlInvalid; mtd->flags &= ~MTD_BIT_WRITEABLE; printk(KERN_DEBUG "%s: program region size/ctrl_valid/ctrl_inval = %d/%d/%d\n", map->name, mtd->writesize, - MTD_PROGREGION_CTRLMODE_VALID(mtd), - MTD_PROGREGION_CTRLMODE_INVALID(mtd)); + cfi->interleave * prinfo->ControlValid, + cfi->interleave * prinfo->ControlInvalid); } /* diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index d56849f..69d49e0 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -662,7 +662,7 @@ static int cfi_staa_write_buffers (struct mtd_info *mtd, loff_t to, * a small buffer for this. * XXX: If the buffer size is not a multiple of 2, this will break */ -#define ECCBUF_SIZE (mtd->eccsize) +#define ECCBUF_SIZE (mtd->writesize) #define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) #define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) static int diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 603a795..8a0c4de 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -569,7 +569,6 @@ void DoC2k_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; mtd->erasesize = 0; mtd->writesize = 512; diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index fe71a12..6f368ae 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -348,7 +348,6 @@ void DoCMil_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; /* FIXME: erase size is not always 8KiB */ diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c index ba4db68..88ba82d 100644 --- a/drivers/mtd/devices/doc2001plus.c +++ b/drivers/mtd/devices/doc2001plus.c @@ -472,7 +472,6 @@ void DoCMilPlus_init(struct mtd_info *mtd) mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_RS_DiskOnChip; mtd->size = 0; mtd->erasesize = 0; diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index f457315..bbf0553 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -204,7 +204,7 @@ config MTD_ESB2ROM config MTD_CK804XROM tristate "BIOS flash chip on Nvidia CK804" - depends on X86 && MTD_JEDECPROBE + depends on X86 && MTD_JEDECPROBE && PCI help Support for treating the BIOS flash chip on nvidia motherboards as an MTD device - with this you can reprogram your BIOS. diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c index 78b6711..728aed6 100644 --- a/drivers/mtd/maps/amd76xrom.c +++ b/drivers/mtd/maps/amd76xrom.c @@ -205,8 +205,8 @@ static int __devinit amd76xrom_init_one (struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* There is no generic VPP support */ for(map->map.bankwidth = 32; map->map.bankwidth; diff --git a/drivers/mtd/maps/ck804xrom.c b/drivers/mtd/maps/ck804xrom.c index 238d42e..3d4a4d8 100644 --- a/drivers/mtd/maps/ck804xrom.c +++ b/drivers/mtd/maps/ck804xrom.c @@ -207,8 +207,8 @@ static int __devinit ck804xrom_init_one (struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* There is no generic VPP support */ for(map->map.bankwidth = 32; map->map.bankwidth; @@ -327,7 +327,7 @@ static int __init init_ck804xrom(void) pdev = NULL; for(id = ck804xrom_pci_tbl; id->vendor; id++) { - pdev = pci_find_device(id->vendor, id->device, NULL); + pdev = pci_get_device(id->vendor, id->device, NULL); if (pdev) break; } diff --git a/drivers/mtd/maps/esb2rom.c b/drivers/mtd/maps/esb2rom.c index a9d808a..0bc013f 100644 --- a/drivers/mtd/maps/esb2rom.c +++ b/drivers/mtd/maps/esb2rom.c @@ -289,8 +289,8 @@ static int __devinit esb2rom_init_one(struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* Firmware hubs only use vpp when being programmed * in a factory setting. So in-place programming diff --git a/drivers/mtd/maps/ichxrom.c b/drivers/mtd/maps/ichxrom.c index 2bb3e63..2c884c4 100644 --- a/drivers/mtd/maps/ichxrom.c +++ b/drivers/mtd/maps/ichxrom.c @@ -227,8 +227,8 @@ static int __devinit ichxrom_init_one (struct pci_dev *pdev, (((unsigned long)(window->virt)) + offset); map->map.size = 0xffffffffUL - map_top + 1UL; /* Set the name of the map to the address I am trying */ - sprintf(map->map_name, "%s @%08lx", - MOD_NAME, map->map.phys); + sprintf(map->map_name, "%s @%08Lx", + MOD_NAME, (unsigned long long)map->map.phys); /* Firmware hubs only use vpp when being programmed * in a factory setting. So in-place programming diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index ed21547..95dcab2 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -94,7 +94,9 @@ static struct mtd_info *mymtd; static int __init init_netsc520(void) { - printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys); + printk(KERN_NOTICE "NetSc520 flash device: 0x%Lx at 0x%Lx\n", + (unsigned long long)netsc520_map.size, + (unsigned long long)netsc520_map.phys); netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size); if (!netsc520_map.virt) { diff --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index 9b50cfc..4045e37 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -237,8 +237,9 @@ static int __init init_sc520cdp(void) #endif for (i = 0; i < NUM_FLASH_BANKS; i++) { - printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", - sc520cdp_map[i].size, sc520cdp_map[i].phys); + printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n", + (unsigned long long)sc520cdp_map[i].size, + (unsigned long long)sc520cdp_map[i].phys); sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 61a994e..1592eac 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -419,8 +419,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file, info.erasesize = mtd->erasesize; info.writesize = mtd->writesize; info.oobsize = mtd->oobsize; - info.ecctype = mtd->ecctype; - info.eccsize = mtd->eccsize; + /* The below fields are obsolete */ + info.ecctype = -1; + info.eccsize = 0; if (copy_to_user(argp, &info, sizeof(struct mtd_info_user))) return -EFAULT; break; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 0690268..880580c 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -727,8 +727,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c concat->mtd.erasesize = subdev[0]->erasesize; concat->mtd.writesize = subdev[0]->writesize; concat->mtd.oobsize = subdev[0]->oobsize; - concat->mtd.ecctype = subdev[0]->ecctype; - concat->mtd.eccsize = subdev[0]->eccsize; if (subdev[0]->writev) concat->mtd.writev = concat_writev; if (subdev[0]->read_oob) @@ -774,8 +772,6 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to c if (concat->mtd.writesize != subdev[i]->writesize || concat->mtd.subpage_sft != subdev[i]->subpage_sft || concat->mtd.oobsize != subdev[i]->oobsize || - concat->mtd.ecctype != subdev[i]->ecctype || - concat->mtd.eccsize != subdev[i]->eccsize || !concat->mtd.read_oob != !subdev[i]->read_oob || !concat->mtd.write_oob != !subdev[i]->write_oob) { kfree(concat); diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index bafd2fb..633def3 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -338,8 +338,6 @@ int add_mtd_partitions(struct mtd_info *master, slave->mtd.size = parts[i].size; slave->mtd.writesize = master->writesize; slave->mtd.oobsize = master->oobsize; - slave->mtd.ecctype = master->ecctype; - slave->mtd.eccsize = master->eccsize; slave->mtd.subpage_sft = master->subpage_sft; slave->mtd.name = parts[i].name; diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 358f55a..2d12dcd 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -126,10 +126,6 @@ config MTD_NAND_S3C2410_HWECC incorrect ECC generation, and if using these, the default of software ECC is preferable. - If you lay down a device with the hardware ECC, then you will - currently not be able to switch to software, as there is no - implementation for ECC method used by the S3C2410 - config MTD_NAND_NDFC tristate "NDFC NanD Flash Controller" depends on MTD_NAND && 44x @@ -221,9 +217,17 @@ config MTD_NAND_SHARPSL tristate "Support for NAND Flash on Sharp SL Series (C7xx + others)" depends on MTD_NAND && ARCH_PXA +config MTD_NAND_BASLER_EXCITE + tristate "Support for NAND Flash on Basler eXcite" + depends on MTD_NAND && BASLER_EXCITE + help + This enables the driver for the NAND flash device found on the + Basler eXcite Smart Camera. If built as a module, the driver + will be named "excite_nandflash.ko". + config MTD_NAND_CAFE tristate "NAND support for OLPC CAFÉ chip" - depends on PCI + depends on MTD_NAND && PCI help Use NAND flash attached to the CAFÉ chip designed for the $100 laptop. diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f7a53f0..80f1dfc 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_AT91) += at91_nand.o +obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o nand-objs := nand_base.o nand_bbt.o cafe_nand-objs := cafe.o cafe_ecc.o diff --git a/drivers/mtd/nand/cafe.c b/drivers/mtd/nand/cafe.c index 08cb060..fd6bb3e 100644 --- a/drivers/mtd/nand/cafe.c +++ b/drivers/mtd/nand/cafe.c @@ -78,8 +78,9 @@ module_param(regdebug, int, 0644); static int checkecc = 1; module_param(checkecc, int, 0644); -static int slowtiming = 0; -module_param(slowtiming, int, 0644); +static int numtimings; +static int timing[3]; +module_param_array(timing, int, &numtimings, 0644); /* Hrm. Why isn't this already conditional on something in the struct device? */ #define cafe_dev_dbg(dev, args...) do { if (debug) dev_dbg(dev, ##args); } while(0) @@ -264,10 +265,10 @@ static void cafe_nand_cmdfunc(struct mtd_info *mtd, unsigned command, ndelay(100); if (1) { - int c = 500000; + int c; uint32_t irqs; - while (c--) { + for (c = 500000; c != 0; c--) { irqs = cafe_readl(cafe, NAND_IRQ); if (irqs & doneint) break; @@ -529,6 +530,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, { struct mtd_info *mtd; struct cafe_priv *cafe; + uint32_t timing1, timing2, timing3; uint32_t ctrl; int err = 0; @@ -580,31 +582,45 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe->nand.block_bad = cafe_nand_block_bad; } + if (numtimings && numtimings != 3) { + dev_warn(&cafe->pdev->dev, "%d timing register values ignored; precisely three are required\n", numtimings); + } + + if (numtimings == 3) { + timing1 = timing[0]; + timing2 = timing[1]; + timing3 = timing[2]; + cafe_dev_dbg(&cafe->pdev->dev, "Using provided timings (%08x %08x %08x)\n", + timing1, timing2, timing3); + } else { + timing1 = cafe_readl(cafe, NAND_TIMING1); + timing2 = cafe_readl(cafe, NAND_TIMING2); + timing3 = cafe_readl(cafe, NAND_TIMING3); + + if (timing1 | timing2 | timing3) { + cafe_dev_dbg(&cafe->pdev->dev, "Timing registers already set (%08x %08x %08x)\n", timing1, timing2, timing3); + } else { + dev_warn(&cafe->pdev->dev, "Timing registers unset; using most conservative defaults\n"); + timing1 = timing2 = timing3 = 0xffffffff; + } + } + /* Start off by resetting the NAND controller completely */ cafe_writel(cafe, 1, NAND_RESET); cafe_writel(cafe, 0, NAND_RESET); - cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); + cafe_writel(cafe, timing1, NAND_TIMING1); + cafe_writel(cafe, timing2, NAND_TIMING2); + cafe_writel(cafe, timing3, NAND_TIMING3); - /* Timings from Marvell's test code (not verified or calculated by us) */ - if (!slowtiming) { - cafe_writel(cafe, 0x01010a0a, NAND_TIMING1); - cafe_writel(cafe, 0x24121212, NAND_TIMING2); - cafe_writel(cafe, 0x11000000, NAND_TIMING3); - } else { - cafe_writel(cafe, 0xffffffff, NAND_TIMING1); - cafe_writel(cafe, 0xffffffff, NAND_TIMING2); - cafe_writel(cafe, 0xffffffff, NAND_TIMING3); - } cafe_writel(cafe, 0xffffffff, NAND_IRQ_MASK); err = request_irq(pdev->irq, &cafe_nand_interrupt, IRQF_SHARED, "CAFE NAND", mtd); if (err) { dev_warn(&pdev->dev, "Could not register IRQ %d\n", pdev->irq); - goto out_free_dma; } -#if 1 + /* Disable master reset, enable NAND clock */ ctrl = cafe_readl(cafe, GLOBAL_CTRL); ctrl &= 0xffffeff0; @@ -631,32 +647,8 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe_writel(cafe, 0x80000007, GLOBAL_IRQ_MASK); cafe_dev_dbg(&cafe->pdev->dev, "Control %x, IRQ mask %x\n", cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); -#endif -#if 1 - mtd->writesize=2048; - mtd->oobsize = 0x40; - memset(cafe->dmabuf, 0x5a, 2112); - cafe->nand.cmdfunc(mtd, NAND_CMD_READID, 0, -1); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); -#endif -#if 0 - cafe->nand.cmdfunc(mtd, NAND_CMD_READ0, 0, 0); - // nand_wait_ready(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); - cafe->nand.read_byte(mtd); -#endif -#if 0 - writel(0x84600070, cafe->mmio); - udelay(10); - cafe_dev_dbg(&cafe->pdev->dev, "Status %x\n", cafe_readl(cafe, NAND_NONMEM)); -#endif - /* Scan to find existance of the device */ + + /* Scan to find existence of the device */ if (nand_scan_ident(mtd, 1)) { err = -ENXIO; goto out_irq; @@ -760,13 +752,4 @@ module_exit(cafe_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); -MODULE_DESCRIPTION("NAND flash driver for OLPC CAFE chip"); - -/* Correct ECC for 2048 bytes of 0xff: - 41 a0 71 65 54 27 f3 93 ec a9 be ed 0b a1 */ - -/* dwmw2's B-test board, in case of completely screwing it: -Bad eraseblock 2394 at 0x12b40000 -Bad eraseblock 2627 at 0x14860000 -Bad eraseblock 3349 at 0x1a2a0000 -*/ +MODULE_DESCRIPTION("NAND flash driver for OLPC CAFÉ chip"); diff --git a/drivers/mtd/nand/cafe_ecc.c b/drivers/mtd/nand/cafe_ecc.c index 1b9fa05..ea5c849 100644 --- a/drivers/mtd/nand/cafe_ecc.c +++ b/drivers/mtd/nand/cafe_ecc.c @@ -1045,7 +1045,7 @@ static unsigned short err_pos_lut[4096] = { static unsigned short err_pos(unsigned short din) { - BUG_ON(din > 4096); + BUG_ON(din >= ARRAY_SIZE(err_pos_lut)); return err_pos_lut[din]; } static int chk_no_err_only(unsigned short *chk_syndrome_list, unsigned short *err_info) diff --git a/drivers/mtd/nand/excite_nandflash.c b/drivers/mtd/nand/excite_nandflash.c new file mode 100644 index 0000000..7e9afc4 --- /dev/null +++ b/drivers/mtd/nand/excite_nandflash.c @@ -0,0 +1,248 @@ +/* +* Copyright (C) 2005 - 2007 by Basler Vision Technologies AG +* Author: Thomas Koeller <thomas.koeller.qbaslerweb.com> +* Original code by Thies Moeller <thies.moeller@baslerweb.com> +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation; either version 2 of the License, or +* (at your option) any later version. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +*/ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> +#include <linux/kernel.h> +#include <linux/string.h> +#include <linux/ioport.h> +#include <linux/platform_device.h> +#include <linux/delay.h> +#include <linux/err.h> +#include <linux/kernel.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/rm9k-ocd.h> + +#include <excite_nandflash.h> + +#define EXCITE_NANDFLASH_VERSION "0.1" + +/* I/O register offsets */ +#define EXCITE_NANDFLASH_DATA_BYTE 0x00 +#define EXCITE_NANDFLASH_STATUS_BYTE 0x0c +#define EXCITE_NANDFLASH_ADDR_BYTE 0x10 +#define EXCITE_NANDFLASH_CMD_BYTE 0x14 + +/* prefix for debug output */ +static const char module_id[] = "excite_nandflash"; + +/* + * partition definition + */ +static const struct mtd_partition partition_info[] = { + { + .name = "eXcite RootFS", + .offset = 0, + .size = MTDPART_SIZ_FULL + } +}; + +static inline const struct resource * +excite_nand_get_resource(struct platform_device *d, unsigned long flags, + const char *basename) +{ + char buf[80]; + + if (snprintf(buf, sizeof buf, "%s_%u", basename, d->id) >= sizeof buf) + return NULL; + return platform_get_resource_byname(d, flags, buf); +} + +static inline void __iomem * +excite_nand_map_regs(struct platform_device *d, const char *basename) +{ + void *result = NULL; + const struct resource *const r = + excite_nand_get_resource(d, IORESOURCE_MEM, basename); + + if (r) + result = ioremap_nocache(r->start, r->end + 1 - r->start); + return result; +} + +/* controller and mtd information */ +struct excite_nand_drvdata { + struct mtd_info board_mtd; + struct nand_chip board_chip; + void __iomem *regs; + void __iomem *tgt; +}; + +/* Control function */ +static void excite_nand_control(struct mtd_info *mtd, int cmd, + unsigned int ctrl) +{ + struct excite_nand_drvdata * const d = + container_of(mtd, struct excite_nand_drvdata, board_mtd); + + switch (ctrl) { + case NAND_CTRL_CHANGE | NAND_CTRL_CLE: + d->tgt = d->regs + EXCITE_NANDFLASH_CMD_BYTE; + break; + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + d->tgt = d->regs + EXCITE_NANDFLASH_ADDR_BYTE; + break; + case NAND_CTRL_CHANGE | NAND_NCE: + d->tgt = d->regs + EXCITE_NANDFLASH_DATA_BYTE; + break; + } + + if (cmd != NAND_CMD_NONE) + __raw_writeb(cmd, d->tgt); +} + +/* Return 0 if flash is busy, 1 if ready */ +static int excite_nand_devready(struct mtd_info *mtd) +{ + struct excite_nand_drvdata * const drvdata = + container_of(mtd, struct excite_nand_drvdata, board_mtd); + + return __raw_readb(drvdata->regs + EXCITE_NANDFLASH_STATUS_BYTE); +} + +/* + * Called by device layer to remove the driver. + * The binding to the mtd and all allocated + * resources are released. + */ +static int __exit excite_nand_remove(struct device *dev) +{ + struct excite_nand_drvdata * const this = dev_get_drvdata(dev); + + dev_set_drvdata(dev, NULL); + + if (unlikely(!this)) { + printk(KERN_ERR "%s: called %s without private data!!", + module_id, __func__); + return -EINVAL; + } + + /* first thing we need to do is release our mtd + * then go through freeing the resource used + */ + nand_release(&this->board_mtd); + + /* free the common resources */ + iounmap(this->regs); + kfree(this); + + DEBUG(MTD_DEBUG_LEVEL1, "%s: removed\n", module_id); + return 0; +} + +/* + * Called by device layer when it finds a device matching + * one our driver can handle. This code checks to see if + * it can allocate all necessary resources then calls the + * nand layer to look for devices. +*/ +static int __init excite_nand_probe(struct device *dev) +{ + struct platform_device * const pdev = to_platform_device(dev); + struct excite_nand_drvdata *drvdata; /* private driver data */ + struct nand_chip *board_chip; /* private flash chip data */ + struct mtd_info *board_mtd; /* mtd info for this board */ + int scan_res; + + drvdata = kzalloc(sizeof(*drvdata), GFP_KERNEL); + if (unlikely(!drvdata)) { + printk(KERN_ERR "%s: no memory for drvdata\n", + module_id); + return -ENOMEM; + } + + /* bind private data into driver */ + dev_set_drvdata(dev, drvdata); + + /* allocate and map the resource */ + drvdata->regs = + excite_nand_map_regs(pdev, EXCITE_NANDFLASH_RESOURCE_REGS); + + if (unlikely(!drvdata->regs)) { + printk(KERN_ERR "%s: cannot reserve register region\n", + module_id); + kfree(drvdata); + return -ENXIO; + } + + drvdata->tgt = drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; + + /* initialise our chip */ + board_chip = &drvdata->board_chip; + board_chip->IO_ADDR_R = board_chip->IO_ADDR_W = + drvdata->regs + EXCITE_NANDFLASH_DATA_BYTE; + board_chip->cmd_ctrl = excite_nand_control; + board_chip->dev_ready = excite_nand_devready; + board_chip->chip_delay = 25; + board_chip->ecc.mode = NAND_ECC_SOFT; + + /* link chip to mtd */ + board_mtd = &drvdata->board_mtd; + board_mtd->priv = board_chip; + + DEBUG(MTD_DEBUG_LEVEL2, "%s: device scan\n", module_id); + scan_res = nand_scan(&drvdata->board_mtd, 1); + + if (likely(!scan_res)) { + DEBUG(MTD_DEBUG_LEVEL2, "%s: register partitions\n", module_id); + add_mtd_partitions(&drvdata->board_mtd, partition_info, + sizeof partition_info / sizeof partition_info[0]); + } else { + iounmap(drvdata->regs); + kfree(drvdata); + printk(KERN_ERR "%s: device scan failed\n", module_id); + return -EIO; + } + return 0; +} + +static struct device_driver excite_nand_driver = { + .name = "excite_nand", + .bus = &platform_bus_type, + .probe = excite_nand_probe, + .remove = __exit_p(excite_nand_remove) +}; + +static int __init excite_nand_init(void) +{ + pr_info("Basler eXcite nand flash driver Version " + EXCITE_NANDFLASH_VERSION "\n"); + return driver_register(&excite_nand_driver); +} + +static void __exit excite_nand_exit(void) +{ + driver_unregister(&excite_nand_driver); +} + +module_init(excite_nand_init); +module_exit(excite_nand_exit); + +MODULE_AUTHOR("Thomas Koeller <thomas.koeller@baslerweb.com>"); +MODULE_DESCRIPTION("Basler eXcite NAND-Flash driver"); +MODULE_LICENSE("GPL"); +MODULE_VERSION(EXCITE_NANDFLASH_VERSION) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index dfe56e0..acaf97b 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1272,10 +1272,25 @@ static int nand_do_read_oob(struct mtd_info *mtd, loff_t from, DEBUG(MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08Lx, len = %i\n", (unsigned long long)from, readlen); - if (ops->mode == MTD_OOB_RAW) - len = mtd->oobsize; - else + if (ops->mode == MTD_OOB_AUTO) len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + + if (unlikely(ops->ooboffs >= len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start read outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(from >= mtd->size || + ops->ooboffs + readlen > ((mtd->size >> chip->page_shift) - + (from >> chip->page_shift)) * len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt read beyond end of device\n"); + return -EINVAL; + } chipnr = (int)(from >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -1742,19 +1757,40 @@ static int nand_write(struct mtd_info *mtd, loff_t to, size_t len, static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - int chipnr, page, status; + int chipnr, page, status, len; struct nand_chip *chip = mtd->priv; DEBUG(MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int)to, (int)ops->ooblen); + if (ops->mode == MTD_OOB_AUTO) + len = chip->ecc.layout->oobavail; + else + len = mtd->oobsize; + /* Do not allow write past end of page */ - if ((ops->ooboffs + ops->ooblen) > mtd->oobsize) { + if ((ops->ooboffs + ops->ooblen) > len) { DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: " "Attempt to write past end of page\n"); return -EINVAL; } + if (unlikely(ops->ooboffs >= len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt to start write outside oob\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + ops->ooboffs + ops->ooblen > + ((mtd->size >> chip->page_shift) - + (to >> chip->page_shift)) * len)) { + DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: " + "Attempt write beyond end of device\n"); + return -EINVAL; + } + chipnr = (int)(to >> chip->chip_shift); chip->select_chip(mtd, chipnr); @@ -2530,7 +2566,6 @@ int nand_scan_tail(struct mtd_info *mtd) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_SW; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8b32035..0ddfd6d 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -337,17 +337,69 @@ static int s3c2412_nand_devready(struct mtd_info *mtd) static int s3c2410_nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) { - pr_debug("s3c2410_nand_correct_data(%p,%p,%p,%p)\n", mtd, dat, read_ecc, calc_ecc); + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned int diff0, diff1, diff2; + unsigned int bit, byte; - pr_debug("eccs: read %02x,%02x,%02x vs calc %02x,%02x,%02x\n", - read_ecc[0], read_ecc[1], read_ecc[2], calc_ecc[0], calc_ecc[1], calc_ecc[2]); + pr_debug("%s(%p,%p,%p,%p)\n", __func__, mtd, dat, read_ecc, calc_ecc); - if (read_ecc[0] == calc_ecc[0] && read_ecc[1] == calc_ecc[1] && read_ecc[2] == calc_ecc[2]) - return 0; + diff0 = read_ecc[0] ^ calc_ecc[0]; + diff1 = read_ecc[1] ^ calc_ecc[1]; + diff2 = read_ecc[2] ^ calc_ecc[2]; + + pr_debug("%s: rd %02x%02x%02x calc %02x%02x%02x diff %02x%02x%02x\n", + __func__, + read_ecc[0], read_ecc[1], read_ecc[2], + calc_ecc[0], calc_ecc[1], calc_ecc[2], + diff0, diff1, diff2); + + if (diff0 == 0 && diff1 == 0 && diff2 == 0) + return 0; /* ECC is ok */ + + /* Can we correct this ECC (ie, one row and column change). + * Note, this is similar to the 256 error code on smartmedia */ + + if (((diff0 ^ (diff0 >> 1)) & 0x55) == 0x55 && + ((diff1 ^ (diff1 >> 1)) & 0x55) == 0x55 && + ((diff2 ^ (diff2 >> 1)) & 0x55) == 0x55) { + /* calculate the bit position of the error */ + + bit = (diff2 >> 2) & 1; + bit |= (diff2 >> 3) & 2; + bit |= (diff2 >> 4) & 4; + + /* calculate the byte position of the error */ + + byte = (diff1 << 1) & 0x80; + byte |= (diff1 << 2) & 0x40; + byte |= (diff1 << 3) & 0x20; + byte |= (diff1 << 4) & 0x10; + + byte |= (diff0 >> 3) & 0x08; + byte |= (diff0 >> 2) & 0x04; + byte |= (diff0 >> 1) & 0x02; + byte |= (diff0 >> 0) & 0x01; - /* we curently have no method for correcting the error */ + byte |= (diff2 << 8) & 0x100; - return -1; + dev_dbg(info->device, "correcting error bit %d, byte %d\n", + bit, byte); + + dat[byte] ^= (1 << bit); + return 1; + } + + /* if there is only one bit difference in the ECC, then + * one of only a row or column parity has changed, which + * means the error is most probably in the ECC itself */ + + diff0 |= (diff1 << 8); + diff0 |= (diff2 << 16); + + if ((diff0 & ~(1<<fls(diff0))) == 0) + return 1; + + return 0; } /* ECC functions @@ -366,6 +418,15 @@ static void s3c2410_nand_enable_hwecc(struct mtd_info *mtd, int mode) writel(ctrl, info->regs + S3C2410_NFCONF); } +static void s3c2412_nand_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ctrl; + + ctrl = readl(info->regs + S3C2440_NFCONT); + writel(ctrl | S3C2412_NFCONT_INIT_MAIN_ECC, info->regs + S3C2440_NFCONT); +} + static void s3c2440_nand_enable_hwecc(struct mtd_info *mtd, int mode) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); @@ -383,6 +444,21 @@ static int s3c2410_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ecc_code[1] = readb(info->regs + S3C2410_NFECC + 1); ecc_code[2] = readb(info->regs + S3C2410_NFECC + 2); + pr_debug("%s: returning ecc %02x%02x%02x\n", __func__, + ecc_code[0], ecc_code[1], ecc_code[2]); + + return 0; +} + +static int s3c2412_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) +{ + struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); + unsigned long ecc = readl(info->regs + S3C2412_NFMECC0); + + ecc_code[0] = ecc; + ecc_code[1] = ecc >> 8; + ecc_code[2] = ecc >> 16; + pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); return 0; @@ -397,7 +473,7 @@ static int s3c2440_nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u ecc_code[1] = ecc >> 8; ecc_code[2] = ecc >> 16; - pr_debug("calculate_ecc: returning ecc %02x,%02x,%02x\n", ecc_code[0], ecc_code[1], ecc_code[2]); + pr_debug("%s: returning ecc %06x\n", __func__, ecc); return 0; } @@ -565,6 +641,10 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, break; case TYPE_S3C2412: + chip->ecc.hwctl = s3c2412_nand_enable_hwecc; + chip->ecc.calculate = s3c2412_nand_calculate_ecc; + break; + case TYPE_S3C2440: chip->ecc.hwctl = s3c2440_nand_enable_hwecc; chip->ecc.calculate = s3c2440_nand_calculate_ecc; diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 2da6bb2..7f1cb6e 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1,7 +1,7 @@ /* * linux/drivers/mtd/onenand/onenand_base.c * - * Copyright (C) 2005-2006 Samsung Electronics + * Copyright (C) 2005-2007 Samsung Electronics * Kyungmin Park <kyungmin.park@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -94,16 +94,9 @@ static void onenand_writew(unsigned short value, void __iomem *addr) */ static int onenand_block_address(struct onenand_chip *this, int block) { - if (this->device_id & ONENAND_DEVICE_IS_DDP) { - /* Device Flash Core select, NAND Flash Block Address */ - int dfs = 0; - - if (block & this->density_mask) - dfs = 1; - - return (dfs << ONENAND_DDP_SHIFT) | - (block & (this->density_mask - 1)); - } + /* Device Flash Core select, NAND Flash Block Address */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1 | (block ^ this->density_mask); return block; } @@ -118,17 +111,11 @@ static int onenand_block_address(struct onenand_chip *this, int block) */ static int onenand_bufferram_address(struct onenand_chip *this, int block) { - if (this->device_id & ONENAND_DEVICE_IS_DDP) { - /* Device BufferRAM Select */ - int dbs = 0; - - if (block & this->density_mask) - dbs = 1; + /* Device BufferRAM Select */ + if (block & this->density_mask) + return ONENAND_DDP_CHIP1; - return (dbs << ONENAND_DDP_SHIFT); - } - - return 0; + return ONENAND_DDP_CHIP0; } /** @@ -317,22 +304,25 @@ static int onenand_wait(struct mtd_info *mtd, int state) 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\n", ctrl); + printk(KERN_ERR "onenand_wait: controller error = 0x%04x\n", ctrl); if (ctrl & ONENAND_CTRL_LOCK) - DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: it's locked error.\n"); + printk(KERN_ERR "onenand_wait: it's locked error.\n"); return ctrl; } if (interrupt & ONENAND_INT_READ) { int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); if (ecc) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_wait: ECC error = 0x%04x\n", ecc); + printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); if (ecc & ONENAND_ECC_2BIT_ALL) { mtd->ecc_stats.failed++; return ecc; } else if (ecc & ONENAND_ECC_1BIT_ALL) mtd->ecc_stats.corrected++; } + } else if (state == FL_READING) { + printk(KERN_ERR "onenand_wait: read timeout! ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); + return -EIO; } return 0; @@ -587,22 +577,32 @@ static int onenand_write_bufferram(struct mtd_info *mtd, int area, static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) { struct onenand_chip *this = mtd->priv; - int block, page; - int i; + int blockpage, found = 0; + unsigned int i; - block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift); - page &= this->page_mask; + blockpage = (int) (addr >> this->page_shift); + /* Is there valid data? */ i = ONENAND_CURRENT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) + found = 1; + else { + /* Check another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) { + ONENAND_SET_NEXT_BUFFERRAM(this); + found = 1; + } + } - /* Is there valid data? */ - if (this->bufferram[i].block == block && - this->bufferram[i].page == page && - this->bufferram[i].valid) - return 1; + if (found && ONENAND_IS_DDP(this)) { + /* Select DataRAM for DDP */ + int block = (int) (addr >> this->erase_shift); + int value = onenand_bufferram_address(this, block); + this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); + } - return 0; + return found; } /** @@ -613,31 +613,49 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) * * Update BufferRAM information */ -static int onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, +static void onenand_update_bufferram(struct mtd_info *mtd, loff_t addr, int valid) { struct onenand_chip *this = mtd->priv; - int block, page; - int i; + int blockpage; + unsigned int i; - block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift); - page &= this->page_mask; + blockpage = (int) (addr >> this->page_shift); - /* Invalidate BufferRAM */ - for (i = 0; i < MAX_BUFFERRAM; i++) { - if (this->bufferram[i].block == block && - this->bufferram[i].page == page) - this->bufferram[i].valid = 0; - } + /* Invalidate another BufferRAM */ + i = ONENAND_NEXT_BUFFERRAM(this); + if (this->bufferram[i].blockpage == blockpage) + this->bufferram[i].blockpage = -1; /* Update BufferRAM */ i = ONENAND_CURRENT_BUFFERRAM(this); - this->bufferram[i].block = block; - this->bufferram[i].page = page; - this->bufferram[i].valid = valid; + if (valid) + this->bufferram[i].blockpage = blockpage; + else + this->bufferram[i].blockpage = -1; +} - return 0; +/** + * onenand_invalidate_bufferram - [GENERIC] Invalidate BufferRAM information + * @param mtd MTD data structure + * @param addr start address to invalidate + * @param len length to invalidate + * + * Invalidate BufferRAM information + */ +static void onenand_invalidate_bufferram(struct mtd_info *mtd, loff_t addr, + unsigned int len) +{ + struct onenand_chip *this = mtd->priv; + int i; + loff_t end_addr = addr + len; + + /* Invalidate BufferRAM */ + for (i = 0; i < MAX_BUFFERRAM; i++) { + loff_t buf_addr = this->bufferram[i].blockpage << this->page_shift; + if (buf_addr >= addr && buf_addr < end_addr) + this->bufferram[i].blockpage = -1; + } } /** @@ -716,7 +734,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read: Attempt read beyond end of device\n"); + printk(KERN_ERR "onenand_read: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } @@ -724,8 +742,6 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); - /* TODO handling oob */ - stats = mtd->ecc_stats; /* Read-while-load method */ @@ -754,9 +770,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, * Now we issued chip 1 read and pointed chip 1 * bufferam so we have to point chip 0 bufferam. */ - if (this->device_id & ONENAND_DEVICE_IS_DDP && - unlikely(from == (this->chipsize >> 1))) { - this->write_word(0, this->base + ONENAND_REG_START_ADDRESS2); + if (ONENAND_IS_DDP(this) && + unlikely(from == (this->chipsize >> 1))) { + this->write_word(ONENAND_DDP_CHIP0, this->base + ONENAND_REG_START_ADDRESS2); boundary = 1; } else boundary = 0; @@ -770,7 +786,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, break; /* Set up for next read from bufferRAM */ if (unlikely(boundary)) - this->write_word(0x8000, this->base + ONENAND_REG_START_ADDRESS2); + this->write_word(ONENAND_DDP_CHIP1, this->base + ONENAND_REG_START_ADDRESS2); ONENAND_SET_NEXT_BUFFERRAM(this); buf += thislen; thislen = min_t(int, mtd->writesize, len - read); @@ -801,20 +817,59 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, } /** + * onenand_transfer_auto_oob - [Internal] oob auto-placement transfer + * @param mtd MTD device structure + * @param buf destination address + * @param column oob offset to read from + * @param thislen oob length to read + */ +static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int column, + int thislen) +{ + struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int readcol = column; + int readend = column + thislen; + int lastgap = 0; + uint8_t *oob_buf = this->page_buf + mtd->writesize; + + for (free = this->ecclayout->oobfree; free->length; ++free) { + if (readcol >= lastgap) + readcol += free->offset - lastgap; + if (readend >= lastgap) + readend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + this->read_bufferram(mtd, ONENAND_SPARERAM, oob_buf, 0, mtd->oobsize); + for (free = this->ecclayout->oobfree; free->length; ++free) { + int free_end = free->offset + free->length; + if (free->offset < readend && free_end > readcol) { + int st = max_t(int,free->offset,readcol); + int ed = min_t(int,free_end,readend); + int n = ed - st; + memcpy(buf, oob_buf + st, n); + buf += n; + } + } + return 0; +} + +/** * onenand_do_read_oob - [MTD Interface] OneNAND read out-of-band * @param mtd MTD device structure * @param from offset to read from * @param len number of bytes to read * @param retlen pointer to variable to store the number of read bytes * @param buf the databuffer to put data + * @param mode operation mode * * OneNAND read out-of-band data from the spare area */ -int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, mtd_oob_mode_t mode) { struct onenand_chip *this = mtd->priv; - int read = 0, thislen, column; + int read = 0, thislen, column, oobsize; int ret = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); @@ -822,21 +877,33 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, /* Initialize return length value */ *retlen = 0; + if (mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + column = from & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + printk(KERN_ERR "onenand_read_oob: Attempted to start read outside oob\n"); + return -EINVAL; + } + /* Do not allow reads past end of device */ - if (unlikely((from + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: Attempt read beyond end of device\n"); + if (unlikely(from >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (from >> this->page_shift)) * oobsize)) { + printk(KERN_ERR "onenand_read_oob: Attempted to read beyond end of device\n"); return -EINVAL; } /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_READING); - column = from & (mtd->oobsize - 1); - while (read < len) { cond_resched(); - thislen = mtd->oobsize - column; + thislen = oobsize - column; thislen = min_t(int, thislen, len); this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); @@ -846,11 +913,14 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, ret = this->wait(mtd, FL_READING); /* First copy data and check return value for ECC handling */ - this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); + if (mode == MTD_OOB_AUTO) + onenand_transfer_auto_oob(mtd, buf, column, thislen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_read_oob: read failed = 0x%x\n", ret); - goto out; + printk(KERN_ERR "onenand_read_oob: read failed = 0x%x\n", ret); + break; } read += thislen; @@ -868,7 +938,6 @@ int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, } } -out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); @@ -885,10 +954,132 @@ out: static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { - BUG_ON(ops->mode != MTD_OOB_PLACE); - + switch (ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + case MTD_OOB_RAW: + /* Not implemented yet */ + default: + return -EINVAL; + } return onenand_do_read_oob(mtd, from + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); +} + +/** + * onenand_bbt_wait - [DEFAULT] wait until the command is done + * @param mtd MTD device structure + * @param state state to select the max. timeout value + * + * Wait for command done. + */ +static int onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + struct onenand_chip *this = mtd->priv; + unsigned long timeout; + unsigned int interrupt; + unsigned int ctrl; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + if (interrupt & ONENAND_INT_MASTER) + break; + } + /* To get correct interrupt status in timeout case */ + interrupt = this->read_word(this->base + ONENAND_REG_INTERRUPT); + ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); + + if (ctrl & ONENAND_CTRL_ERROR) { + printk(KERN_DEBUG "onenand_bbt_wait: controller error = 0x%04x\n", ctrl); + /* Initial bad block case */ + if (ctrl & ONENAND_CTRL_LOAD) + return ONENAND_BBT_READ_ERROR; + return ONENAND_BBT_READ_FATAL_ERROR; + } + + if (interrupt & ONENAND_INT_READ) { + int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); + if (ecc & ONENAND_ECC_2BIT_ALL) + return ONENAND_BBT_READ_ERROR; + } else { + printk(KERN_ERR "onenand_bbt_wait: read timeout!" + "ctrl=0x%04x intr=0x%04x\n", ctrl, interrupt); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + return 0; +} + +/** + * onenand_bbt_read_oob - [MTD Interface] OneNAND read out-of-band for bbt scan + * @param mtd MTD device structure + * @param from offset to read from + * @param @ops oob operation description structure + * + * OneNAND read out-of-band data from the spare area for bbt scan + */ +int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + int read = 0, thislen, column; + int ret = 0; + size_t len = ops->ooblen; + u_char *buf = ops->oobbuf; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_bbt_read_oob: from = 0x%08x, len = %zi\n", (unsigned int) from, len); + + /* Initialize return value */ + ops->oobretlen = 0; + + /* Do not allow reads past end of device */ + if (unlikely((from + len) > mtd->size)) { + printk(KERN_ERR "onenand_bbt_read_oob: Attempt read beyond end of device\n"); + return ONENAND_BBT_READ_FATAL_ERROR; + } + + /* Grab the lock and see if the device is available */ + onenand_get_device(mtd, FL_READING); + + column = from & (mtd->oobsize - 1); + + while (read < len) { + cond_resched(); + + thislen = mtd->oobsize - column; + thislen = min_t(int, thislen, len); + + this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + + onenand_update_bufferram(mtd, from, 0); + + ret = onenand_bbt_wait(mtd, FL_READING); + if (ret) + break; + + this->read_bufferram(mtd, ONENAND_SPARERAM, buf, column, thislen); + read += thislen; + if (read == len) + break; + + buf += thislen; + + /* Read more? */ + if (read < len) { + /* Update Page size */ + from += mtd->writesize; + column = 0; + } + } + + /* Deselect and wake up anyone waiting on the device */ + onenand_release_device(mtd); + + ops->oobretlen = read; + return ret; } #ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE @@ -897,14 +1088,12 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, * @param mtd MTD device structure * @param buf the databuffer to verify * @param to offset to read from - * @param len number of bytes to read and compare * */ -static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to, int len) +static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to) { struct onenand_chip *this = mtd->priv; - char *readp = this->page_buf; - int column = to & (mtd->oobsize - 1); + char *readp = this->page_buf + mtd->writesize; int status, i; this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); @@ -913,9 +1102,8 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to if (status) return status; - this->read_bufferram(mtd, ONENAND_SPARERAM, readp, column, len); - - for(i = 0; i < len; i++) + this->read_bufferram(mtd, ONENAND_SPARERAM, readp, 0, mtd->oobsize); + for(i = 0; i < mtd->oobsize; i++) if (buf[i] != 0xFF && buf[i] != readp[i]) return -EBADMSG; @@ -923,41 +1111,51 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to } /** - * onenand_verify_page - [GENERIC] verify the chip contents after a write - * @param mtd MTD device structure - * @param buf the databuffer to verify + * onenand_verify - [GENERIC] verify the chip contents after a write + * @param mtd MTD device structure + * @param buf the databuffer to verify + * @param addr offset to read from + * @param len number of bytes to read and compare * - * Check DataRAM area directly */ -static int onenand_verify_page(struct mtd_info *mtd, u_char *buf, loff_t addr) +static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - void __iomem *dataram0, *dataram1; + void __iomem *dataram; int ret = 0; + int thislen, column; - /* In partial page write, just skip it */ - if ((addr & (mtd->writesize - 1)) != 0) - return 0; + while (len != 0) { + thislen = min_t(int, mtd->writesize, len); + column = addr & (mtd->writesize - 1); + if (column + thislen > mtd->writesize) + thislen = mtd->writesize - column; - this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); + this->command(mtd, ONENAND_CMD_READ, addr, mtd->writesize); - ret = this->wait(mtd, FL_READING); - if (ret) - return ret; + onenand_update_bufferram(mtd, addr, 0); + + ret = this->wait(mtd, FL_READING); + if (ret) + return ret; - onenand_update_bufferram(mtd, addr, 1); + onenand_update_bufferram(mtd, addr, 1); - /* Check, if the two dataram areas are same */ - dataram0 = this->base + ONENAND_DATARAM; - dataram1 = dataram0 + mtd->writesize; + dataram = this->base + ONENAND_DATARAM; + dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); - if (memcmp(dataram0, dataram1, mtd->writesize)) - return -EBADMSG; + if (memcmp(buf, dataram + column, thislen)) + return -EBADMSG; + + len -= thislen; + buf += thislen; + addr += thislen; + } return 0; } #else -#define onenand_verify_page(...) (0) +#define onenand_verify(...) (0) #define onenand_verify_oob(...) (0) #endif @@ -988,60 +1186,57 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, /* Do not allow writes past end of device */ if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt write to past end of device\n"); + printk(KERN_ERR "onenand_write: Attempt write to past end of device\n"); return -EINVAL; } /* Reject writes, which are not page aligned */ if (unlikely(NOTALIGNED(to)) || unlikely(NOTALIGNED(len))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: Attempt to write not page aligned data\n"); + printk(KERN_ERR "onenand_write: Attempt to write not page aligned data\n"); return -EINVAL; } column = to & (mtd->writesize - 1); - subpage = column || (len & (mtd->writesize - 1)); /* Grab the lock and see if the device is available */ onenand_get_device(mtd, FL_WRITING); /* Loop until all data write */ while (written < len) { - int bytes = mtd->writesize; - int thislen = min_t(int, bytes, len - written); + int thislen = min_t(int, mtd->writesize - column, len - written); u_char *wbuf = (u_char *) buf; cond_resched(); - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, bytes); + this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen); /* Partial page write */ + subpage = thislen < mtd->writesize; if (subpage) { - bytes = min_t(int, bytes - column, (int) len); memset(this->page_buf, 0xff, mtd->writesize); - memcpy(this->page_buf + column, buf, bytes); + memcpy(this->page_buf + column, buf, thislen); wbuf = this->page_buf; - /* Even though partial write, we need page size */ - thislen = mtd->writesize; } - this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, thislen); + this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize); this->write_bufferram(mtd, ONENAND_SPARERAM, ffchars, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize); + ret = this->wait(mtd, FL_WRITING); + /* In partial page write we don't update bufferram */ - onenand_update_bufferram(mtd, to, !subpage); + onenand_update_bufferram(mtd, to, !ret && !subpage); - ret = this->wait(mtd, FL_WRITING); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: write filaed %d\n", ret); + printk(KERN_ERR "onenand_write: write filaed %d\n", ret); break; } /* Only check verify write turn on */ - ret = onenand_verify_page(mtd, (u_char *) wbuf, to); + ret = onenand_verify(mtd, (u_char *) wbuf, to, thislen); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write: verify failed %d\n", ret); + printk(KERN_ERR "onenand_write: verify failed %d\n", ret); break; } @@ -1064,20 +1259,58 @@ static int onenand_write(struct mtd_info *mtd, loff_t to, size_t len, } /** + * onenand_fill_auto_oob - [Internal] oob auto-placement transfer + * @param mtd MTD device structure + * @param oob_buf oob buffer + * @param buf source address + * @param column oob offset to write to + * @param thislen oob length to write + */ +static int onenand_fill_auto_oob(struct mtd_info *mtd, u_char *oob_buf, + const u_char *buf, int column, int thislen) +{ + struct onenand_chip *this = mtd->priv; + struct nand_oobfree *free; + int writecol = column; + int writeend = column + thislen; + int lastgap = 0; + + for (free = this->ecclayout->oobfree; free->length; ++free) { + if (writecol >= lastgap) + writecol += free->offset - lastgap; + if (writeend >= lastgap) + writeend += free->offset - lastgap; + lastgap = free->offset + free->length; + } + for (free = this->ecclayout->oobfree; free->length; ++free) { + int free_end = free->offset + free->length; + if (free->offset < writeend && free_end > writecol) { + int st = max_t(int,free->offset,writecol); + int ed = min_t(int,free_end,writeend); + int n = ed - st; + memcpy(oob_buf + st, buf, n); + buf += n; + } + } + return 0; +} + +/** * onenand_do_write_oob - [Internal] OneNAND write out-of-band * @param mtd MTD device structure * @param to offset to write to * @param len number of bytes to write * @param retlen pointer to variable to store the number of written bytes * @param buf the data to write + * @param mode operation mode * * OneNAND write out-of-band */ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) + size_t *retlen, const u_char *buf, mtd_oob_mode_t mode) { struct onenand_chip *this = mtd->priv; - int column, ret = 0; + int column, ret = 0, oobsize; int written = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); @@ -1085,9 +1318,30 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, /* Initialize retlen, in case of early exit */ *retlen = 0; - /* Do not allow writes past end of device */ - if (unlikely((to + len) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: Attempt write to past end of device\n"); + if (mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + column = to & (mtd->oobsize - 1); + + if (unlikely(column >= oobsize)) { + printk(KERN_ERR "onenand_write_oob: Attempted to start write outside oob\n"); + return -EINVAL; + } + + /* For compatibility with NAND: Do not allow write past end of page */ + if (column + len > oobsize) { + printk(KERN_ERR "onenand_write_oob: " + "Attempt to write past end of page\n"); + return -EINVAL; + } + + /* Do not allow reads past end of device */ + if (unlikely(to >= mtd->size || + column + len > ((mtd->size >> this->page_shift) - + (to >> this->page_shift)) * oobsize)) { + printk(KERN_ERR "onenand_write_oob: Attempted to write past end of device\n"); return -EINVAL; } @@ -1096,18 +1350,19 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, /* Loop until all data write */ while (written < len) { - int thislen = min_t(int, mtd->oobsize, len - written); + int thislen = min_t(int, oobsize, len - written); cond_resched(); - column = to & (mtd->oobsize - 1); - this->command(mtd, ONENAND_CMD_BUFFERRAM, to, mtd->oobsize); /* We send data to spare ram with oobsize * to prevent byte access */ memset(this->page_buf, 0xff, mtd->oobsize); - memcpy(this->page_buf + column, buf, thislen); + if (mode == MTD_OOB_AUTO) + onenand_fill_auto_oob(mtd, this->page_buf, buf, column, thislen); + else + memcpy(this->page_buf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, this->page_buf, 0, mtd->oobsize); this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); @@ -1116,26 +1371,25 @@ static int onenand_do_write_oob(struct mtd_info *mtd, loff_t to, size_t len, ret = this->wait(mtd, FL_WRITING); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: write filaed %d\n", ret); - goto out; + printk(KERN_ERR "onenand_write_oob: write failed %d\n", ret); + break; } - ret = onenand_verify_oob(mtd, buf, to, thislen); + ret = onenand_verify_oob(mtd, this->page_buf, to); if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_write_oob: verify failed %d\n", ret); - goto out; + printk(KERN_ERR "onenand_write_oob: verify failed %d\n", ret); + break; } written += thislen; - if (written == len) break; - to += thislen; + to += mtd->writesize; buf += thislen; + column = 0; } -out: /* Deselect and wake up anyone waiting on the device */ onenand_release_device(mtd); @@ -1153,10 +1407,17 @@ out: static int onenand_write_oob(struct mtd_info *mtd, loff_t to, struct mtd_oob_ops *ops) { - BUG_ON(ops->mode != MTD_OOB_PLACE); - + switch (ops->mode) { + case MTD_OOB_PLACE: + case MTD_OOB_AUTO: + break; + case MTD_OOB_RAW: + /* Not implemented yet */ + default: + return -EINVAL; + } return onenand_do_write_oob(mtd, to + ops->ooboffs, ops->ooblen, - &ops->oobretlen, ops->oobbuf); + &ops->oobretlen, ops->oobbuf, ops->mode); } /** @@ -1199,19 +1460,19 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) /* Start address must align on block boundary */ if (unlikely(instr->addr & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Unaligned address\n"); + printk(KERN_ERR "onenand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (unlikely(instr->len & (block_size - 1))) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Length not block aligned\n"); + printk(KERN_ERR "onenand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if (unlikely((instr->len + instr->addr) > mtd->size)) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Erase past end of device\n"); + printk(KERN_ERR "onenand_erase: Erase past end of device\n"); return -EINVAL; } @@ -1238,10 +1499,12 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) this->command(mtd, ONENAND_CMD_ERASE, addr, block_size); + onenand_invalidate_bufferram(mtd, addr, block_size); + ret = this->wait(mtd, FL_ERASING); /* Check, if it is write protected */ if (ret) { - DEBUG(MTD_DEBUG_LEVEL0, "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); + printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; @@ -1322,7 +1585,7 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) /* We write two bytes, so we dont have to mess with 16 bit access */ ofs += mtd->oobsize + (bbm->badblockpos & ~0x01); - return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf); + return onenand_do_write_oob(mtd, ofs , 2, &retlen, buf, MTD_OOB_PLACE); } /** @@ -1491,6 +1754,8 @@ static int onenand_unlock_all(struct mtd_info *mtd) struct onenand_chip *this = mtd->priv; if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Set start block address */ + this->write_word(0, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Write unlock command */ this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); @@ -1503,13 +1768,10 @@ static int onenand_unlock_all(struct mtd_info *mtd) continue; /* Workaround for all block unlock in DDP */ - if (this->device_id & ONENAND_DEVICE_IS_DDP) { - loff_t ofs; - size_t len; - + if (ONENAND_IS_DDP(this)) { /* 1st block on another chip */ - ofs = this->chipsize >> 1; - len = 1 << this->erase_shift; + loff_t ofs = this->chipsize >> 1; + size_t len = mtd->erasesize; onenand_unlock(mtd, ofs, len); } @@ -1617,7 +1879,7 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = onenand_do_write_oob(mtd, from, len, retlen, buf); + ret = onenand_do_write_oob(mtd, from, len, retlen, buf, MTD_OOB_PLACE); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -1823,12 +2085,13 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, #endif /* CONFIG_MTD_ONENAND_OTP */ /** - * onenand_lock_scheme - Check and set OneNAND lock scheme + * onenand_check_features - Check and set OneNAND features * @param mtd MTD data structure * - * Check and set OneNAND lock scheme + * Check and set OneNAND features + * - lock scheme */ -static void onenand_lock_scheme(struct mtd_info *mtd) +static void onenand_check_features(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; unsigned int density, process; @@ -1961,26 +2224,28 @@ static int onenand_probe(struct mtd_info *mtd) density = dev_id >> ONENAND_DEVICE_DENSITY_SHIFT; this->chipsize = (16 << density) << 20; /* Set density mask. it is used for DDP */ - this->density_mask = (1 << (density + 6)); + if (ONENAND_IS_DDP(this)) + this->density_mask = (1 << (density + 6)); + else + this->density_mask = 0; /* OneNAND page size & block size */ /* The data buffer size is equal to page size */ mtd->writesize = this->read_word(this->base + ONENAND_REG_DATA_BUFFER_SIZE); mtd->oobsize = mtd->writesize >> 5; - /* Pagers per block is always 64 in OneNAND */ + /* Pages per a block are always 64 in OneNAND */ mtd->erasesize = mtd->writesize << 6; this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; - this->ppb_shift = (this->erase_shift - this->page_shift); - this->page_mask = (mtd->erasesize / mtd->writesize) - 1; + this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; /* REVIST: Multichip handling */ mtd->size = this->chipsize; - /* Check OneNAND lock scheme */ - onenand_lock_scheme(mtd); + /* Check OneNAND features */ + onenand_check_features(mtd); return 0; } @@ -2021,6 +2286,7 @@ static void onenand_resume(struct mtd_info *mtd) */ int onenand_scan(struct mtd_info *mtd, int maxchips) { + int i; struct onenand_chip *this = mtd->priv; if (!this->read_word) @@ -2092,12 +2358,21 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) } this->subpagesize = mtd->writesize >> mtd->subpage_sft; + + /* + * The number of bytes available for a client to place data into + * the out of band area + */ + this->ecclayout->oobavail = 0; + for (i = 0; this->ecclayout->oobfree[i].length; i++) + this->ecclayout->oobavail += + this->ecclayout->oobfree[i].length; + mtd->ecclayout = this->ecclayout; /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; mtd->flags = MTD_CAP_NANDFLASH; - mtd->ecctype = MTD_ECC_SW; mtd->erase = onenand_erase; mtd->point = NULL; mtd->unpoint = NULL; @@ -2144,8 +2419,11 @@ void onenand_release(struct mtd_info *mtd) del_mtd_device (mtd); /* Free bad block table memory, if allocated */ - if (this->bbm) + if (this->bbm) { + struct bbm_info *bbm = this->bbm; + kfree(bbm->bbt); kfree(this->bbm); + } /* Buffer allocated by onenand_scan */ if (this->options & ONENAND_PAGEBUF_ALLOC) kfree(this->page_buf); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 98f8fd1..aecdd50 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -17,8 +17,8 @@ #include <linux/mtd/onenand.h> #include <linux/mtd/compatmac.h> -extern int onenand_do_read_oob(struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); +extern int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops); /** * check_short_pattern - [GENERIC] check if a pattern is in the buffer @@ -65,10 +65,11 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr int startblock; loff_t from; size_t readlen, ooblen; + struct mtd_oob_ops ops; printk(KERN_INFO "Scanning device for bad blocks\n"); - len = 1; + len = 2; /* We need only read few bytes from the OOB area */ scanlen = ooblen = 0; @@ -82,22 +83,24 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr startblock = 0; from = 0; + ops.mode = MTD_OOB_PLACE; + ops.ooblen = readlen; + ops.oobbuf = buf; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 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 = onenand_do_read_oob(mtd, from + j * mtd->writesize + bd->offs, - readlen, &retlen, &buf[0]); + ret = onenand_bbt_read_oob(mtd, from + j * mtd->writesize + bd->offs, &ops); /* If it is a initial bad block, just ignore it */ - if (ret && !(ret & ONENAND_CTRL_LOAD)) - return ret; + if (ret == ONENAND_BBT_READ_FATAL_ERROR) + return -EIO; - if (check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { + if (ret || check_short_pattern(&buf[j * scanlen], scanlen, mtd->writesize, bd)) { bbm->bbt[i >> 3] |= 0x03 << (i & 0x6); printk(KERN_WARNING "Bad eraseblock %d at 0x%08x\n", i >> 1, (unsigned int) from); @@ -168,8 +171,8 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) * 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. + * The bad block table memory is allocated here. It is freed + * by the onenand_release function. * */ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 035cd9b..a61351f 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -94,8 +94,19 @@ static int parse_redboot_partitions(struct mtd_info *master, * (NOTE: this is 'size' not 'data_length'; size is * the full size of the entry.) */ - if (swab32(buf[i].size) == master->erasesize) { + + /* RedBoot can combine the FIS directory and + config partitions into a single eraseblock; + we assume wrong-endian if either the swapped + 'size' matches the eraseblock size precisely, + or if the swapped size actually fits in an + eraseblock while the unswapped size doesn't. */ + if (swab32(buf[i].size) == master->erasesize || + (buf[i].size > master->erasesize + && swab32(buf[i].size) < master->erasesize)) { int j; + /* Update numslots based on actual FIS directory size */ + numslots = swab32(buf[i].size) / sizeof (struct fis_image_desc); for (j = 0; j < numslots; ++j) { /* A single 0xff denotes a deleted entry. @@ -120,11 +131,11 @@ static int parse_redboot_partitions(struct mtd_info *master, swab32s(&buf[j].desc_cksum); swab32s(&buf[j].file_cksum); } + } else if (buf[i].size < master->erasesize) { + /* Update numslots based on actual FIS directory size */ + numslots = buf[i].size / sizeof(struct fis_image_desc); } break; - } else { - /* re-calculate of real numslots */ - numslots = buf[i].size / sizeof(struct fis_image_desc); } } if (i == numslots) { diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 0282696..07119c4 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -348,23 +348,27 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) ret = jffs2_sum_init(c); if (ret) - return ret; + goto out_free; if (jffs2_build_filesystem(c)) { dbg_fsbuild("build_fs failed\n"); jffs2_free_ino_caches(c); jffs2_free_raw_node_refs(c); -#ifndef __ECOS - if (jffs2_blocks_use_vmalloc(c)) - vfree(c->blocks); - else -#endif - kfree(c->blocks); - - return -EIO; + ret = -EIO; + goto out_free; } jffs2_calc_trigger_levels(c); return 0; + + out_free: +#ifndef __ECOS + if (jffs2_blocks_use_vmalloc(c)) + vfree(c->blocks); + else +#endif + kfree(c->blocks); + + return ret; } diff --git a/fs/jffs2/jffs2_fs_sb.h b/fs/jffs2/jffs2_fs_sb.h index b985949..ea88f69 100644 --- a/fs/jffs2/jffs2_fs_sb.h +++ b/fs/jffs2/jffs2_fs_sb.h @@ -98,20 +98,14 @@ struct jffs2_sb_info { uint32_t wbuf_pagesize; /* 0 for NOR and other flashes with no wbuf */ #ifdef CONFIG_JFFS2_FS_WRITEBUFFER - /* Write-behind buffer for NAND flash */ - unsigned char *wbuf; - unsigned char *oobbuf; + unsigned char *wbuf; /* Write-behind buffer for NAND flash */ uint32_t wbuf_ofs; uint32_t wbuf_len; struct jffs2_inodirty *wbuf_inodes; - struct rw_semaphore wbuf_sem; /* Protects the write buffer */ - /* Information about out-of-band area usage... */ - struct nand_ecclayout *ecclayout; - uint32_t badblock_pos; - uint32_t fsdata_pos; - uint32_t fsdata_len; + unsigned char *oobbuf; + int oobavail; /* How many bytes are available for JFFS2 in OOB */ #endif struct jffs2_summary *summary; /* Summary information */ diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 3af746e..31c1475 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -450,16 +450,20 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo #ifdef CONFIG_JFFS2_FS_WRITEBUFFER if (jffs2_cleanmarker_oob(c)) { - int ret = jffs2_check_nand_cleanmarker(c, jeb); + int ret; + + if (c->mtd->block_isbad(c->mtd, jeb->offset)) + return BLK_STATE_BADBLOCK; + + ret = jffs2_check_nand_cleanmarker(c, jeb); D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); + /* Even if it's not found, we still scan to see if the block is empty. We use this information to decide whether to erase it or not. */ switch (ret) { case 0: cleanmarkerfound = 1; break; case 1: break; - case 2: return BLK_STATE_BADBLOCK; - case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ default: return ret; } } diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index 9c99859..de718e3 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -957,43 +957,48 @@ exit: return ret; } -#define NR_OOB_SCAN_PAGES 4 +#define NR_OOB_SCAN_PAGES 4 + +/* For historical reasons we use only 12 bytes for OOB clean marker */ +#define OOB_CM_SIZE 12 + +static const struct jffs2_unknown_node oob_cleanmarker = +{ + .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), + .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), + .totlen = cpu_to_je32(8) +}; /* - * Check, if the out of band area is empty + * Check, if the out of band area is empty. This function knows about the clean + * marker and if it is present in OOB, treats the OOB as empty anyway. */ int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) { - int i, page, ret; - int oobsize = c->mtd->oobsize; + int i, ret; + int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); struct mtd_oob_ops ops; - ops.ooblen = NR_OOB_SCAN_PAGES * oobsize; + ops.mode = MTD_OOB_AUTO; + ops.ooblen = NR_OOB_SCAN_PAGES * c->oobavail; ops.oobbuf = c->oobbuf; - ops.ooboffs = 0; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); - if (ret) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " - "failed %d for block at %08x\n", ret, jeb->offset)); + if (ret || ops.oobretlen != ops.ooblen) { + printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd" + " bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); + if (!ret) + ret = -EIO; return ret; } - if (ops.oobretlen < ops.ooblen) { - D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB " - "returned short read (%zd bytes not %d) for block " - "at %08x\n", ops.oobretlen, ops.ooblen, jeb->offset)); - return -EIO; - } - - /* Special check for first page */ - for(i = 0; i < oobsize ; i++) { - /* Yeah, we know about the cleanmarker. */ - if (mode && i >= c->fsdata_pos && - i < c->fsdata_pos + c->fsdata_len) + for(i = 0; i < ops.ooblen; i++) { + if (mode && i < cmlen) + /* Yeah, we know about the cleanmarker */ continue; if (ops.oobbuf[i] != 0xFF) { @@ -1003,111 +1008,63 @@ int jffs2_check_oob_empty(struct jffs2_sb_info *c, } } - /* we know, we are aligned :) */ - for (page = oobsize; page < ops.ooblen; page += sizeof(long)) { - long dat = *(long *)(&ops.oobbuf[page]); - if(dat != -1) - return 1; - } return 0; } /* - * Scan for a valid cleanmarker and for bad blocks + * Check for a valid cleanmarker. + * Returns: 0 if a valid cleanmarker was found + * 1 if no cleanmarker was found + * negative error code if an error occurred */ -int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, - struct jffs2_eraseblock *jeb) +int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, + struct jffs2_eraseblock *jeb) { - struct jffs2_unknown_node n; struct mtd_oob_ops ops; - int oobsize = c->mtd->oobsize; - unsigned char *p,*b; - int i, ret; - size_t offset = jeb->offset; - - /* Check first if the block is bad. */ - if (c->mtd->block_isbad(c->mtd, offset)) { - D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker()" - ": Bad block at %08x\n", jeb->offset)); - return 2; - } + int ret, cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); - ops.ooblen = oobsize; + ops.mode = MTD_OOB_AUTO; + ops.ooblen = cmlen; ops.oobbuf = c->oobbuf; - ops.ooboffs = 0; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; - ret = c->mtd->read_oob(c->mtd, offset, &ops); - if (ret) { - D1 (printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " - "Read OOB failed %d for block at %08x\n", - ret, jeb->offset)); + ret = c->mtd->read_oob(c->mtd, jeb->offset, &ops); + if (ret || ops.oobretlen != ops.ooblen) { + printk(KERN_ERR "cannot read OOB for EB at %08x, requested %zd" + " bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); + if (!ret) + ret = -EIO; return ret; } - if (ops.oobretlen < ops.ooblen) { - D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): " - "Read OOB return short read (%zd bytes not %d) " - "for block at %08x\n", ops.oobretlen, ops.ooblen, - jeb->offset)); - return -EIO; - } - - n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); - n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); - n.totlen = cpu_to_je32 (8); - p = (unsigned char *) &n; - b = c->oobbuf + c->fsdata_pos; - - for (i = c->fsdata_len; i; i--) { - if (*b++ != *p++) - ret = 1; - } - - D1(if (ret == 1) { - printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): " - "Cleanmarker node not detected in block at %08x\n", - offset); - printk(KERN_WARNING "OOB at %08zx was ", offset); - for (i=0; i < oobsize; i++) - printk("%02x ", c->oobbuf[i]); - printk("\n"); - }); - return ret; + return !!memcmp(&oob_cleanmarker, c->oobbuf, cmlen); } int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { - struct jffs2_unknown_node n; - int ret; + int ret; struct mtd_oob_ops ops; + int cmlen = min_t(int, c->oobavail, OOB_CM_SIZE); - n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); - n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); - n.totlen = cpu_to_je32(8); - - ops.ooblen = c->fsdata_len; - ops.oobbuf = (uint8_t *)&n; - ops.ooboffs = c->fsdata_pos; + ops.mode = MTD_OOB_AUTO; + ops.ooblen = cmlen; + ops.oobbuf = (uint8_t *)&oob_cleanmarker; + ops.len = ops.ooboffs = ops.retlen = ops.oobretlen = 0; ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; ret = c->mtd->write_oob(c->mtd, jeb->offset, &ops); - - if (ret) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " - "Write failed for block at %08x: error %d\n", - jeb->offset, ret)); + if (ret || ops.oobretlen != ops.ooblen) { + printk(KERN_ERR "cannot write OOB for EB at %08x, requested %zd" + " bytes, read %zd bytes, error %d\n", + jeb->offset, ops.ooblen, ops.oobretlen, ret); + if (!ret) + ret = -EIO; return ret; } - if (ops.oobretlen != ops.ooblen) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): " - "Short write for block at %08x: %zd not %d\n", - jeb->offset, ops.oobretlen, ops.ooblen)); - return -EIO; - } + return 0; } @@ -1140,41 +1097,24 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock * return 1; } -static int jffs2_nand_set_oobinfo(struct jffs2_sb_info *c) +int jffs2_nand_flash_setup(struct jffs2_sb_info *c) { struct nand_ecclayout *oinfo = c->mtd->ecclayout; - /* Do this only, if we have an oob buffer */ if (!c->mtd->oobsize) return 0; /* Cleanmarker is out-of-band, so inline size zero */ c->cleanmarker_size = 0; - /* Should we use autoplacement ? */ - if (!oinfo) { - D1(printk(KERN_DEBUG "JFFS2 on NAND. No autoplacment info found\n")); + if (!oinfo || oinfo->oobavail == 0) { + printk(KERN_ERR "inconsistent device description\n"); return -EINVAL; } - D1(printk(KERN_DEBUG "JFFS2 using autoplace on NAND\n")); - /* Get the position of the free bytes */ - if (!oinfo->oobfree[0].length) { - printk (KERN_WARNING "jffs2_nand_set_oobinfo(): Eeep." - " Autoplacement selected and no empty space in oob\n"); - return -ENOSPC; - } - c->fsdata_pos = oinfo->oobfree[0].offset; - c->fsdata_len = oinfo->oobfree[0].length; - if (c->fsdata_len > 8) - c->fsdata_len = 8; + D1(printk(KERN_DEBUG "JFFS2 using OOB on NAND\n")); - return 0; -} - -int jffs2_nand_flash_setup(struct jffs2_sb_info *c) -{ - int res; + c->oobavail = oinfo->oobavail; /* Initialise write buffer */ init_rwsem(&c->wbuf_sem); @@ -1185,22 +1125,13 @@ int jffs2_nand_flash_setup(struct jffs2_sb_info *c) if (!c->wbuf) return -ENOMEM; - c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->mtd->oobsize, GFP_KERNEL); - if (!c->oobbuf) - return -ENOMEM; - - res = jffs2_nand_set_oobinfo(c); - -#ifdef BREAKME - if (!brokenbuf) - brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); - if (!brokenbuf) { + c->oobbuf = kmalloc(NR_OOB_SCAN_PAGES * c->oobavail, GFP_KERNEL); + if (!c->oobbuf) { kfree(c->wbuf); return -ENOMEM; } - memset(brokenbuf, 0xdb, c->wbuf_pagesize); -#endif - return res; + + return 0; } void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) diff --git a/include/linux/mtd/bbm.h b/include/linux/mtd/bbm.h index 1221b7c..fff8c53 100644 --- a/include/linux/mtd/bbm.h +++ b/include/linux/mtd/bbm.h @@ -92,6 +92,13 @@ struct nand_bbt_descr { */ #define ONENAND_BADBLOCK_POS 0 +/* + * Bad block scanning errors + */ +#define ONENAND_BBT_READ_ERROR 1 +#define ONENAND_BBT_READ_ECC_ERROR 2 +#define ONENAND_BBT_READ_FATAL_ERROR 4 + /** * struct bbm_info - [GENERIC] Bad Block Table data structure * @bbt_erase_shift: [INTERN] number of address bits in a bbt entry diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index 28d461d..81f3a31 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -183,7 +183,7 @@ typedef union { struct map_info { char *name; unsigned long size; - unsigned long phys; + resource_size_t phys; #define NO_XIP (-1UL) void __iomem *virt; diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index d644e57..6a8570b 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -85,6 +85,10 @@ typedef enum { * mode = MTD_OOB_PLACE) * @datbuf: data buffer - if NULL only oob data are read/written * @oobbuf: oob data buffer + * + * Note, it is allowed to read more then one OOB area at one go, but not write. + * The interface assumes that the OOB write requests program only one page's + * OOB area. */ struct mtd_oob_ops { mtd_oob_mode_t mode; @@ -117,18 +121,6 @@ struct mtd_info { u_int32_t writesize; u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) - u_int32_t ecctype; - u_int32_t eccsize; - - /* - * Reuse some of the above unused fields in the case of NOR flash - * with configurable programming regions to avoid modifying the - * user visible structure layout/size. Only valid when the - * MTD_PROGRAM_REGIONS flag is set. - * (Maybe we should have an union for those?) - */ -#define MTD_PROGREGION_CTRLMODE_VALID(mtd) (mtd)->oobsize -#define MTD_PROGREGION_CTRLMODE_INVALID(mtd) (mtd)->ecctype // Kernel-only stuff starts here. char *name; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 2071b02..9752388 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -343,6 +343,7 @@ struct nand_buffers { * @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 + * @cellinfo: [INTERN] MLC/multichip data from chip ident * @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 diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index f775a7a..d8af8a9 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -1,7 +1,7 @@ /* * linux/include/linux/mtd/onenand.h * - * Copyright (C) 2005-2006 Samsung Electronics + * Copyright (C) 2005-2007 Samsung Electronics * Kyungmin Park <kyungmin.park@samsung.com> * * This program is free software; you can redistribute it and/or modify @@ -42,14 +42,10 @@ typedef enum { /** * struct onenand_bufferram - OneNAND BufferRAM Data - * @block: block address in BufferRAM - * @page: page address in BufferRAM - * @valid: valid flag + * @blockpage: block & page address in BufferRAM */ struct onenand_bufferram { - int block; - int page; - int valid; + int blockpage; }; /** @@ -63,7 +59,6 @@ struct onenand_bufferram { * partly be set to inform onenand_scan about * @erase_shift: [INTERN] number of address bits in a block * @page_shift: [INTERN] number of address bits in a page - * @ppb_shift: [INTERN] number of address bits in a pages per block * @page_mask: [INTERN] a page per block mask * @bufferram_index: [INTERN] BufferRAM index * @bufferram: [INTERN] BufferRAM info @@ -103,7 +98,6 @@ struct onenand_chip { unsigned int erase_shift; unsigned int page_shift; - unsigned int ppb_shift; /* Pages per block shift */ unsigned int page_mask; unsigned int bufferram_index; @@ -150,6 +144,9 @@ struct onenand_chip { #define ONENAND_SET_SYS_CFG1(v, this) \ (this->write_word(v, this->base + ONENAND_REG_SYS_CFG1)) +#define ONENAND_IS_DDP(this) \ + (this->device_id & ONENAND_DEVICE_IS_DDP) + /* Check byte access in OneNAND */ #define ONENAND_CHECK_BYTE_ACCESS(addr) (addr & 0x1) diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index e31c8f5..af94719 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -3,7 +3,8 @@ * * OneNAND Register header file * - * Copyright (C) 2005-2006 Samsung Electronics + * Copyright (C) 2005-2007 Samsung Electronics + * Kyungmin Park <kyungmin.park@samsung.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 @@ -80,9 +81,11 @@ #define ONENAND_VERSION_PROCESS_SHIFT (8) /* - * Start Address 1 F100h (R/W) + * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W) */ #define ONENAND_DDP_SHIFT (15) +#define ONENAND_DDP_CHIP0 (0) +#define ONENAND_DDP_CHIP1 (1 << ONENAND_DDP_SHIFT) /* * Start Address 8 F107h (R/W) diff --git a/include/linux/mtd/physmap.h b/include/linux/mtd/physmap.h index 86831e3..0dc07d5 100644 --- a/include/linux/mtd/physmap.h +++ b/include/linux/mtd/physmap.h @@ -18,9 +18,10 @@ #define __LINUX_MTD_PHYSMAP__ #include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> #include <linux/mtd/partitions.h> +struct map_info; + struct physmap_flash_data { unsigned int width; void (*set_vpp)(struct map_info *, int); diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index f913c30..8e501a7 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -36,12 +36,6 @@ struct mtd_oob_buf { #define MTD_CAP_NORFLASH (MTD_WRITEABLE | MTD_BIT_WRITEABLE) #define MTD_CAP_NANDFLASH (MTD_WRITEABLE) - -// Types of automatic ECC/Checksum available -#define MTD_ECC_NONE 0 // No automatic ECC available -#define MTD_ECC_RS_DiskOnChip 1 // Automatic ECC on DiskOnChip -#define MTD_ECC_SW 2 // SW ECC for Toshiba & Samsung devices - /* ECC byte placement */ #define MTD_NANDECC_OFF 0 // Switch off ECC (Not recommended) #define MTD_NANDECC_PLACE 1 // Use the given placement in the structure (YAFFS1 legacy mode) @@ -61,6 +55,8 @@ struct mtd_info_user { uint32_t erasesize; uint32_t writesize; uint32_t oobsize; // Amount of OOB data per block (e.g. 16) + /* The below two fields are obsolete and broken, do not use them + * (TODO: remove at some point) */ uint32_t ecctype; uint32_t eccsize; }; |