From d694846b6b1c92bcc946b6ffb0a5ea25d5df1014 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 5 Apr 2009 07:38:33 -0700 Subject: [MTD] set blkdev parent to the mtd device, not its parent Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index a49a9c8f2cb1..8773481a30c2 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -286,7 +286,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) gd->private_data = new; new->blkcore_priv = gd; gd->queue = tr->blkcore_priv->rq; - gd->driverfs_dev = new->mtd->dev.parent; + gd->driverfs_dev = &new->mtd->dev; if (new->readonly) set_disk_ro(gd, 1); -- cgit v1.2.3 From 15bce40cb3133bcc07d548013df97e4653d363c1 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 5 Apr 2009 07:40:58 -0700 Subject: [MTD] Restore suspend/resume support for mtd devices This is intended to suspend/resume the _chip_, while we leave board drivers to handle their own suspend/resume for the controller. Signed-off-by: David Woodhouse --- drivers/mtd/mtdcore.c | 47 +++++++++++++++++++++++++++++++++++++---------- 1 file changed, 37 insertions(+), 10 deletions(-) diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index bccb4b1ffc46..fac54a3fa3f1 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -23,8 +23,15 @@ #include "mtdcore.h" - -static struct class *mtd_class; +static int mtd_cls_suspend(struct device *dev, pm_message_t state); +static int mtd_cls_resume(struct device *dev); + +static struct class mtd_class = { + .name = "mtd", + .owner = THIS_MODULE, + .suspend = mtd_cls_suspend, + .resume = mtd_cls_resume, +}; /* These are exported solely for the purpose of mtd_blkdevs.c. You should not use them for _anything_ else */ @@ -52,7 +59,26 @@ static void mtd_release(struct device *dev) /* remove /dev/mtdXro node if needed */ if (index) - device_destroy(mtd_class, index + 1); + device_destroy(&mtd_class, index + 1); +} + +static int mtd_cls_suspend(struct device *dev, pm_message_t state) +{ + struct mtd_info *mtd = dev_to_mtd(dev); + + if (mtd->suspend) + return mtd->suspend(mtd); + else + return 0; +} + +static int mtd_cls_resume(struct device *dev) +{ + struct mtd_info *mtd = dev_to_mtd(dev); + + if (mtd->resume) + mtd->resume(mtd); + return 0; } static ssize_t mtd_type_show(struct device *dev, @@ -269,7 +295,7 @@ int add_mtd_device(struct mtd_info *mtd) * physical device. */ mtd->dev.type = &mtd_devtype; - mtd->dev.class = mtd_class; + mtd->dev.class = &mtd_class; mtd->dev.devt = MTD_DEVT(i); dev_set_name(&mtd->dev, "mtd%d", i); if (device_register(&mtd->dev) != 0) { @@ -278,7 +304,7 @@ int add_mtd_device(struct mtd_info *mtd) } if (MTD_DEVT(i)) - device_create(mtd_class, mtd->dev.parent, + device_create(&mtd_class, mtd->dev.parent, MTD_DEVT(i) + 1, NULL, "mtd%dro", i); @@ -604,11 +630,12 @@ done: static int __init init_mtd(void) { - mtd_class = class_create(THIS_MODULE, "mtd"); + int ret; + ret = class_register(&mtd_class); - if (IS_ERR(mtd_class)) { - pr_err("Error creating mtd class.\n"); - return PTR_ERR(mtd_class); + if (ret) { + pr_err("Error registering mtd class: %d\n", ret); + return ret; } #ifdef CONFIG_PROC_FS if ((proc_mtd = create_proc_entry( "mtd", 0, NULL ))) @@ -623,7 +650,7 @@ static void __exit cleanup_mtd(void) if (proc_mtd) remove_proc_entry( "mtd", NULL); #endif /* CONFIG_PROC_FS */ - class_destroy(mtd_class); + class_unregister(&mtd_class); } module_init(init_mtd); -- cgit v1.2.3 From ccd93854d44710adaa02cecf0ef5f24ab383dd20 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 5 Apr 2009 07:49:48 -0700 Subject: [MTD] Remove mtd->{suspend,resume} calls from board drivers Now the MTD core will do this for us, we don't need to hook it up from the board drivers. Shame we can't do shutdown from the class too... Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap.c | 40 --------------------------------------- drivers/mtd/maps/pxa2xx-flash.c | 22 --------------------- drivers/mtd/maps/rbtx4939-flash.c | 23 ---------------------- drivers/mtd/maps/sa1100-flash.c | 23 ---------------------- drivers/mtd/nand/mxc_nand.c | 5 ----- 5 files changed, 113 deletions(-) diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 29a901157352..380648e9051a 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -195,42 +195,6 @@ err_out: } #ifdef CONFIG_PM -static int physmap_flash_suspend(struct platform_device *dev, pm_message_t state) -{ - struct physmap_flash_info *info = platform_get_drvdata(dev); - int ret = 0; - int i; - - for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) - if (info->mtd[i]->suspend) { - ret = info->mtd[i]->suspend(info->mtd[i]); - if (ret) - goto fail; - } - - return 0; -fail: - for (--i; i >= 0; --i) - if (info->mtd[i]->suspend) { - BUG_ON(!info->mtd[i]->resume); - info->mtd[i]->resume(info->mtd[i]); - } - - return ret; -} - -static int physmap_flash_resume(struct platform_device *dev) -{ - struct physmap_flash_info *info = platform_get_drvdata(dev); - int i; - - for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++) - if (info->mtd[i]->resume) - info->mtd[i]->resume(info->mtd[i]); - - return 0; -} - static void physmap_flash_shutdown(struct platform_device *dev) { struct physmap_flash_info *info = platform_get_drvdata(dev); @@ -242,16 +206,12 @@ static void physmap_flash_shutdown(struct platform_device *dev) info->mtd[i]->resume(info->mtd[i]); } #else -#define physmap_flash_suspend NULL -#define physmap_flash_resume NULL #define physmap_flash_shutdown NULL #endif static struct platform_driver physmap_flash_driver = { .probe = physmap_flash_probe, .remove = physmap_flash_remove, - .suspend = physmap_flash_suspend, - .resume = physmap_flash_resume, .shutdown = physmap_flash_shutdown, .driver = { .name = "physmap-flash", diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 572d32fdf38a..643aa06b599e 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -140,24 +140,6 @@ static int __devexit pxa2xx_flash_remove(struct platform_device *dev) } #ifdef CONFIG_PM -static int pxa2xx_flash_suspend(struct platform_device *dev, pm_message_t state) -{ - struct pxa2xx_flash_info *info = platform_get_drvdata(dev); - int ret = 0; - - if (info->mtd && info->mtd->suspend) - ret = info->mtd->suspend(info->mtd); - return ret; -} - -static int pxa2xx_flash_resume(struct platform_device *dev) -{ - struct pxa2xx_flash_info *info = platform_get_drvdata(dev); - - if (info->mtd && info->mtd->resume) - info->mtd->resume(info->mtd); - return 0; -} static void pxa2xx_flash_shutdown(struct platform_device *dev) { struct pxa2xx_flash_info *info = platform_get_drvdata(dev); @@ -166,8 +148,6 @@ static void pxa2xx_flash_shutdown(struct platform_device *dev) info->mtd->resume(info->mtd); } #else -#define pxa2xx_flash_suspend NULL -#define pxa2xx_flash_resume NULL #define pxa2xx_flash_shutdown NULL #endif @@ -178,8 +158,6 @@ static struct platform_driver pxa2xx_flash_driver = { }, .probe = pxa2xx_flash_probe, .remove = __devexit_p(pxa2xx_flash_remove), - .suspend = pxa2xx_flash_suspend, - .resume = pxa2xx_flash_resume, .shutdown = pxa2xx_flash_shutdown, }; diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c index d39f0adac846..83ed64512c5e 100644 --- a/drivers/mtd/maps/rbtx4939-flash.c +++ b/drivers/mtd/maps/rbtx4939-flash.c @@ -145,25 +145,6 @@ err_out: } #ifdef CONFIG_PM -static int rbtx4939_flash_suspend(struct platform_device *dev, - pm_message_t state) -{ - struct rbtx4939_flash_info *info = platform_get_drvdata(dev); - - if (info->mtd->suspend) - return info->mtd->suspend(info->mtd); - return 0; -} - -static int rbtx4939_flash_resume(struct platform_device *dev) -{ - struct rbtx4939_flash_info *info = platform_get_drvdata(dev); - - if (info->mtd->resume) - info->mtd->resume(info->mtd); - return 0; -} - static void rbtx4939_flash_shutdown(struct platform_device *dev) { struct rbtx4939_flash_info *info = platform_get_drvdata(dev); @@ -173,16 +154,12 @@ static void rbtx4939_flash_shutdown(struct platform_device *dev) info->mtd->resume(info->mtd); } #else -#define rbtx4939_flash_suspend NULL -#define rbtx4939_flash_resume NULL #define rbtx4939_flash_shutdown NULL #endif static struct platform_driver rbtx4939_flash_driver = { .probe = rbtx4939_flash_probe, .remove = rbtx4939_flash_remove, - .suspend = rbtx4939_flash_suspend, - .resume = rbtx4939_flash_resume, .shutdown = rbtx4939_flash_shutdown, .driver = { .name = "rbtx4939-flash", diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index 05e9362dc7f0..c6210f5118d1 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -415,25 +415,6 @@ static int __exit sa1100_mtd_remove(struct platform_device *pdev) } #ifdef CONFIG_PM -static int sa1100_mtd_suspend(struct platform_device *dev, pm_message_t state) -{ - struct sa_info *info = platform_get_drvdata(dev); - int ret = 0; - - if (info) - ret = info->mtd->suspend(info->mtd); - - return ret; -} - -static int sa1100_mtd_resume(struct platform_device *dev) -{ - struct sa_info *info = platform_get_drvdata(dev); - if (info) - info->mtd->resume(info->mtd); - return 0; -} - static void sa1100_mtd_shutdown(struct platform_device *dev) { struct sa_info *info = platform_get_drvdata(dev); @@ -441,16 +422,12 @@ static void sa1100_mtd_shutdown(struct platform_device *dev) info->mtd->resume(info->mtd); } #else -#define sa1100_mtd_suspend NULL -#define sa1100_mtd_resume NULL #define sa1100_mtd_shutdown NULL #endif static struct platform_driver sa1100_mtd_driver = { .probe = sa1100_mtd_probe, .remove = __exit_p(sa1100_mtd_remove), - .suspend = sa1100_mtd_suspend, - .resume = sa1100_mtd_resume, .shutdown = sa1100_mtd_shutdown, .driver = { .name = "sa1100-mtd", diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index f3548d048014..65040de54bb7 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -1015,8 +1015,6 @@ static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) int ret = 0; DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); - if (info) - ret = info->suspend(info); /* Disable the NFC clock */ clk_disable(nfc_clk); /* FIXME */ @@ -1033,9 +1031,6 @@ static int mxcnd_resume(struct platform_device *pdev) /* Enable the NFC clock */ clk_enable(nfc_clk); /* FIXME */ - if (info) - info->resume(info); - return ret; } -- cgit v1.2.3 From 4704a78472cd5c58f6b4c4f8c04d32de2da3f20a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 5 Apr 2009 07:56:23 -0700 Subject: [MTD] Only set partition suspend/resume method if parent not registered Signed-off-by: David Woodhouse --- drivers/mtd/mtdpart.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 29675edb44b4..63d1cd2c17be 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -395,7 +395,7 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->mtd.get_fact_prot_info = part_get_fact_prot_info; if (master->sync) slave->mtd.sync = part_sync; - if (!partno && master->suspend && master->resume) { + if (!partno && !master->dev.class && master->suspend && master->resume) { slave->mtd.suspend = part_suspend; slave->mtd.resume = part_resume; } -- cgit v1.2.3 From b90cf6681f4f6263920616e7ca2fd09130e4143a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Sun, 5 Apr 2009 08:23:44 -0700 Subject: [MTD] Remove option for add_mtd_partitions() to not register partitions. This breaks the dilnetpc map driver, but it could be fixed not to use that option. We want to simplify the partition handling, and this is a step towards that. Remove superfluous 'index' field from private struct mtd_part too, while we're at it. Signed-off-by: David Woodhouse --- drivers/mtd/mtdpart.c | 18 ++++-------------- include/linux/mtd/partitions.h | 1 - 2 files changed, 4 insertions(+), 15 deletions(-) diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index 63d1cd2c17be..349fcbe5cc0f 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -27,9 +27,7 @@ struct mtd_part { struct mtd_info mtd; struct mtd_info *master; uint64_t offset; - int index; struct list_head list; - int registered; }; /* @@ -321,8 +319,7 @@ int del_mtd_partitions(struct mtd_info *master) list_for_each_entry_safe(slave, next, &mtd_partitions, list) if (slave->master == master) { list_del(&slave->list); - if (slave->registered) - del_mtd_device(&slave->mtd); + del_mtd_device(&slave->mtd); kfree(slave); } @@ -412,7 +409,6 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, slave->mtd.erase = part_erase; slave->master = master; slave->offset = part->offset; - slave->index = partno; if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; @@ -500,15 +496,9 @@ static struct mtd_part *add_one_partition(struct mtd_info *master, } out_register: - if (part->mtdp) { - /* store the object pointer (caller may or may not register it*/ - *part->mtdp = &slave->mtd; - slave->registered = 0; - } else { - /* register our partition */ - add_mtd_device(&slave->mtd); - slave->registered = 1; - } + /* register our partition */ + add_mtd_device(&slave->mtd); + return slave; } diff --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 7535a74083b9..af6dcb992bc3 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -40,7 +40,6 @@ struct mtd_partition { uint64_t offset; /* offset within the master MTD space */ uint32_t mask_flags; /* master MTD flags to mask out for this partition */ struct nand_ecclayout *ecclayout; /* out of band layout for this partition (NAND only)*/ - struct mtd_info **mtdp; /* pointer to store the MTD object */ }; #define MTDPART_OFS_NXTBLK (-2) -- cgit v1.2.3 From 8d42b524f4323cdf49872fe5e3a0e824f32feb51 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 29 May 2009 13:57:13 +0100 Subject: mtd: DIL/NetPC broken for now We'll fix it up again, but for now I don't think anyone really cares. Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 82923bd2d9c5..cdd54cb4d224 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -270,7 +270,7 @@ config MTD_ALCHEMY config MTD_DILNETPC tristate "CFI Flash device mapped on DIL/Net PC" - depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT + depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN help MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP". For details, see -- cgit v1.2.3 From 9fd1e8f92ad17b3bc94245fee9b4e4bfea0dba0e Mon Sep 17 00:00:00 2001 From: Catalin Marinas Date: Thu, 23 Apr 2009 17:41:10 +0100 Subject: mtd: Add armflash support for multiple blocks of flash This patch adds MTD concatenation support to integrator-flash.c for platforms with more than one block of flash memory (e.g. RealView PB11MPCore). The implementation is based on the sa1100-flash.c one. Signed-off-by: Catalin Marinas Acked-by: Russell King Signed-off-by: David Woodhouse --- drivers/mtd/maps/integrator-flash.c | 226 ++++++++++++++++++++++++++---------- 1 file changed, 164 insertions(+), 62 deletions(-) diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index c9681a339a59..b08a798ee254 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -36,27 +36,33 @@ #include #include #include +#include #include #include #include -#ifdef CONFIG_ARCH_P720T -#define FLASH_BASE (0x04000000) -#define FLASH_SIZE (64*1024*1024) -#endif +#define SUBDEV_NAME_SIZE (BUS_ID_SIZE + 2) -struct armflash_info { +struct armflash_subdev_info { + char name[SUBDEV_NAME_SIZE]; + struct mtd_info *mtd; + struct map_info map; struct flash_platform_data *plat; +}; + +struct armflash_info { struct resource *res; struct mtd_partition *parts; struct mtd_info *mtd; - struct map_info map; + int nr_subdev; + struct armflash_subdev_info subdev[0]; }; static void armflash_set_vpp(struct map_info *map, int on) { - struct armflash_info *info = container_of(map, struct armflash_info, map); + struct armflash_subdev_info *info = + container_of(map, struct armflash_subdev_info, map); if (info->plat && info->plat->set_vpp) info->plat->set_vpp(on); @@ -64,32 +70,17 @@ static void armflash_set_vpp(struct map_info *map, int on) static const char *probes[] = { "cmdlinepart", "RedBoot", "afs", NULL }; -static int armflash_probe(struct platform_device *dev) +static int armflash_subdev_probe(struct armflash_subdev_info *subdev, + struct resource *res) { - struct flash_platform_data *plat = dev->dev.platform_data; - struct resource *res = dev->resource; - unsigned int size = res->end - res->start + 1; - struct armflash_info *info; - int err; + struct flash_platform_data *plat = subdev->plat; + resource_size_t size = res->end - res->start + 1; void __iomem *base; + int err = 0; - info = kzalloc(sizeof(struct armflash_info), GFP_KERNEL); - if (!info) { - err = -ENOMEM; - goto out; - } - - info->plat = plat; - if (plat && plat->init) { - err = plat->init(); - if (err) - goto no_resource; - } - - info->res = request_mem_region(res->start, size, "armflash"); - if (!info->res) { + if (!request_mem_region(res->start, size, subdev->name)) { err = -EBUSY; - goto no_resource; + goto out; } base = ioremap(res->start, size); @@ -101,27 +92,132 @@ static int armflash_probe(struct platform_device *dev) /* * look for CFI based flash parts fitted to this board */ - info->map.size = size; - info->map.bankwidth = plat->width; - info->map.phys = res->start; - info->map.virt = base; - info->map.name = dev_name(&dev->dev); - info->map.set_vpp = armflash_set_vpp; + subdev->map.size = size; + subdev->map.bankwidth = plat->width; + subdev->map.phys = res->start; + subdev->map.virt = base; + subdev->map.name = subdev->name; + subdev->map.set_vpp = armflash_set_vpp; - simple_map_init(&info->map); + simple_map_init(&subdev->map); /* * Also, the CFI layer automatically works out what size * of chips we have, and does the necessary identification * for us automatically. */ - info->mtd = do_map_probe(plat->map_name, &info->map); - if (!info->mtd) { + subdev->mtd = do_map_probe(plat->map_name, &subdev->map); + if (!subdev->mtd) { err = -ENXIO; goto no_device; } - info->mtd->owner = THIS_MODULE; + subdev->mtd->owner = THIS_MODULE; + + /* Successful? */ + if (err == 0) + return err; + + if (subdev->mtd) + map_destroy(subdev->mtd); + no_device: + iounmap(base); + no_mem: + release_mem_region(res->start, size); + out: + return err; +} + +static void armflash_subdev_remove(struct armflash_subdev_info *subdev) +{ + if (subdev->mtd) + map_destroy(subdev->mtd); + if (subdev->map.virt) + iounmap(subdev->map.virt); + release_mem_region(subdev->map.phys, subdev->map.size); +} + +static int armflash_probe(struct platform_device *dev) +{ + struct flash_platform_data *plat = dev->dev.platform_data; + unsigned int size; + struct armflash_info *info; + int i, nr, err; + + /* Count the number of devices */ + for (nr = 0; ; nr++) + if (!platform_get_resource(dev, IORESOURCE_MEM, nr)) + break; + if (nr == 0) { + err = -ENODEV; + goto out; + } + + size = sizeof(struct armflash_info) + + sizeof(struct armflash_subdev_info) * nr; + info = kzalloc(size, GFP_KERNEL); + if (!info) { + err = -ENOMEM; + goto out; + } + + if (plat && plat->init) { + err = plat->init(); + if (err) + goto no_resource; + } + + for (i = 0; i < nr; i++) { + struct armflash_subdev_info *subdev = &info->subdev[i]; + struct resource *res; + + res = platform_get_resource(dev, IORESOURCE_MEM, i); + if (!res) + break; + + if (nr == 1) + /* No MTD concatenation, just use the default name */ + snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s", + dev_name(&dev->dev)); + else + snprintf(subdev->name, SUBDEV_NAME_SIZE, "%s-%d", + dev_name(&dev->dev), i); + subdev->plat = plat; + + err = armflash_subdev_probe(subdev, res); + if (err) + break; + } + info->nr_subdev = i; + + if (err) + goto subdev_err; + + if (info->nr_subdev == 1) + info->mtd = info->subdev[0].mtd; + else if (info->nr_subdev > 1) { +#ifdef CONFIG_MTD_CONCAT + struct mtd_info *cdev[info->nr_subdev]; + + /* + * We detected multiple devices. Concatenate them together. + */ + for (i = 0; i < info->nr_subdev; i++) + cdev[i] = info->subdev[i].mtd; + + info->mtd = mtd_concat_create(cdev, info->nr_subdev, + dev_name(&dev->dev)); + if (info->mtd == NULL) + err = -ENXIO; +#else + printk(KERN_ERR "armflash: multiple devices found but " + "MTD concat support disabled.\n"); + err = -ENXIO; +#endif + } + + if (err < 0) + goto cleanup; err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); if (err > 0) { @@ -131,28 +227,30 @@ static int armflash_probe(struct platform_device *dev) "mtd partition registration failed: %d\n", err); } - if (err == 0) + if (err == 0) { platform_set_drvdata(dev, info); + return err; + } /* - * If we got an error, free all resources. + * We got an error, free all resources. */ - if (err < 0) { - if (info->mtd) { - del_mtd_partitions(info->mtd); - map_destroy(info->mtd); - } - kfree(info->parts); - - no_device: - iounmap(base); - no_mem: - release_mem_region(res->start, size); - no_resource: - if (plat && plat->exit) - plat->exit(); - kfree(info); + cleanup: + if (info->mtd) { + del_mtd_partitions(info->mtd); +#ifdef CONFIG_MTD_CONCAT + if (info->mtd != info->subdev[0].mtd) + mtd_concat_destroy(info->mtd); +#endif } + kfree(info->parts); + subdev_err: + for (i = info->nr_subdev - 1; i >= 0; i--) + armflash_subdev_remove(&info->subdev[i]); + no_resource: + if (plat && plat->exit) + plat->exit(); + kfree(info); out: return err; } @@ -160,22 +258,26 @@ static int armflash_probe(struct platform_device *dev) static int armflash_remove(struct platform_device *dev) { struct armflash_info *info = platform_get_drvdata(dev); + struct flash_platform_data *plat = dev->dev.platform_data; + int i; platform_set_drvdata(dev, NULL); if (info) { if (info->mtd) { del_mtd_partitions(info->mtd); - map_destroy(info->mtd); +#ifdef CONFIG_MTD_CONCAT + if (info->mtd != info->subdev[0].mtd) + mtd_concat_destroy(info->mtd); +#endif } kfree(info->parts); - iounmap(info->map.virt); - release_resource(info->res); - kfree(info->res); + for (i = info->nr_subdev - 1; i >= 0; i--) + armflash_subdev_remove(&info->subdev[i]); - if (info->plat && info->plat->exit) - info->plat->exit(); + if (plat && plat->exit) + plat->exit(); kfree(info); } -- cgit v1.2.3 From 81933046ef2a615031c46171013bde2c5225ee69 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 29 May 2009 14:26:23 +0100 Subject: mtd: Fix handling of mtdname in txx9ndfmc.c As pointed out by Kay Sievers, the name size limit is gone from the driver-core, and BUS_ID_SIZE is obsolescent. Rather than just papering over the problem by replacing the mtdname array size with an arbitrary '20 + 2', fix the problem properly and handle arbitrary name sizes. Signed-off-by: David Woodhouse --- drivers/mtd/nand/txx9ndfmc.c | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 812479264896..5f919e63b29b 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -64,7 +64,7 @@ struct txx9ndfmc_priv { struct nand_chip chip; struct mtd_info mtd; int cs; - char mtdname[BUS_ID_SIZE + 2]; + const char *mtdname; }; #define MAX_TXX9NDFMC_DEV 4 @@ -334,11 +334,17 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) if (plat->ch_mask != 1) { txx9_priv->cs = i; - sprintf(txx9_priv->mtdname, "%s.%u", - dev_name(&dev->dev), i); + txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u", + dev_name(&dev->dev), i); + if (!txx9_priv->mtdname) { + kfree(txx9_priv); + dev_err(&dev->dev, + "Unable to allocate TXx9 NDFMC MTD device name.\n"); + continue; + } } else { txx9_priv->cs = -1; - strcpy(txx9_priv->mtdname, dev_name(&dev->dev)); + txx9_priv->mtdname = dev_name(&dev->dev); } if (plat->wide_mask & (1 << i)) chip->options |= NAND_BUSWIDTH_16; @@ -385,6 +391,8 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) kfree(drvdata->parts[i]); #endif del_mtd_device(mtd); + if (txx9_priv->mtdname != dev_name(&dev->dev)) + kfree(txx9_priv->mtdname); kfree(txx9_priv); } return 0; -- cgit v1.2.3 From 19fe7f1a00023d2aa97617655b7ea56eb72f4db8 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 8 Apr 2009 22:51:43 -0700 Subject: Documentation: add MTD sysfs docs Signed-off-by: Kevin Cernekee Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- Documentation/ABI/testing/sysfs-class-mtd | 125 ++++++++++++++++++++++++++++++ 1 file changed, 125 insertions(+) create mode 100644 Documentation/ABI/testing/sysfs-class-mtd diff --git a/Documentation/ABI/testing/sysfs-class-mtd b/Documentation/ABI/testing/sysfs-class-mtd new file mode 100644 index 000000000000..4d55a1888981 --- /dev/null +++ b/Documentation/ABI/testing/sysfs-class-mtd @@ -0,0 +1,125 @@ +What: /sys/class/mtd/ +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + The mtd/ class subdirectory belongs to the MTD subsystem + (MTD core). + +What: /sys/class/mtd/mtdX/ +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + The /sys/class/mtd/mtd{0,1,2,3,...} directories correspond + to each /dev/mtdX character device. These may represent + physical/simulated flash devices, partitions on a flash + device, or concatenated flash devices. They exist regardless + of whether CONFIG_MTD_CHAR is actually enabled. + +What: /sys/class/mtd/mtdXro/ +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + These directories provide the corresponding read-only device + nodes for /sys/class/mtd/mtdX/ . They are only created + (for the benefit of udev) if CONFIG_MTD_CHAR is enabled. + +What: /sys/class/mtd/mtdX/dev +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + Major and minor numbers of the character device corresponding + to this MTD device (in : format). This is the + read-write device so will be even. + +What: /sys/class/mtd/mtdXro/dev +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + Major and minor numbers of the character device corresponding + to the read-only variant of thie MTD device (in + : format). In this case will be odd. + +What: /sys/class/mtd/mtdX/erasesize +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + "Major" erase size for the device. If numeraseregions is + zero, this is the eraseblock size for the entire device. + Otherwise, the MEMGETREGIONCOUNT/MEMGETREGIONINFO ioctls + can be used to determine the actual eraseblock layout. + +What: /sys/class/mtd/mtdX/flags +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + A hexadecimal value representing the device flags, ORed + together: + + 0x0400: MTD_WRITEABLE - device is writable + 0x0800: MTD_BIT_WRITEABLE - single bits can be flipped + 0x1000: MTD_NO_ERASE - no erase necessary + 0x2000: MTD_POWERUP_LOCK - always locked after reset + +What: /sys/class/mtd/mtdX/name +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + A human-readable ASCII name for the device or partition. + This will match the name in /proc/mtd . + +What: /sys/class/mtd/mtdX/numeraseregions +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + For devices that have variable eraseblock sizes, this + provides the total number of erase regions. Otherwise, + it will read back as zero. + +What: /sys/class/mtd/mtdX/oobsize +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + Number of OOB bytes per page. + +What: /sys/class/mtd/mtdX/size +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + Total size of the device/partition, in bytes. + +What: /sys/class/mtd/mtdX/type +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + One of the following ASCII strings, representing the device + type: + + absent, ram, rom, nor, nand, dataflash, ubi, unknown + +What: /sys/class/mtd/mtdX/writesize +Date: April 2009 +KernelVersion: 2.6.29 +Contact: linux-mtd@lists.infradead.org +Description: + Minimal writable flash unit size. This will always be + a positive integer. + + In the case of NOR flash it is 1 (even though individual + bits can be cleared). + + In the case of NAND flash it is one NAND page (or a + half page, or a quarter page). + + In the case of ECC NOR, it is the ECC block size. -- cgit v1.2.3 From 0dc54e9f33e2fbcea28356bc2c8c931cb307d3b3 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 8 Apr 2009 22:52:28 -0700 Subject: mtd: add MEMERASE64 ioctl for >4GiB devices New MEMERASE/MEMREADOOB/MEMWRITEOOB ioctls are needed in order to support 64-bit offsets into large NAND flash devices. Signed-off-by: Kevin Cernekee Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 29 +++++++++++++++++++++-------- fs/compat_ioctl.c | 1 + include/mtd/mtd-abi.h | 6 ++++++ 3 files changed, 28 insertions(+), 8 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 763d3f0a1f42..ad4b8618977d 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -417,6 +417,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; case MEMERASE: + case MEMERASE64: { struct erase_info *erase; @@ -427,20 +428,32 @@ static int mtd_ioctl(struct inode *inode, struct file *file, if (!erase) ret = -ENOMEM; else { - struct erase_info_user einfo; - wait_queue_head_t waitq; DECLARE_WAITQUEUE(wait, current); init_waitqueue_head(&waitq); - if (copy_from_user(&einfo, argp, - sizeof(struct erase_info_user))) { - kfree(erase); - return -EFAULT; + if (cmd == MEMERASE64) { + struct erase_info_user64 einfo64; + + if (copy_from_user(&einfo64, argp, + sizeof(struct erase_info_user64))) { + kfree(erase); + return -EFAULT; + } + erase->addr = einfo64.start; + erase->len = einfo64.length; + } else { + struct erase_info_user einfo32; + + if (copy_from_user(&einfo32, argp, + sizeof(struct erase_info_user))) { + kfree(erase); + return -EFAULT; + } + erase->addr = einfo32.start; + erase->len = einfo32.length; } - erase->addr = einfo.start; - erase->len = einfo.length; erase->mtd = mtd; erase->callback = mtdchar_erase_callback; erase->priv = (unsigned long)&waitq; diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index b83f6bcfa51a..c603ca2c223a 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -2441,6 +2441,7 @@ COMPATIBLE_IOCTL(MEMGETREGIONCOUNT) COMPATIBLE_IOCTL(MEMGETREGIONINFO) COMPATIBLE_IOCTL(MEMGETBADBLOCK) COMPATIBLE_IOCTL(MEMSETBADBLOCK) +COMPATIBLE_IOCTL(MEMERASE64) /* NBD */ ULONG_IOCTL(NBD_SET_SOCK) ULONG_IOCTL(NBD_SET_BLKSIZE) diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index b6595b3c68b6..2e32be1e3a1e 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -12,6 +12,11 @@ struct erase_info_user { __u32 length; }; +struct erase_info_user64 { + __u64 start; + __u64 length; +}; + struct mtd_oob_buf { __u32 start; __u32 length; @@ -95,6 +100,7 @@ struct otp_info { #define ECCGETLAYOUT _IOR('M', 17, struct nand_ecclayout) #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) #define MTDFILEMODE _IO('M', 19) +#define MEMERASE64 _IOW('M', 20, struct erase_info_user64) /* * Obsolete legacy interface. Keep it in order not to break userspace -- cgit v1.2.3 From 977185404046afb31d2e18fac0a076de1a20bf0e Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 8 Apr 2009 22:53:13 -0700 Subject: mtd: compat_ioctl cleanup 1) Move the MEMREADOOB/MEMWRITEOOB compat_ioctl wrappers from fs/compat_ioctl.c into mtdchar.c . Original request was here: http://lkml.org/lkml/2009/4/1/295 2) Add missing COMPATIBLE_IOCTL lines, so that mtd-utils does not error out when running in 64/32 compatibility mode. LKML-Reference: <200904011650.22928.arnd@arndb.de> Signed-off-by: Kevin Cernekee Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 255 +++++++++++++++++++++++++++++++++----------------- fs/compat_ioctl.c | 51 ++-------- 2 files changed, 180 insertions(+), 126 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index ad4b8618977d..51bb0b092003 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -14,6 +14,7 @@ #include #include #include +#include #include #include @@ -355,6 +356,100 @@ static int otp_select_filemode(struct mtd_file_info *mfi, int mode) # define otp_select_filemode(f,m) -EOPNOTSUPP #endif +static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd, + uint64_t start, uint32_t length, void __user *ptr, + uint32_t __user *retp) +{ + struct mtd_oob_ops ops; + uint32_t retlen; + int ret = 0; + + if (!(file->f_mode & FMODE_WRITE)) + return -EPERM; + + if (length > 4096) + return -EINVAL; + + if (!mtd->write_oob) + ret = -EOPNOTSUPP; + else + ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT; + + if (ret) + return ret; + + ops.ooblen = length; + ops.ooboffs = start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(length, GFP_KERNEL); + if (!ops.oobbuf) + return -ENOMEM; + + if (copy_from_user(ops.oobbuf, ptr, length)) { + kfree(ops.oobbuf); + return -EFAULT; + } + + start &= ~((uint64_t)mtd->oobsize - 1); + ret = mtd->write_oob(mtd, start, &ops); + + if (ops.oobretlen > 0xFFFFFFFFU) + ret = -EOVERFLOW; + retlen = ops.oobretlen; + if (copy_to_user(retp, &retlen, sizeof(length))) + ret = -EFAULT; + + kfree(ops.oobbuf); + return ret; +} + +static int mtd_do_readoob(struct mtd_info *mtd, uint64_t start, + uint32_t length, void __user *ptr, uint32_t __user *retp) +{ + struct mtd_oob_ops ops; + int ret = 0; + + if (length > 4096) + return -EINVAL; + + if (!mtd->read_oob) + ret = -EOPNOTSUPP; + else + ret = access_ok(VERIFY_WRITE, ptr, + length) ? 0 : -EFAULT; + if (ret) + return ret; + + ops.ooblen = length; + ops.ooboffs = start & (mtd->oobsize - 1); + ops.datbuf = NULL; + ops.mode = MTD_OOB_PLACE; + + if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) + return -EINVAL; + + ops.oobbuf = kmalloc(length, GFP_KERNEL); + if (!ops.oobbuf) + return -ENOMEM; + + start &= ~((uint64_t)mtd->oobsize - 1); + ret = mtd->read_oob(mtd, start, &ops); + + if (put_user(ops.oobretlen, retp)) + ret = -EFAULT; + else if (ops.oobretlen && copy_to_user(ptr, ops.oobbuf, + ops.oobretlen)) + ret = -EFAULT; + + kfree(ops.oobbuf); + return ret; +} + static int mtd_ioctl(struct inode *inode, struct file *file, u_int cmd, u_long arg) { @@ -487,100 +582,28 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case MEMWRITEOOB: { struct mtd_oob_buf buf; - struct mtd_oob_ops ops; - struct mtd_oob_buf __user *user_buf = argp; - uint32_t retlen; - - if(!(file->f_mode & FMODE_WRITE)) - return -EPERM; - - if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) - return -EFAULT; - - if (buf.length > 4096) - return -EINVAL; - - if (!mtd->write_oob) - ret = -EOPNOTSUPP; - else - ret = access_ok(VERIFY_READ, buf.ptr, - buf.length) ? 0 : EFAULT; - - if (ret) - return ret; - - ops.ooblen = buf.length; - ops.ooboffs = buf.start & (mtd->oobsize - 1); - ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; - - if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) - return -EINVAL; - - ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); - if (!ops.oobbuf) - return -ENOMEM; - - if (copy_from_user(ops.oobbuf, buf.ptr, buf.length)) { - kfree(ops.oobbuf); - return -EFAULT; - } - - buf.start &= ~(mtd->oobsize - 1); - ret = mtd->write_oob(mtd, buf.start, &ops); + struct mtd_oob_buf __user *buf_user = argp; - if (ops.oobretlen > 0xFFFFFFFFU) - ret = -EOVERFLOW; - retlen = ops.oobretlen; - if (copy_to_user(&user_buf->length, &retlen, sizeof(buf.length))) + /* NOTE: writes return length to buf_user->length */ + if (copy_from_user(&buf, argp, sizeof(buf))) ret = -EFAULT; - - kfree(ops.oobbuf); + else + ret = mtd_do_writeoob(file, mtd, buf.start, buf.length, + buf.ptr, &buf_user->length); break; - } case MEMREADOOB: { struct mtd_oob_buf buf; - struct mtd_oob_ops ops; - - if (copy_from_user(&buf, argp, sizeof(struct mtd_oob_buf))) - return -EFAULT; - - if (buf.length > 4096) - return -EINVAL; - - if (!mtd->read_oob) - ret = -EOPNOTSUPP; - else - ret = access_ok(VERIFY_WRITE, buf.ptr, - buf.length) ? 0 : -EFAULT; - if (ret) - return ret; - - ops.ooblen = buf.length; - ops.ooboffs = buf.start & (mtd->oobsize - 1); - ops.datbuf = NULL; - ops.mode = MTD_OOB_PLACE; + struct mtd_oob_buf __user *buf_user = argp; - if (ops.ooboffs && ops.ooblen > (mtd->oobsize - ops.ooboffs)) - return -EINVAL; - - ops.oobbuf = kmalloc(buf.length, GFP_KERNEL); - if (!ops.oobbuf) - return -ENOMEM; - - buf.start &= ~(mtd->oobsize - 1); - ret = mtd->read_oob(mtd, buf.start, &ops); - - if (put_user(ops.oobretlen, (uint32_t __user *)argp)) - ret = -EFAULT; - else if (ops.oobretlen && copy_to_user(buf.ptr, ops.oobbuf, - ops.oobretlen)) + /* NOTE: writes return length to buf_user->start */ + if (copy_from_user(&buf, argp, sizeof(buf))) ret = -EFAULT; - - kfree(ops.oobbuf); + else + ret = mtd_do_readoob(mtd, buf.start, buf.length, + buf.ptr, &buf_user->start); break; } @@ -771,6 +794,67 @@ static int mtd_ioctl(struct inode *inode, struct file *file, return ret; } /* memory_ioctl */ +#ifdef CONFIG_COMPAT + +struct mtd_oob_buf32 { + u_int32_t start; + u_int32_t length; + compat_caddr_t ptr; /* unsigned char* */ +}; + +#define MEMWRITEOOB32 _IOWR('M', 3, struct mtd_oob_buf32) +#define MEMREADOOB32 _IOWR('M', 4, struct mtd_oob_buf32) + +static long mtd_compat_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + struct mtd_file_info *mfi = file->private_data; + struct mtd_info *mtd = mfi->mtd; + void __user *argp = (void __user *)arg; + int ret = 0; + + lock_kernel(); + + switch (cmd) { + case MEMWRITEOOB32: + { + struct mtd_oob_buf32 buf; + struct mtd_oob_buf32 __user *buf_user = argp; + + if (copy_from_user(&buf, argp, sizeof(buf))) + ret = -EFAULT; + else + ret = mtd_do_writeoob(file, mtd, buf.start, + buf.length, compat_ptr(buf.ptr), + &buf_user->length); + break; + } + + case MEMREADOOB32: + { + struct mtd_oob_buf32 buf; + struct mtd_oob_buf32 __user *buf_user = argp; + + /* NOTE: writes return length to buf->start */ + if (copy_from_user(&buf, argp, sizeof(buf))) + ret = -EFAULT; + else + ret = mtd_do_readoob(mtd, buf.start, + buf.length, compat_ptr(buf.ptr), + &buf_user->start); + break; + } + default: + ret = -ENOIOCTLCMD; + } + + unlock_kernel(); + + return ret; +} + +#endif /* CONFIG_COMPAT */ + /* * try to determine where a shared mapping can be made * - only supported for NOMMU at the moment (MMU can't doesn't copy private @@ -830,6 +914,9 @@ static const struct file_operations mtd_fops = { .read = mtd_read, .write = mtd_write, .ioctl = mtd_ioctl, +#ifdef CONFIG_COMPAT + .compat_ioctl = mtd_compat_ioctl, +#endif .open = mtd_open, .release = mtd_close, .mmap = mtd_mmap, diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index c603ca2c223a..196397bff086 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -1411,46 +1411,6 @@ static int ioc_settimeout(unsigned int fd, unsigned int cmd, unsigned long arg) #define HIDPGETCONNLIST _IOR('H', 210, int) #define HIDPGETCONNINFO _IOR('H', 211, int) -struct mtd_oob_buf32 { - u_int32_t start; - u_int32_t length; - compat_caddr_t ptr; /* unsigned char* */ -}; - -#define MEMWRITEOOB32 _IOWR('M',3,struct mtd_oob_buf32) -#define MEMREADOOB32 _IOWR('M',4,struct mtd_oob_buf32) - -static int mtd_rw_oob(unsigned int fd, unsigned int cmd, unsigned long arg) -{ - struct mtd_oob_buf __user *buf = compat_alloc_user_space(sizeof(*buf)); - struct mtd_oob_buf32 __user *buf32 = compat_ptr(arg); - u32 data; - char __user *datap; - unsigned int real_cmd; - int err; - - real_cmd = (cmd == MEMREADOOB32) ? - MEMREADOOB : MEMWRITEOOB; - - if (copy_in_user(&buf->start, &buf32->start, - 2 * sizeof(u32)) || - get_user(data, &buf32->ptr)) - return -EFAULT; - datap = compat_ptr(data); - if (put_user(datap, &buf->ptr)) - return -EFAULT; - - err = sys_ioctl(fd, real_cmd, (unsigned long) buf); - - if (!err) { - if (copy_in_user(&buf32->start, &buf->start, - 2 * sizeof(u32))) - err = -EFAULT; - } - - return err; -} - #ifdef CONFIG_BLOCK struct raw32_config_request { @@ -2439,8 +2399,17 @@ COMPATIBLE_IOCTL(MEMLOCK) COMPATIBLE_IOCTL(MEMUNLOCK) COMPATIBLE_IOCTL(MEMGETREGIONCOUNT) COMPATIBLE_IOCTL(MEMGETREGIONINFO) +COMPATIBLE_IOCTL(MEMSETOOBSEL) +COMPATIBLE_IOCTL(MEMGETOOBSEL) COMPATIBLE_IOCTL(MEMGETBADBLOCK) COMPATIBLE_IOCTL(MEMSETBADBLOCK) +COMPATIBLE_IOCTL(OTPSELECT) +COMPATIBLE_IOCTL(OTPGETREGIONCOUNT) +COMPATIBLE_IOCTL(OTPGETREGIONINFO) +COMPATIBLE_IOCTL(OTPLOCK) +COMPATIBLE_IOCTL(ECCGETLAYOUT) +COMPATIBLE_IOCTL(ECCGETSTATS) +COMPATIBLE_IOCTL(MTDFILEMODE) COMPATIBLE_IOCTL(MEMERASE64) /* NBD */ ULONG_IOCTL(NBD_SET_SOCK) @@ -2551,8 +2520,6 @@ COMPATIBLE_IOCTL(JSIOCGBUTTONS) COMPATIBLE_IOCTL(JSIOCGNAME(0)) /* now things that need handlers */ -HANDLE_IOCTL(MEMREADOOB32, mtd_rw_oob) -HANDLE_IOCTL(MEMWRITEOOB32, mtd_rw_oob) #ifdef CONFIG_NET HANDLE_IOCTL(SIOCGIFNAME, dev_ifname32) HANDLE_IOCTL(SIOCGIFCONF, dev_ifconf) -- cgit v1.2.3 From aea7cea9fa9e39e71f95ad70b3daf98ba9972587 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Wed, 8 Apr 2009 22:53:49 -0700 Subject: mtd: add OOB ioctls for >4GiB devices Signed-off-by: Kevin Cernekee Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 28 ++++++++++++++++++++++++++++ fs/compat_ioctl.c | 2 ++ include/mtd/mtd-abi.h | 9 +++++++++ 3 files changed, 39 insertions(+) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 51bb0b092003..99d1fbc95011 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -607,6 +607,34 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; } + case MEMWRITEOOB64: + { + struct mtd_oob_buf64 buf; + struct mtd_oob_buf64 __user *buf_user = argp; + + if (copy_from_user(&buf, argp, sizeof(buf))) + ret = -EFAULT; + else + ret = mtd_do_writeoob(file, mtd, buf.start, buf.length, + (void __user *)(uintptr_t)buf.usr_ptr, + &buf_user->length); + break; + } + + case MEMREADOOB64: + { + struct mtd_oob_buf64 buf; + struct mtd_oob_buf64 __user *buf_user = argp; + + if (copy_from_user(&buf, argp, sizeof(buf))) + ret = -EFAULT; + else + ret = mtd_do_readoob(mtd, buf.start, buf.length, + (void __user *)(uintptr_t)buf.usr_ptr, + &buf_user->length); + break; + } + case MEMLOCK: { struct erase_info_user einfo; diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 196397bff086..8da222eacbaf 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -2411,6 +2411,8 @@ COMPATIBLE_IOCTL(ECCGETLAYOUT) COMPATIBLE_IOCTL(ECCGETSTATS) COMPATIBLE_IOCTL(MTDFILEMODE) COMPATIBLE_IOCTL(MEMERASE64) +COMPATIBLE_IOCTL(MEMREADOOB64) +COMPATIBLE_IOCTL(MEMWRITEOOB64) /* NBD */ ULONG_IOCTL(NBD_SET_SOCK) ULONG_IOCTL(NBD_SET_BLKSIZE) diff --git a/include/mtd/mtd-abi.h b/include/mtd/mtd-abi.h index 2e32be1e3a1e..be51ae2bd0ff 100644 --- a/include/mtd/mtd-abi.h +++ b/include/mtd/mtd-abi.h @@ -23,6 +23,13 @@ struct mtd_oob_buf { unsigned char __user *ptr; }; +struct mtd_oob_buf64 { + __u64 start; + __u32 pad; + __u32 length; + __u64 usr_ptr; +}; + #define MTD_ABSENT 0 #define MTD_RAM 1 #define MTD_ROM 2 @@ -101,6 +108,8 @@ struct otp_info { #define ECCGETSTATS _IOR('M', 18, struct mtd_ecc_stats) #define MTDFILEMODE _IO('M', 19) #define MEMERASE64 _IOW('M', 20, struct erase_info_user64) +#define MEMWRITEOOB64 _IOWR('M', 21, struct mtd_oob_buf64) +#define MEMREADOOB64 _IOWR('M', 22, struct mtd_oob_buf64) /* * Obsolete legacy interface. Keep it in order not to break userspace -- cgit v1.2.3 From 668ff9ab45d595222d3f90d7974ccba3518e3bb3 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 14 Apr 2009 21:59:22 -0700 Subject: mtd: Handle compat ioctls directly; remove all trace from compat_ioctl.c Remove all references to MTD ioctls from fs/compat_ioctl.c and let them all be handled by mtd_compat_ioctl(). Signed-off-by: Kevin Cernekee Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 3 ++- fs/compat_ioctl.c | 22 ---------------------- 2 files changed, 2 insertions(+), 23 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 99d1fbc95011..5fff04f3303d 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -836,6 +836,7 @@ struct mtd_oob_buf32 { static long mtd_compat_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { + struct inode *inode = file->f_path.dentry->d_inode; struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; void __user *argp = (void __user *)arg; @@ -873,7 +874,7 @@ static long mtd_compat_ioctl(struct file *file, unsigned int cmd, break; } default: - ret = -ENOIOCTLCMD; + ret = mtd_ioctl(inode, file, cmd, arg); } unlock_kernel(); diff --git a/fs/compat_ioctl.c b/fs/compat_ioctl.c index 8da222eacbaf..aa6ba39ff370 100644 --- a/fs/compat_ioctl.c +++ b/fs/compat_ioctl.c @@ -94,7 +94,6 @@ #include #include #include -#include #include #include @@ -2392,27 +2391,6 @@ COMPATIBLE_IOCTL(USBDEVFS_SUBMITURB32) COMPATIBLE_IOCTL(USBDEVFS_REAPURB32) COMPATIBLE_IOCTL(USBDEVFS_REAPURBNDELAY32) COMPATIBLE_IOCTL(USBDEVFS_CLEAR_HALT) -/* MTD */ -COMPATIBLE_IOCTL(MEMGETINFO) -COMPATIBLE_IOCTL(MEMERASE) -COMPATIBLE_IOCTL(MEMLOCK) -COMPATIBLE_IOCTL(MEMUNLOCK) -COMPATIBLE_IOCTL(MEMGETREGIONCOUNT) -COMPATIBLE_IOCTL(MEMGETREGIONINFO) -COMPATIBLE_IOCTL(MEMSETOOBSEL) -COMPATIBLE_IOCTL(MEMGETOOBSEL) -COMPATIBLE_IOCTL(MEMGETBADBLOCK) -COMPATIBLE_IOCTL(MEMSETBADBLOCK) -COMPATIBLE_IOCTL(OTPSELECT) -COMPATIBLE_IOCTL(OTPGETREGIONCOUNT) -COMPATIBLE_IOCTL(OTPGETREGIONINFO) -COMPATIBLE_IOCTL(OTPLOCK) -COMPATIBLE_IOCTL(ECCGETLAYOUT) -COMPATIBLE_IOCTL(ECCGETSTATS) -COMPATIBLE_IOCTL(MTDFILEMODE) -COMPATIBLE_IOCTL(MEMERASE64) -COMPATIBLE_IOCTL(MEMREADOOB64) -COMPATIBLE_IOCTL(MEMWRITEOOB64) /* NBD */ ULONG_IOCTL(NBD_SET_SOCK) ULONG_IOCTL(NBD_SET_BLKSIZE) -- cgit v1.2.3 From 0b6585ce05f169f10ce74329e87bd8c5070b4bb9 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 29 May 2009 16:09:08 +0100 Subject: mtd: Fix pointer handling in compat ioctls to use compat_ptr() Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5fff04f3303d..5b081cb84351 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -839,7 +839,7 @@ static long mtd_compat_ioctl(struct file *file, unsigned int cmd, struct inode *inode = file->f_path.dentry->d_inode; struct mtd_file_info *mfi = file->private_data; struct mtd_info *mtd = mfi->mtd; - void __user *argp = (void __user *)arg; + void __user *argp = compat_ptr(arg); int ret = 0; lock_kernel(); @@ -874,7 +874,7 @@ static long mtd_compat_ioctl(struct file *file, unsigned int cmd, break; } default: - ret = mtd_ioctl(inode, file, cmd, arg); + ret = mtd_ioctl(inode, file, cmd, (unsigned long)argp); } unlock_kernel(); -- cgit v1.2.3 From ec0482e6cfbd460bc69a9073ffbef4c2f3422fdf Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sat, 30 May 2009 16:55:29 +0100 Subject: [MTD] [NAND] S3C2410: Move to using platform device table Commit 57fee4a58fe802272742caae248872c392a60670 added an method to specify the platform device compatibility by using an id-table instead of registering multiple drivers. Move the S3C24XX NAND driver to using this ID table. Signed-off-by: Ben Dooks CC: Eric Miao --- drivers/mtd/nand/s3c2410.c | 78 ++++++++++++++++------------------------------ 1 file changed, 26 insertions(+), 52 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8e375d5fe231..b7f0740d842f 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -593,7 +593,7 @@ static inline void s3c2410_nand_cpufreq_deregister(struct s3c2410_nand_info *inf /* device management functions */ -static int s3c2410_nand_remove(struct platform_device *pdev) +static int s3c24xx_nand_remove(struct platform_device *pdev) { struct s3c2410_nand_info *info = to_nand_info(pdev); @@ -788,18 +788,17 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, } } -/* s3c2410_nand_probe +/* s3c24xx_nand_probe * * called by device layer when it finds a device matching * one our driver can handled. This code checks to see if * it can allocate all necessary resources then calls the * nand layer to look for devices */ - -static int s3c24xx_nand_probe(struct platform_device *pdev, - enum s3c_cpu_type cpu_type) +static int s3c24xx_nand_probe(struct platform_device *pdev) { struct s3c2410_platform_nand *plat = to_nand_plat(pdev); + enum s3c_cpu_type cpu_type; struct s3c2410_nand_info *info; struct s3c2410_nand_mtd *nmtd; struct s3c2410_nand_set *sets; @@ -809,6 +808,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, int nr_sets; int setno; + cpu_type = platform_get_device_id(pdev)->driver_data; + pr_debug("s3c2410_nand_probe(%p)\n", pdev); info = kmalloc(sizeof(*info), GFP_KERNEL); @@ -922,7 +923,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev, return 0; exit_error: - s3c2410_nand_remove(pdev); + s3c24xx_nand_remove(pdev); if (err == 0) err = -EINVAL; @@ -983,50 +984,30 @@ static int s3c24xx_nand_resume(struct platform_device *dev) /* driver device registration */ -static int s3c2410_nand_probe(struct platform_device *dev) -{ - return s3c24xx_nand_probe(dev, TYPE_S3C2410); -} - -static int s3c2440_nand_probe(struct platform_device *dev) -{ - return s3c24xx_nand_probe(dev, TYPE_S3C2440); -} - -static int s3c2412_nand_probe(struct platform_device *dev) -{ - return s3c24xx_nand_probe(dev, TYPE_S3C2412); -} - -static struct platform_driver s3c2410_nand_driver = { - .probe = s3c2410_nand_probe, - .remove = s3c2410_nand_remove, - .suspend = s3c24xx_nand_suspend, - .resume = s3c24xx_nand_resume, - .driver = { - .name = "s3c2410-nand", - .owner = THIS_MODULE, +static struct platform_device_id s3c24xx_driver_ids[] = { + { + .name = "s3c2410-nand", + .driver_data = TYPE_S3C2410, + }, { + .name = "s3c2440-nand", + .driver_data = TYPE_S3C2440, + }, { + .name = "s3c2412-nand", + .driver_data = TYPE_S3C2412, }, + { } }; -static struct platform_driver s3c2440_nand_driver = { - .probe = s3c2440_nand_probe, - .remove = s3c2410_nand_remove, - .suspend = s3c24xx_nand_suspend, - .resume = s3c24xx_nand_resume, - .driver = { - .name = "s3c2440-nand", - .owner = THIS_MODULE, - }, -}; +MODULE_DEVICE_TABLE(platform, s3c24xx_driver_ids); -static struct platform_driver s3c2412_nand_driver = { - .probe = s3c2412_nand_probe, - .remove = s3c2410_nand_remove, +static struct platform_driver s3c24xx_nand_driver = { + .probe = s3c24xx_nand_probe, + .remove = s3c24xx_nand_remove, .suspend = s3c24xx_nand_suspend, .resume = s3c24xx_nand_resume, + .id_table = s3c24xx_driver_ids, .driver = { - .name = "s3c2412-nand", + .name = "s3c24xx-nand", .owner = THIS_MODULE, }, }; @@ -1035,16 +1016,12 @@ static int __init s3c2410_nand_init(void) { printk("S3C24XX NAND Driver, (c) 2004 Simtec Electronics\n"); - platform_driver_register(&s3c2412_nand_driver); - platform_driver_register(&s3c2440_nand_driver); - return platform_driver_register(&s3c2410_nand_driver); + return platform_driver_register(&s3c24xx_nand_driver); } static void __exit s3c2410_nand_exit(void) { - platform_driver_unregister(&s3c2412_nand_driver); - platform_driver_unregister(&s3c2440_nand_driver); - platform_driver_unregister(&s3c2410_nand_driver); + platform_driver_unregister(&s3c24xx_nand_driver); } module_init(s3c2410_nand_init); @@ -1053,6 +1030,3 @@ module_exit(s3c2410_nand_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Ben Dooks "); MODULE_DESCRIPTION("S3C24XX MTD NAND driver"); -MODULE_ALIAS("platform:s3c2410-nand"); -MODULE_ALIAS("platform:s3c2412-nand"); -MODULE_ALIAS("platform:s3c2440-nand"); -- cgit v1.2.3 From 3db72151aa4c246f8bdb8b3501972e1f1b32fe0d Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sat, 30 May 2009 17:18:15 +0100 Subject: [MTD] [NAND] S3C2410: Basic kerneldoc comment updates Move to using kerneldoc style commenting in the driver Signed-off-by: Ben Dooks --- drivers/mtd/nand/s3c2410.c | 89 ++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 78 insertions(+), 11 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index b7f0740d842f..a2d1c70c5227 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -74,6 +74,14 @@ static struct nand_ecclayout nand_hw_eccoob = { struct s3c2410_nand_info; +/** + * struct s3c2410_nand_mtd - driver MTD structure + * @mtd: The MTD instance to pass to the MTD layer. + * @chip: The NAND chip information. + * @set: The platform information supplied for this set of NAND chips. + * @info: Link back to the hardware information. + * @scan_res: The result from calling nand_scan_ident(). +*/ struct s3c2410_nand_mtd { struct mtd_info mtd; struct nand_chip chip; @@ -90,6 +98,21 @@ enum s3c_cpu_type { /* overview of the s3c2410 nand state */ +/** + * struct s3c2410_nand_info - NAND controller state. + * @mtds: An array of MTD instances on this controoler. + * @platform: The platform data for this board. + * @device: The platform device we bound to. + * @area: The IO area resource that came from request_mem_region(). + * @clk: The clock resource for this controller. + * @regs: The area mapped for the hardware registers described by @area. + * @sel_reg: Pointer to the register controlling the NAND selection. + * @sel_bit: The bit in @sel_reg to select the NAND chip. + * @mtd_count: The number of MTDs created from this controller. + * @save_sel: The contents of @sel_reg to be saved over suspend. + * @clk_rate: The clock rate from @clk. + * @cpu_type: The exact type of this controller. + */ struct s3c2410_nand_info { /* mtd info */ struct nand_hw_control controller; @@ -145,6 +168,14 @@ static inline int allow_clk_stop(struct s3c2410_nand_info *info) #define NS_IN_KHZ 1000000 +/** + * s3c_nand_calc_rate - calculate timing data. + * @wanted: The cycle time in nanoseconds. + * @clk: The clock rate in kHz. + * @max: The maximum divider value. + * + * Calculate the timing value from the given parameters. + */ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) { int result; @@ -169,6 +200,14 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) /* controller setup */ +/** + * s3c2410_nand_setrate - setup controller timing information. + * @info: The controller instance. + * + * Given the information supplied by the platform, calculate and set + * the necessary timing registers in the hardware to generate the + * necessary timing cycles to the hardware. + */ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) { struct s3c2410_platform_nand *plat = info->platform; @@ -245,6 +284,13 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) return 0; } +/** + * s3c2410_nand_inithw - basic hardware initialisation + * @info: The hardware state. + * + * Do the basic initialisation of the hardware, using s3c2410_nand_setrate() + * to setup the hardware access speeds and set the controller to be enabled. +*/ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) { int ret; @@ -268,8 +314,19 @@ static int s3c2410_nand_inithw(struct s3c2410_nand_info *info) return 0; } -/* select chip */ - +/** + * s3c2410_nand_select_chip - select the given nand chip + * @mtd: The MTD instance for this chip. + * @chip: The chip number. + * + * This is called by the MTD layer to either select a given chip for the + * @mtd instance, or to indicate that the access has finished and the + * chip can be de-selected. + * + * The routine ensures that the nFCE line is correctly setup, and any + * platform specific selection code is called to route nFCE to the specific + * chip. + */ static void s3c2410_nand_select_chip(struct mtd_info *mtd, int chip) { struct s3c2410_nand_info *info; @@ -667,11 +724,16 @@ static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, } #endif -/* s3c2410_nand_init_chip +/** + * s3c2410_nand_init_chip - initialise a single instance of an chip + * @info: The base NAND controller the chip is on. + * @nmtd: The new controller MTD instance to fill in. + * @set: The information passed from the board specific platform data. * - * init a single instance of an chip -*/ - + * Initialise the given @nmtd from the information in @info and @set. This + * readies the structure for use with the MTD layer functions by ensuring + * all pointers are setup and the necessary control routines selected. + */ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *nmtd, struct s3c2410_nand_set *set) @@ -759,12 +821,17 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, chip->ecc.mode = NAND_ECC_NONE; } -/* s3c2410_nand_update_chip +/** + * s3c2410_nand_update_chip - post probe update + * @info: The controller instance. + * @nmtd: The driver version of the MTD instance. * - * post-probe chip update, to change any items, such as the - * layout for large page nand - */ - + * This routine is called after the chip probe has succesfully completed + * and the relevant per-chip information updated. This call ensure that + * we update the internal state accordingly. + * + * The internal state is currently limited to the ECC state information. +*/ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *nmtd) { -- cgit v1.2.3 From 8c3e843d56f74889f3ff32202e82e3bc16d0d552 Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 10 May 2009 15:41:25 -0500 Subject: [MTD] [NAND] S3C2410: NAND ECC by chip rather than global This makes us take note about the chosen ECC mode per-chip and not the one set globally. Signed-off-by: Andy Green Signed-off-by: Nelson Castillo [ben-linux@fluff.org: andy@openmoko.com => andy@warmcat.com, rewrite subject] Signed-off-by: Ben Dooks --- drivers/mtd/nand/s3c2410.c | 34 +++++++++++++++++++++++++--------- 1 file changed, 25 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index a2d1c70c5227..daa4af918205 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -819,6 +819,21 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, if (set->disable_ecc) chip->ecc.mode = NAND_ECC_NONE; + + switch (chip->ecc.mode) { + case NAND_ECC_NONE: + dev_info(info->device, "NAND ECC disabled\n"); + break; + case NAND_ECC_SOFT: + dev_info(info->device, "NAND soft ECC\n"); + break; + case NAND_ECC_HW: + dev_info(info->device, "NAND hardware ECC\n"); + break; + default: + dev_info(info->device, "NAND ECC UNKNOWN\n"); + break; + } } /** @@ -840,18 +855,19 @@ static void s3c2410_nand_update_chip(struct s3c2410_nand_info *info, dev_dbg(info->device, "chip %p => page shift %d\n", chip, chip->page_shift); - if (hardware_ecc) { + if (chip->ecc.mode != NAND_ECC_HW) + return; + /* change the behaviour depending on wether we are using * the large or small page nand device */ - if (chip->page_shift > 10) { - chip->ecc.size = 256; - chip->ecc.bytes = 3; - } else { - chip->ecc.size = 512; - chip->ecc.bytes = 3; - chip->ecc.layout = &nand_hw_eccoob; - } + if (chip->page_shift > 10) { + chip->ecc.size = 256; + chip->ecc.bytes = 3; + } else { + chip->ecc.size = 512; + chip->ecc.bytes = 3; + chip->ecc.layout = &nand_hw_eccoob; } } -- cgit v1.2.3 From fb6ea3258742f41be8dd527ebd58a1a1e2cef23a Mon Sep 17 00:00:00 2001 From: Nelson Castillo Date: Sun, 10 May 2009 15:41:32 -0500 Subject: [MTD] [NAND] S3C2410: Small colon cleanup checkpatch would complain with "disable_ecc : 1". Signed-off-by: Nelson Castillo [ben-linux@fluff.org: subject cleanup] Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/include/plat/nand.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/arch/arm/plat-s3c/include/plat/nand.h b/arch/arm/plat-s3c/include/plat/nand.h index f4dcd14af059..985546750c6d 100644 --- a/arch/arm/plat-s3c/include/plat/nand.h +++ b/arch/arm/plat-s3c/include/plat/nand.h @@ -22,7 +22,7 @@ */ struct s3c2410_nand_set { - unsigned int disable_ecc : 1; + unsigned int disable_ecc:1; int nr_chips; int nr_partitions; @@ -39,7 +39,7 @@ struct s3c2410_platform_nand { int twrph0; /* active time for nWE/nOE */ int twrph1; /* time for release CLE/ALE from nWE/nOE inactive */ - unsigned int ignore_unset_ecc : 1; + unsigned int ignore_unset_ecc:1; int nr_sets; struct s3c2410_nand_set *sets; -- cgit v1.2.3 From 2612e523dc3695df319662ff279806a3d74de375 Mon Sep 17 00:00:00 2001 From: Nelson Castillo Date: Sun, 10 May 2009 15:41:54 -0500 Subject: [MTD] [NAND] S3C2410: Uninitialised variable cleanup ~ Avoid warning without generating code. (I don't even get the warning without the macro uninitialized_var). Signed-off-by: Nelson Castillo [ben-linux@fluff.org: subject cleanup] Signed-off-by: Ben Dooks --- drivers/mtd/nand/s3c2410.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index daa4af918205..7be3663df1e5 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -214,7 +214,7 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) int tacls_max = (info->cpu_type == TYPE_S3C2412) ? 8 : 4; int tacls, twrph0, twrph1; unsigned long clkrate = clk_get_rate(info->clk); - unsigned long set, cfg, mask; + unsigned long uninitialized_var(set), cfg, uninitialized_var(mask); unsigned long flags; /* calculate the timing information for the controller */ @@ -264,9 +264,6 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) break; default: - /* keep compiler happy */ - mask = 0; - set = 0; BUG(); } -- cgit v1.2.3 From ae7304e554642d57993b32265b817e6ae80787de Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 10 May 2009 15:42:02 -0500 Subject: [MTD] [NAND] S3C2410: Fix CFG debug order Fix NAND CFG debug order. Signed-off-by: Andy Green Signed-off-by: Nelson Castillo [ben-linux@fluff.org: Change andy@openmoko.com to andy@warmcat.com, subject cleanup] Signed-off-by: Ben Dooks --- drivers/mtd/nand/s3c2410.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 7be3663df1e5..87c40deb27bb 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -267,8 +267,6 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) BUG(); } - dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); - local_irq_save(flags); cfg = readl(info->regs + S3C2410_NFCONF); @@ -278,6 +276,8 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) local_irq_restore(flags); + dev_dbg(info->device, "NF_CONF is 0x%lx\n", cfg); + return 0; } -- cgit v1.2.3 From ed27f0287062236d50190d7447f6377ff4acdfad Mon Sep 17 00:00:00 2001 From: Andy Green Date: Sun, 10 May 2009 15:42:09 -0500 Subject: [MTD] [NAND] S3C2410: Allow commandline partition processing This patch allows commandline partition processing to work with the s3c2410 NAND platform driver. Signed-off-by: Andy Green Signed-off-by: Nelson Castillo [ben-linux@fluff.org: Change andy@openmoko.com to andy@warmcat.com] Signed-off-by: Ben Dooks --- drivers/mtd/nand/s3c2410.c | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 87c40deb27bb..ef5665258968 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -699,17 +699,31 @@ static int s3c24xx_nand_remove(struct platform_device *pdev) } #ifdef CONFIG_MTD_PARTITIONS +const char *part_probes[] = { "cmdlinepart", NULL }; static int s3c2410_nand_add_partition(struct s3c2410_nand_info *info, struct s3c2410_nand_mtd *mtd, struct s3c2410_nand_set *set) { + struct mtd_partition *part_info; + int nr_part = 0; + if (set == NULL) return add_mtd_device(&mtd->mtd); - if (set->nr_partitions > 0 && set->partitions != NULL) { - return add_mtd_partitions(&mtd->mtd, set->partitions, set->nr_partitions); + if (set->nr_partitions == 0) { + mtd->mtd.name = set->name; + nr_part = parse_mtd_partitions(&mtd->mtd, part_probes, + &part_info, 0); + } else { + if (set->nr_partitions > 0 && set->partitions != NULL) { + nr_part = set->nr_partitions; + part_info = set->partitions; + } } + if (nr_part > 0 && part_info) + return add_mtd_partitions(&mtd->mtd, part_info, nr_part); + return add_mtd_device(&mtd->mtd); } #else -- cgit v1.2.3 From 24b5ce20cc75603ce7c03a42116c30a17bce509a Mon Sep 17 00:00:00 2001 From: Thomas Chou Date: Tue, 21 Apr 2009 12:27:34 +0800 Subject: mtd: plat_nand: fix section error With CONFIG_HOTPLUG=n, the following eror occurred during link: local symbol 0: discarded in section `.devexit.text' from drivers/built-in.o It was caused by improper section reference. The __devexit_p() should be added to the .remove function. Signed-off-by: Thomas Chou Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 86e1d08eee00..28ffd4e8bb2f 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -128,7 +128,7 @@ static int __devexit plat_nand_remove(struct platform_device *pdev) static struct platform_driver plat_nand_driver = { .probe = plat_nand_probe, - .remove = plat_nand_remove, + .remove = __devexit_p(plat_nand_remove), .driver = { .name = "gen_nand", .owner = THIS_MODULE, -- cgit v1.2.3 From 24430abc88c67e3df2f06c96a6ccd73bda4c92f0 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 21 Apr 2009 11:02:01 +0300 Subject: Kill jffs2-user.h This file does not define any kernel-userspace API, all it does it defines few helpers for userspace. Instead, userspace should have a private copy of this file. The main (if not the only) user is the mtd-utils package, but it already has a private copy of this file. This patch also removes references to 'jffs2-user.h' from 'Kbuild' and MAINTAINERS' files. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- MAINTAINERS | 1 - include/mtd/Kbuild | 1 - include/mtd/jffs2-user.h | 34 ---------------------------------- 3 files changed, 36 deletions(-) delete mode 100644 include/mtd/jffs2-user.h diff --git a/MAINTAINERS b/MAINTAINERS index 2b349ba4add4..3276e1c256b8 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -3171,7 +3171,6 @@ W: http://www.linux-mtd.infradead.org/doc/jffs2.html S: Maintained F: fs/jffs2/ F: include/linux/jffs2.h -F: include/mtd/jffs2-user.h JOURNALLING LAYER FOR BLOCK DEVICES (JBD) P: Stephen Tweedie diff --git a/include/mtd/Kbuild b/include/mtd/Kbuild index 8eb018f96002..192f8fb7d546 100644 --- a/include/mtd/Kbuild +++ b/include/mtd/Kbuild @@ -1,5 +1,4 @@ header-y += inftl-user.h -header-y += jffs2-user.h header-y += mtd-abi.h header-y += mtd-user.h header-y += nftl-user.h diff --git a/include/mtd/jffs2-user.h b/include/mtd/jffs2-user.h deleted file mode 100644 index fa94b0eb67c1..000000000000 --- a/include/mtd/jffs2-user.h +++ /dev/null @@ -1,34 +0,0 @@ -/* - * JFFS2 definitions for use in user space only - */ - -#ifndef __JFFS2_USER_H__ -#define __JFFS2_USER_H__ - -/* This file is blessed for inclusion by userspace */ -#include -#include -#include -#include - -#undef cpu_to_je16 -#undef cpu_to_je32 -#undef cpu_to_jemode -#undef je16_to_cpu -#undef je32_to_cpu -#undef jemode_to_cpu - -extern int target_endian; - -#define t16(x) ({ __u16 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_16(__b); }) -#define t32(x) ({ __u32 __b = (x); (target_endian==__BYTE_ORDER)?__b:bswap_32(__b); }) - -#define cpu_to_je16(x) ((jint16_t){t16(x)}) -#define cpu_to_je32(x) ((jint32_t){t32(x)}) -#define cpu_to_jemode(x) ((jmode_t){t32(x)}) - -#define je16_to_cpu(x) (t16((x).v16)) -#define je32_to_cpu(x) (t32((x).v32)) -#define jemode_to_cpu(x) (t32((x).m)) - -#endif /* __JFFS2_USER_H__ */ -- cgit v1.2.3 From 81d19b04a865f9fcc0ca01b20be806169ff9efb3 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 21 Apr 2009 19:51:20 -0700 Subject: mtd: nand: don't walk past end of oobfree[] Resolve issue noted by Sneha: when computing oobavail from the list of free areas in the OOB, don't assume there will always be an unused slot at the end. With ECC_HW_SYNDROME and 4KiB page chips, it's fairly likely there *won't* be one. Signed-off-by: David Brownell Cc: "Narnakaje, Snehaprabha" " Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 3d7ed432fa41..8c21b89d2d0c 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2756,7 +2756,8 @@ int nand_scan_tail(struct mtd_info *mtd) * the out of band area */ chip->ecc.layout->oobavail = 0; - for (i = 0; chip->ecc.layout->oobfree[i].length; i++) + for (i = 0; chip->ecc.layout->oobfree[i].length + && i < ARRAY_SIZE(chip->ecc.layout->oobfree); i++) chip->ecc.layout->oobavail += chip->ecc.layout->oobfree[i].length; mtd->oobavail = chip->ecc.layout->oobavail; -- cgit v1.2.3 From 533a0149148ccaa0199a1ee6492cd860e3c8b456 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 21 Apr 2009 19:51:31 -0700 Subject: mtd: nand: minor davinci_nand cleanup Make the DaVinci NAND driver require platform_data with board-specific configuration. We can't actually do any kind of sane job of configuring it otherwise. Also fix the comment about picking the "best" ECC mode. We can't do those any more; that relied on knowing what kind of CPU we're using (they don't all support 4-bit ECC), and current policy is that drivers not have cpu_is_*() checks. Signed-off-by: David Brownell Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/davinci_nand.c | 27 +++++++++++---------------- 1 file changed, 11 insertions(+), 16 deletions(-) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 0119220de7d0..68b747584bc8 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -306,6 +306,10 @@ static int __init nand_davinci_probe(struct platform_device *pdev) uint32_t val; nand_ecc_modes_t ecc_mode; + /* insist on board-specific configuration */ + if (!pdata) + return -ENODEV; + /* which external chipselect will we be managing? */ if (pdev->id < 0 || pdev->id > 3) return -ENODEV; @@ -351,7 +355,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->chip.select_chip = nand_davinci_select_chip; /* options such as NAND_USE_FLASH_BBT or 16-bit widths */ - info->chip.options = pdata ? pdata->options : 0; + info->chip.options = pdata->options; info->ioaddr = (uint32_t __force) vaddr; @@ -360,14 +364,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->mask_chipsel = pdata->mask_chipsel; /* use nandboot-capable ALE/CLE masks by default */ - if (pdata && pdata->mask_ale) - info->mask_ale = pdata->mask_cle; - else - info->mask_ale = MASK_ALE; - if (pdata && pdata->mask_cle) - info->mask_cle = pdata->mask_cle; - else - info->mask_cle = MASK_CLE; + info->mask_ale = pdata->mask_cle ? : MASK_ALE; + info->mask_cle = pdata->mask_cle ? : MASK_CLE; /* Set address of hardware control function */ info->chip.cmd_ctrl = nand_davinci_hwcontrol; @@ -377,11 +375,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->chip.read_buf = nand_davinci_read_buf; info->chip.write_buf = nand_davinci_write_buf; - /* use board-specific ECC config; else, the best available */ - if (pdata) - ecc_mode = pdata->ecc_mode; - else - ecc_mode = NAND_ECC_HW; + /* Use board-specific ECC config */ + ecc_mode = pdata->ecc_mode; switch (ecc_mode) { case NAND_ECC_NONE: @@ -469,7 +464,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->mtd.name = master_name; } - if (mtd_parts_nb <= 0 && pdata) { + if (mtd_parts_nb <= 0) { mtd_parts = pdata->parts; mtd_parts_nb = pdata->nr_parts; } @@ -482,7 +477,7 @@ static int __init nand_davinci_probe(struct platform_device *pdev) info->partitioned = true; } - } else if (pdata && pdata->nr_parts) { + } else if (pdata->nr_parts) { dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n", pdata->nr_parts, info->mtd.name); } -- cgit v1.2.3 From 6a4123e581b3112ff4ea7439ab9ae5cb271a9dbd Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 21 Apr 2009 19:58:13 -0700 Subject: mtd: nand: davinci_nand, 4-bit ECC for smallpage Minimal support for the 4-bit ECC engine found on DM355, DM365, DA830/OMAP-L137, and similar recent DaVinci-family chips. This is limited to small-page flash for now; there are some page layout issues for large page chips. Note that most boards using this engine (like the DM355 EVM) include 2GiB large page chips. Sanity tested on DM355 EVM after swapping the socketed NAND for a small-page one. Signed-off-by: David Brownell Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- arch/arm/mach-davinci/include/mach/nand.h | 8 +- drivers/mtd/nand/davinci_nand.c | 304 ++++++++++++++++++++++++++++-- 2 files changed, 297 insertions(+), 15 deletions(-) diff --git a/arch/arm/mach-davinci/include/mach/nand.h b/arch/arm/mach-davinci/include/mach/nand.h index aa482841270b..b520c4b5678a 100644 --- a/arch/arm/mach-davinci/include/mach/nand.h +++ b/arch/arm/mach-davinci/include/mach/nand.h @@ -68,10 +68,14 @@ struct davinci_nand_pdata { /* platform_data */ /* none == NAND_ECC_NONE (strongly *not* advised!!) * soft == NAND_ECC_SOFT - * 1-bit == NAND_ECC_HW - * 4-bit == NAND_ECC_HW_SYNDROME (not on all chips) + * else == NAND_ECC_HW, according to ecc_bits + * + * All DaVinci-family chips support 1-bit hardware ECC. + * Newer ones also support 4-bit ECC, but are awkward + * using it with large page chips. */ nand_ecc_modes_t ecc_mode; + u8 ecc_bits; /* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */ unsigned options; diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index 68b747584bc8..cb5a05e01531 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -44,7 +44,7 @@ * and some flavors of secondary chipselect (e.g. based on A12) as used * with multichip packages. * - * The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC + * The 1-bit ECC hardware is supported, as well as the newer 4-bit ECC * available on chips like the DM355 and OMAP-L137 and needed with the * more error-prone MLC NAND chips. * @@ -54,11 +54,14 @@ struct davinci_nand_info { struct mtd_info mtd; struct nand_chip chip; + struct nand_ecclayout ecclayout; struct device *dev; struct clk *clk; bool partitioned; + bool is_readmode; + void __iomem *base; void __iomem *vaddr; @@ -73,6 +76,7 @@ struct davinci_nand_info { }; static DEFINE_SPINLOCK(davinci_nand_lock); +static bool ecc4_busy; #define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd) @@ -217,6 +221,192 @@ static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat, /*----------------------------------------------------------------------*/ +/* + * 4-bit hardware ECC ... context maintained over entire AEMIF + * + * This is a syndrome engine, but we avoid NAND_ECC_HW_SYNDROME + * since that forces use of a problematic "infix OOB" layout. + * Among other things, it trashes manufacturer bad block markers. + * Also, and specific to this hardware, it ECC-protects the "prepad" + * in the OOB ... while having ECC protection for parts of OOB would + * seem useful, the current MTD stack sometimes wants to update the + * OOB without recomputing ECC. + */ + +static void nand_davinci_hwctl_4bit(struct mtd_info *mtd, int mode) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + unsigned long flags; + u32 val; + + spin_lock_irqsave(&davinci_nand_lock, flags); + + /* Start 4-bit ECC calculation for read/write */ + val = davinci_nand_readl(info, NANDFCR_OFFSET); + val &= ~(0x03 << 4); + val |= (info->core_chipsel << 4) | BIT(12); + davinci_nand_writel(info, NANDFCR_OFFSET, val); + + info->is_readmode = (mode == NAND_ECC_READ); + + spin_unlock_irqrestore(&davinci_nand_lock, flags); +} + +/* Read raw ECC code after writing to NAND. */ +static void +nand_davinci_readecc_4bit(struct davinci_nand_info *info, u32 code[4]) +{ + const u32 mask = 0x03ff03ff; + + code[0] = davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET) & mask; + code[1] = davinci_nand_readl(info, NAND_4BIT_ECC2_OFFSET) & mask; + code[2] = davinci_nand_readl(info, NAND_4BIT_ECC3_OFFSET) & mask; + code[3] = davinci_nand_readl(info, NAND_4BIT_ECC4_OFFSET) & mask; +} + +/* Terminate read ECC; or return ECC (as bytes) of data written to NAND. */ +static int nand_davinci_calculate_4bit(struct mtd_info *mtd, + const u_char *dat, u_char *ecc_code) +{ + struct davinci_nand_info *info = to_davinci_nand(mtd); + u32 raw_ecc[4], *p; + unsigned i; + + /* After a read, terminate ECC calculation by a dummy read + * of some 4-bit ECC register. ECC covers everything that + * was read; correct() just uses the hardware state, so + * ecc_code is not needed. + */ + if (info->is_readmode) { + davinci_nand_readl(info, NAND_4BIT_ECC1_OFFSET); + return 0; + } + + /* Pack eight raw 10-bit ecc values into ten bytes, making + * two passes which each convert four values (in upper and + * lower halves of two 32-bit words) into five bytes. The + * ROM boot loader uses this same packing scheme. + */ + nand_davinci_readecc_4bit(info, raw_ecc); + for (i = 0, p = raw_ecc; i < 2; i++, p += 2) { + *ecc_code++ = p[0] & 0xff; + *ecc_code++ = ((p[0] >> 8) & 0x03) | ((p[0] >> 14) & 0xfc); + *ecc_code++ = ((p[0] >> 22) & 0x0f) | ((p[1] << 4) & 0xf0); + *ecc_code++ = ((p[1] >> 4) & 0x3f) | ((p[1] >> 10) & 0xc0); + *ecc_code++ = (p[1] >> 18) & 0xff; + } + + return 0; +} + +/* Correct up to 4 bits in data we just read, using state left in the + * hardware plus the ecc_code computed when it was first written. + */ +static int nand_davinci_correct_4bit(struct mtd_info *mtd, + u_char *data, u_char *ecc_code, u_char *null) +{ + int i; + struct davinci_nand_info *info = to_davinci_nand(mtd); + unsigned short ecc10[8]; + unsigned short *ecc16; + u32 syndrome[4]; + unsigned num_errors, corrected; + + /* All bytes 0xff? It's an erased page; ignore its ECC. */ + for (i = 0; i < 10; i++) { + if (ecc_code[i] != 0xff) + goto compare; + } + return 0; + +compare: + /* Unpack ten bytes into eight 10 bit values. We know we're + * little-endian, and use type punning for less shifting/masking. + */ + if (WARN_ON(0x01 & (unsigned) ecc_code)) + return -EINVAL; + ecc16 = (unsigned short *)ecc_code; + + ecc10[0] = (ecc16[0] >> 0) & 0x3ff; + ecc10[1] = ((ecc16[0] >> 10) & 0x3f) | ((ecc16[1] << 6) & 0x3c0); + ecc10[2] = (ecc16[1] >> 4) & 0x3ff; + ecc10[3] = ((ecc16[1] >> 14) & 0x3) | ((ecc16[2] << 2) & 0x3fc); + ecc10[4] = (ecc16[2] >> 8) | ((ecc16[3] << 8) & 0x300); + ecc10[5] = (ecc16[3] >> 2) & 0x3ff; + ecc10[6] = ((ecc16[3] >> 12) & 0xf) | ((ecc16[4] << 4) & 0x3f0); + ecc10[7] = (ecc16[4] >> 6) & 0x3ff; + + /* Tell ECC controller about the expected ECC codes. */ + for (i = 7; i >= 0; i--) + davinci_nand_writel(info, NAND_4BIT_ECC_LOAD_OFFSET, ecc10[i]); + + /* Allow time for syndrome calculation ... then read it. + * A syndrome of all zeroes 0 means no detected errors. + */ + davinci_nand_readl(info, NANDFSR_OFFSET); + nand_davinci_readecc_4bit(info, syndrome); + if (!(syndrome[0] | syndrome[1] | syndrome[2] | syndrome[3])) + return 0; + + /* Start address calculation, and wait for it to complete. + * We _could_ start reading more data while this is working, + * to speed up the overall page read. + */ + davinci_nand_writel(info, NANDFCR_OFFSET, + davinci_nand_readl(info, NANDFCR_OFFSET) | BIT(13)); + for (;;) { + u32 fsr = davinci_nand_readl(info, NANDFSR_OFFSET); + + switch ((fsr >> 8) & 0x0f) { + case 0: /* no error, should not happen */ + return 0; + case 1: /* five or more errors detected */ + return -EIO; + case 2: /* error addresses computed */ + case 3: + num_errors = 1 + ((fsr >> 16) & 0x03); + goto correct; + default: /* still working on it */ + cpu_relax(); + continue; + } + } + +correct: + /* correct each error */ + for (i = 0, corrected = 0; i < num_errors; i++) { + int error_address, error_value; + + if (i > 1) { + error_address = davinci_nand_readl(info, + NAND_ERR_ADD2_OFFSET); + error_value = davinci_nand_readl(info, + NAND_ERR_ERRVAL2_OFFSET); + } else { + error_address = davinci_nand_readl(info, + NAND_ERR_ADD1_OFFSET); + error_value = davinci_nand_readl(info, + NAND_ERR_ERRVAL1_OFFSET); + } + + if (i & 1) { + error_address >>= 16; + error_value >>= 16; + } + error_address &= 0x3ff; + error_address = (512 + 7) - error_address; + + if (error_address < 512) { + data[error_address] ^= error_value; + corrected++; + } + } + + return corrected; +} + +/*----------------------------------------------------------------------*/ + /* * NOTE: NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's * how these chips are normally wired. This translates to both 8 and 16 @@ -294,6 +484,23 @@ static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info) /*----------------------------------------------------------------------*/ +/* An ECC layout for using 4-bit ECC with small-page flash, storing + * ten ECC bytes plus the manufacturer's bad block marker byte, and + * and not overlapping the default BBT markers. + */ +static struct nand_ecclayout hwecc4_small __initconst = { + .eccbytes = 10, + .eccpos = { 0, 1, 2, 3, 4, + /* offset 5 holds the badblock marker */ + 6, 7, + 13, 14, 15, }, + .oobfree = { + {.offset = 8, .length = 5, }, + {.offset = 16, }, + }, +}; + + static int __init nand_davinci_probe(struct platform_device *pdev) { struct davinci_nand_pdata *pdata = pdev->dev.platform_data; @@ -378,24 +585,41 @@ static int __init nand_davinci_probe(struct platform_device *pdev) /* Use board-specific ECC config */ ecc_mode = pdata->ecc_mode; + ret = -EINVAL; switch (ecc_mode) { case NAND_ECC_NONE: case NAND_ECC_SOFT: + pdata->ecc_bits = 0; break; case NAND_ECC_HW: - info->chip.ecc.calculate = nand_davinci_calculate_1bit; - info->chip.ecc.correct = nand_davinci_correct_1bit; - info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; + if (pdata->ecc_bits == 4) { + /* No sanity checks: CPUs must support this, + * and the chips may not use NAND_BUSWIDTH_16. + */ + + /* No sharing 4-bit hardware between chipselects yet */ + spin_lock_irq(&davinci_nand_lock); + if (ecc4_busy) + ret = -EBUSY; + else + ecc4_busy = true; + spin_unlock_irq(&davinci_nand_lock); + + if (ret == -EBUSY) + goto err_ecc; + + info->chip.ecc.calculate = nand_davinci_calculate_4bit; + info->chip.ecc.correct = nand_davinci_correct_4bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_4bit; + info->chip.ecc.bytes = 10; + } else { + info->chip.ecc.calculate = nand_davinci_calculate_1bit; + info->chip.ecc.correct = nand_davinci_correct_1bit; + info->chip.ecc.hwctl = nand_davinci_hwctl_1bit; + info->chip.ecc.bytes = 3; + } info->chip.ecc.size = 512; - info->chip.ecc.bytes = 3; break; - case NAND_ECC_HW_SYNDROME: - /* FIXME implement */ - info->chip.ecc.size = 512; - info->chip.ecc.bytes = 10; - - dev_warn(&pdev->dev, "4-bit ECC nyet supported\n"); - /* FALL THROUGH */ default: ret = -EINVAL; goto err_ecc; @@ -435,12 +659,56 @@ static int __init nand_davinci_probe(struct platform_device *pdev) spin_unlock_irq(&davinci_nand_lock); /* Scan to find existence of the device(s) */ - ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1); + ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); goto err_scan; } + /* Update ECC layout if needed ... for 1-bit HW ECC, the default + * is OK, but it allocates 6 bytes when only 3 are needed (for + * each 512 bytes). For the 4-bit HW ECC, that default is not + * usable: 10 bytes are needed, not 6. + */ + if (pdata->ecc_bits == 4) { + int chunks = info->mtd.writesize / 512; + + if (!chunks || info->mtd.oobsize < 16) { + dev_dbg(&pdev->dev, "too small\n"); + ret = -EINVAL; + goto err_scan; + } + + /* For small page chips, preserve the manufacturer's + * badblock marking data ... and make sure a flash BBT + * table marker fits in the free bytes. + */ + if (chunks == 1) { + info->ecclayout = hwecc4_small; + info->ecclayout.oobfree[1].length = + info->mtd.oobsize - 16; + goto syndrome_done; + } + + /* For large page chips we'll be wanting to use a + * not-yet-implemented mode that reads OOB data + * before reading the body of the page, to avoid + * the "infix OOB" model of NAND_ECC_HW_SYNDROME + * (and preserve manufacturer badblock markings). + */ + dev_warn(&pdev->dev, "no 4-bit ECC support yet " + "for large page NAND\n"); + ret = -EIO; + goto err_scan; + +syndrome_done: + info->chip.ecc.layout = &info->ecclayout; + } + + ret = nand_scan_tail(&info->mtd); + if (ret < 0) + goto err_scan; + if (mtd_has_partitions()) { struct mtd_partition *mtd_parts = NULL; int mtd_parts_nb = 0; @@ -503,6 +771,11 @@ err_scan: err_clk_enable: clk_put(info->clk); + spin_lock_irq(&davinci_nand_lock); + if (ecc_mode == NAND_ECC_HW_SYNDROME) + ecc4_busy = false; + spin_unlock_irq(&davinci_nand_lock); + err_ecc: err_clk: err_ioremap: @@ -526,6 +799,11 @@ static int __exit nand_davinci_remove(struct platform_device *pdev) else status = del_mtd_device(&info->mtd); + spin_lock_irq(&davinci_nand_lock); + if (info->chip.ecc.mode == NAND_ECC_HW_SYNDROME) + ecc4_busy = false; + spin_unlock_irq(&davinci_nand_lock); + iounmap(info->base); iounmap(info->vaddr); -- cgit v1.2.3 From 260dc003e9fd41a6cac64308e63dea37ea5de13f Mon Sep 17 00:00:00 2001 From: Vimal Singh Date: Mon, 23 Feb 2009 13:46:08 +0530 Subject: mtd: nand: fix 512 byte software ecc support Type of 'byte_addr' needes to be 'unsigned int' for 512 byte ECC support. Signed-off-by: Vimal Singh Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_ecc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index 868147acce2c..c0cb87d6d16e 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -428,8 +428,8 @@ EXPORT_SYMBOL(nand_calculate_ecc); int nand_correct_data(struct mtd_info *mtd, unsigned char *buf, unsigned char *read_ecc, unsigned char *calc_ecc) { - unsigned char b0, b1, b2; - unsigned char byte_addr, bit_addr; + unsigned char b0, b1, b2, bit_addr; + unsigned int byte_addr; /* 256 or 512 bytes/ecc */ const uint32_t eccsize_mult = (((struct nand_chip *)mtd->priv)->ecc.size) >> 8; -- cgit v1.2.3 From b258fd8d0470c65fef5231887d7e97cb246da0d0 Mon Sep 17 00:00:00 2001 From: Magnus Lilja Date: Fri, 8 May 2009 21:57:47 +0200 Subject: mtd: mxc_nand: add correct dev_id parameter to free_irq() calls Make sure to pass the same dev_id data to free_irq() that was used when calling request_irq(), otherwise we get a warning about freeing an already free IRQ. Signed-off-by: Magnus Lilja Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 65040de54bb7..d5aea6951d0b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -981,7 +981,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) return 0; escan: - free_irq(host->irq, NULL); + free_irq(host->irq, host); eirq: iounmap(host->regs); eres: @@ -1001,7 +1001,7 @@ static int __devexit mxcnd_remove(struct platform_device *pdev) platform_set_drvdata(pdev, NULL); nand_release(&host->mtd); - free_irq(host->irq, NULL); + free_irq(host->irq, host); iounmap(host->regs); kfree(host); -- cgit v1.2.3 From 1e42d142e65ebdef38fb399b421d04e092ad1c6e Mon Sep 17 00:00:00 2001 From: Matthieu CASTET Date: Tue, 28 Apr 2009 18:15:31 +0200 Subject: mtd: m25p80 nand: add m45pe10 ids this patch add m45pe10 [1] chip support to the m25p80 driver. [1] http://www.numonyx.com/Documents/Datasheets/M45PE10.pdf Signed-off-by: Matthieu CASTET Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 1 + 1 file changed, 1 insertion(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index cc6369ea67dd..982572c1056c 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -528,6 +528,7 @@ static struct flash_info __devinitdata m25p_data [] = { { "m25p64", 0x202017, 0, 64 * 1024, 128, }, { "m25p128", 0x202018, 0, 256 * 1024, 64, }, + { "m45pe10", 0x204011, 0, 64 * 1024, 2, }, { "m45pe80", 0x204014, 0, 64 * 1024, 16, }, { "m45pe16", 0x204015, 0, 64 * 1024, 32, }, -- cgit v1.2.3 From ee8f37688966ab1438d0cf42e0cb7c6595d9592c Mon Sep 17 00:00:00 2001 From: Adrian Hunter Date: Tue, 5 May 2009 11:04:19 +0300 Subject: mtd: OneNAND: add support for OneNAND manufactured by Numonyx In addition to adding the Numonyx manufacturer code, this patch also ensures 'sync. write' is disabled when reading identification data - something that the Numonyx chip objects to, but the Samsung chip seems to ignore. Signed-off-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 3 ++- include/linux/mtd/onenand.h | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 30d6999e5f9f..2346857a275d 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -2576,6 +2576,7 @@ static void onenand_print_device_info(int device, int version) static const struct onenand_manufacturers onenand_manuf_ids[] = { {ONENAND_MFR_SAMSUNG, "Samsung"}, + {ONENAND_MFR_NUMONYX, "Numonyx"}, }; /** @@ -2621,7 +2622,7 @@ static int onenand_probe(struct mtd_info *mtd) /* Save system configuration 1 */ syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); /* Clear Sync. Burst Read mode to read BootRAM */ - this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ), this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg & ~ONENAND_SYS_CFG1_SYNC_READ & ~ONENAND_SYS_CFG1_SYNC_WRITE), this->base + ONENAND_REG_SYS_CFG1); /* Send the command for reading device ID from BootRAM */ this->write_word(ONENAND_CMD_READID, this->base + ONENAND_BOOTRAM); diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 9aa2a9149b58..0fa3ac4ad576 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -176,6 +176,7 @@ struct onenand_chip { * OneNAND Flash Manufacturer ID Codes */ #define ONENAND_MFR_SAMSUNG 0xec +#define ONENAND_MFR_NUMONYX 0x20 /** * struct onenand_manufacturers - NAND Flash Manufacturer ID Structure -- cgit v1.2.3 From f19e8999a5631e3af0e1ca5127af80a25aba1fd7 Mon Sep 17 00:00:00 2001 From: David Brownell Date: Tue, 12 May 2009 16:23:32 -0700 Subject: mtd: davinci_nand: cmdlinepart uses MTD IDs Remove some legacy code from the davinci_nand driver, which made cmdlinepart ignore the the MTD ID passed to it. Boards can have multiple NAND chips, and some do (like the DM357 EVM), so this dated hack is undesirable. Correct labels are like "davinci_nand.0" (for chipselect 0). Signed-off-by: David Brownell Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/davinci_nand.c | 11 ----------- 1 file changed, 11 deletions(-) diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index cb5a05e01531..ba6940d1d3d1 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -717,19 +717,8 @@ syndrome_done: static const char *probes[] __initconst = { "cmdlinepart", NULL }; - const char *master_name; - - /* Set info->mtd.name = 0 temporarily */ - master_name = info->mtd.name; - info->mtd.name = (char *)0; - - /* info->mtd.name == 0, means: don't bother checking - */ mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes, &mtd_parts, 0); - - /* Restore info->mtd.name */ - info->mtd.name = master_name; } if (mtd_parts_nb <= 0) { -- cgit v1.2.3 From a0645ce9ba2e40fb2e2d74e47c90063015ee4527 Mon Sep 17 00:00:00 2001 From: Michał Mirosław Date: Wed, 13 May 2009 00:37:18 +0200 Subject: mtd: add SST39SF040 chip to jedec_probe MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add SST39SF040 chip (like SST39SF020A but bigger - 4Mbit). Signed-off-by: Michał Mirosław Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index e824b9b9b056..ccc4cfc7e4b5 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -166,6 +166,7 @@ #define SST39LF040 0x00D7 #define SST39SF010A 0x00B5 #define SST39SF020A 0x00B6 +#define SST39SF040 0x00B7 #define SST49LF004B 0x0060 #define SST49LF040B 0x0050 #define SST49LF008A 0x005a @@ -1391,6 +1392,18 @@ static const struct amd_flash_info jedec_table[] = { .regions = { ERASEINFO(0x01000,64), } + }, { + .mfr_id = MANUFACTURER_SST, + .dev_id = SST39SF040, + .name = "SST 39SF040", + .devtypes = CFI_DEVICETYPE_X8, + .uaddr = MTD_UADDR_0x5555_0x2AAA, + .dev_size = SIZE_512KiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 1, + .regions = { + ERASEINFO(0x01000,128), + } }, { .mfr_id = MANUFACTURER_SST, .dev_id = SST49LF040B, -- cgit v1.2.3 From d6fed9e9fc5eefae5be0ecf222bac7e7496e8e74 Mon Sep 17 00:00:00 2001 From: Alexander Clouter Date: Mon, 11 May 2009 19:28:01 +0100 Subject: mtd: extend plat_nand for (read|write)_buf This patch adds (write|read)_buf callbacks to plat_nand. The NAND on the TS-7800 provisioned by the FPGA allows readw() and readl() to be used which gives a 2.5x speed up. To be able to use this from the plat_nand driver a hook for read_buf (and also write_buf whilst we are in there) need to be made available. This patch adds the hook. Signed-off-by: Alexander Clouter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 2 ++ include/linux/mtd/nand.h | 6 ++++++ 2 files changed, 8 insertions(+) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 28ffd4e8bb2f..47a2105b9671 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -61,6 +61,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) data->chip.cmd_ctrl = pdata->ctrl.cmd_ctrl; data->chip.dev_ready = pdata->ctrl.dev_ready; data->chip.select_chip = pdata->ctrl.select_chip; + data->chip.write_buf = pdata->ctrl.write_buf; + data->chip.read_buf = pdata->ctrl.read_buf; data->chip.chip_delay = pdata->chip.chip_delay; data->chip.options |= pdata->chip.options; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 7efb9be34662..0e35375ea795 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -584,6 +584,8 @@ struct platform_nand_chip { * @select_chip: platform specific chip select function * @cmd_ctrl: platform specific function for controlling * ALE/CLE/nCE. Also used to write command and address + * @write_buf: platform specific function for write buffer + * @read_buf: platform specific function for read buffer * @priv: private data to transport driver specific settings * * All fields are optional and depend on the hardware driver requirements @@ -594,6 +596,10 @@ struct platform_nand_ctrl { void (*select_chip)(struct mtd_info *mtd, int chip); void (*cmd_ctrl)(struct mtd_info *mtd, int dat, unsigned int ctrl); + void (*write_buf)(struct mtd_info *mtd, + const uint8_t *buf, int len); + void (*read_buf)(struct mtd_info *mtd, + uint8_t *buf, int len); void *priv; }; -- cgit v1.2.3 From ec2d0d842577854eee18f0dc06bd48fe17189b54 Mon Sep 17 00:00:00 2001 From: Daniel Ribeiro Date: Sun, 17 May 2009 08:02:17 -0300 Subject: mtd: CFI: quirk for PF38F4476. This chip reports CFI 1.3, but the CFI PRI is like CFI 1.1. Add a quirk to pass probe on this chip. This patch depends on "MTD: CFI 1.0 and CFI 1.1" Signed-off-by: Daniel Ribeiro Acked-by: Nicolas Pitre Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index c240454fd113..e772803b4959 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -46,6 +46,7 @@ #define MANUFACTURER_INTEL 0x0089 #define I82802AB 0x00ad #define I82802AC 0x00ac +#define PF38F4476 0x881c #define MANUFACTURER_ST 0x0020 #define M50LPW080 0x002F #define M50FLW080A 0x0080 @@ -315,9 +316,18 @@ static struct cfi_fixup fixup_table[] = { { 0, 0, NULL, NULL } }; +static void cfi_fixup_major_minor(struct cfi_private *cfi, + struct cfi_pri_intelext *extp) +{ + if (cfi->mfr == MANUFACTURER_INTEL && + cfi->id == PF38F4476 && extp->MinorVersion == '3') + extp->MinorVersion = '1'; +} + static inline struct cfi_pri_intelext * read_pri_intelext(struct map_info *map, __u16 adr) { + struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_intelext *extp; unsigned int extp_size = sizeof(*extp); @@ -326,6 +336,8 @@ read_pri_intelext(struct map_info *map, __u16 adr) if (!extp) return NULL; + cfi_fixup_major_minor(cfi, extp); + if (extp->MajorVersion != '1' || (extp->MinorVersion < '0' || extp->MinorVersion > '5')) { printk(KERN_ERR " Unknown Intel/Sharp Extended Query " -- cgit v1.2.3 From e1b158abc532f5a9d355c187583038c4f75ab11d Mon Sep 17 00:00:00 2001 From: Daniel Ribeiro Date: Sun, 17 May 2009 08:02:08 -0300 Subject: mtd: CFI 1.0 and CFI 1.1 This patch allows otpinfo for CFI >= 1.0 and burst read for CFI >= 1.1. references: 1.0: http://www.datasheetcatalog.org/datasheets2/81/816884_1.pdf 1.1: http://milkymist.org/doc/MT28F640J3.pdf http://www.delorie.com/agenda/specs/29066709.pdf Signed-off-by: Daniel Ribeiro Acked-by: Nicolas Pitre Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index e772803b4959..8664feebc93b 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -329,6 +329,7 @@ read_pri_intelext(struct map_info *map, __u16 adr) { struct cfi_private *cfi = map->fldrv_priv; struct cfi_pri_intelext *extp; + unsigned int extra_size = 0; unsigned int extp_size = sizeof(*extp); again: @@ -352,19 +353,24 @@ read_pri_intelext(struct map_info *map, __u16 adr) extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); - if (extp->MajorVersion == '1' && extp->MinorVersion >= '3') { - unsigned int extra_size = 0; - int nb_parts, i; + if (extp->MinorVersion >= '0') { + extra_size = 0; /* Protection Register info */ extra_size += (extp->NumProtectionFields - 1) * sizeof(struct cfi_intelext_otpinfo); + } + if (extp->MinorVersion >= '1') { /* Burst Read info */ extra_size += 2; if (extp_size < sizeof(*extp) + extra_size) goto need_more; - extra_size += extp->extra[extra_size-1]; + extra_size += extp->extra[extra_size - 1]; + } + + if (extp->MinorVersion >= '3') { + int nb_parts, i; /* Number of hardware-partitions */ extra_size += 1; -- cgit v1.2.3 From ab1ff210a86ae4ab5990b7bd2dc69fafbfa2355a Mon Sep 17 00:00:00 2001 From: Lennert Buytenhek Date: Wed, 20 May 2009 13:07:11 +0200 Subject: mtd: m25p80: add support for Macronix MX25L12805D Signed-off-by: Lennert Buytenhek Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/m25p80.c | 3 +++ 1 file changed, 3 insertions(+) diff --git a/drivers/mtd/devices/m25p80.c b/drivers/mtd/devices/m25p80.c index 982572c1056c..59c46126a5ce 100644 --- a/drivers/mtd/devices/m25p80.c +++ b/drivers/mtd/devices/m25p80.c @@ -500,6 +500,9 @@ static struct flash_info __devinitdata m25p_data [] = { { "at26df161a", 0x1f4601, 0, 64 * 1024, 32, SECT_4K, }, { "at26df321", 0x1f4701, 0, 64 * 1024, 64, SECT_4K, }, + /* Macronix */ + { "mx25l12805d", 0xc22018, 0, 64 * 1024, 256, }, + /* Spansion -- single (large) sector size only, at least * for the chips listed here (without boot sectors). */ -- cgit v1.2.3 From d3412dbd721c0136379d86242297d19399f0c05f Mon Sep 17 00:00:00 2001 From: Mika Korhonen Date: Thu, 21 May 2009 23:09:42 +0300 Subject: mtd: OneNAND: add missing __devexit_p Add missing __devexit_p wrapper and no more mark shutdown with __devexit. Fixes build in configurations where devexit functions get discarded. Signed-off-by: Mika Korhonen Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/onenand/omap2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index f2e9de1414df..df26db863f2f 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -566,7 +566,7 @@ int omap2_onenand_rephase(void) NULL, __adjust_timing); } -static void __devexit omap2_onenand_shutdown(struct platform_device *pdev) +static void omap2_onenand_shutdown(struct platform_device *pdev) { struct omap2_onenand *c = dev_get_drvdata(&pdev->dev); @@ -778,7 +778,7 @@ static int __devexit omap2_onenand_remove(struct platform_device *pdev) static struct platform_driver omap2_onenand_driver = { .probe = omap2_onenand_probe, - .remove = omap2_onenand_remove, + .remove = __devexit_p(omap2_onenand_remove), .shutdown = omap2_onenand_shutdown, .driver = { .name = DRIVER_NAME, -- cgit v1.2.3 From 4d964824ec826ed97bdde10bc8d8c4ce10540a98 Mon Sep 17 00:00:00 2001 From: Shane McDonald Date: Sat, 2 May 2009 09:40:06 -0600 Subject: mtd: remove pmcmsp-ramroot.c The RAMROOT function was a successful but non-portable attempt to append the root filesystem to the end of the kernel image. The preferred and portable solution is to use an initramfs instead. The only user of this function was the msp71xx configuration in the MIPS architecture; as the use of the RAMROOT has been removed from that configuration, there are no more users, so this code can be removed. Signed-off-by: Shane McDonald Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 9 ---- drivers/mtd/maps/Makefile | 1 - drivers/mtd/maps/pmcmsp-ramroot.c | 104 -------------------------------------- 3 files changed, 114 deletions(-) delete mode 100644 drivers/mtd/maps/pmcmsp-ramroot.c diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index cdd54cb4d224..91544c2c99a6 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -105,15 +105,6 @@ config MSP_FLASH_MAP_LIMIT default "0x02000000" depends on MSP_FLASH_MAP_LIMIT_32M -config MTD_PMC_MSP_RAMROOT - tristate "Embedded RAM block device for root on PMC-Sierra MSP" - depends on PMC_MSP_EMBEDDED_ROOTFS && \ - (MTD_BLOCK || MTD_BLOCK_RO) && \ - MTD_RAM - help - This provides support for the embedded root file system - on PMC MSP devices. This memory is mapped as a MTD block device. - config MTD_SUN_UFLASH tristate "Sun Microsystems userflash support" depends on SPARC && MTD_CFI && PCI diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index 2dbc1bec8488..8bae7f9850c0 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -25,7 +25,6 @@ obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o -obj-$(CONFIG_MTD_PMC_MSP_RAMROOT)+= pmcmsp-ramroot.o obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o diff --git a/drivers/mtd/maps/pmcmsp-ramroot.c b/drivers/mtd/maps/pmcmsp-ramroot.c deleted file mode 100644 index 30de5c0c09a9..000000000000 --- a/drivers/mtd/maps/pmcmsp-ramroot.c +++ /dev/null @@ -1,104 +0,0 @@ -/* - * Mapping of the rootfs in a physical region of memory - * - * Copyright (C) 2005-2007 PMC-Sierra Inc. - * Author: Andrew Hughes, Andrew_Hughes@pmc-sierra.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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * 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., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include - -#include - -static struct mtd_info *rr_mtd; - -struct map_info rr_map = { - .name = "ramroot", - .bankwidth = 4, -}; - -static int __init init_rrmap(void) -{ - void *ramroot_start; - unsigned long ramroot_size; - - /* Check for supported rootfs types */ - if (get_ramroot(&ramroot_start, &ramroot_size)) { - rr_map.phys = CPHYSADDR(ramroot_start); - rr_map.size = ramroot_size; - - printk(KERN_NOTICE - "PMC embedded root device: 0x%08lx @ 0x%08lx\n", - rr_map.size, (unsigned long)rr_map.phys); - } else { - printk(KERN_ERR - "init_rrmap: no supported embedded rootfs detected!\n"); - return -ENXIO; - } - - /* Map rootfs to I/O space for block device driver */ - rr_map.virt = ioremap(rr_map.phys, rr_map.size); - if (!rr_map.virt) { - printk(KERN_ERR "Failed to ioremap\n"); - return -EIO; - } - - simple_map_init(&rr_map); - - rr_mtd = do_map_probe("map_ram", &rr_map); - if (rr_mtd) { - rr_mtd->owner = THIS_MODULE; - - add_mtd_device(rr_mtd); - - return 0; - } - - iounmap(rr_map.virt); - return -ENXIO; -} - -static void __exit cleanup_rrmap(void) -{ - del_mtd_device(rr_mtd); - map_destroy(rr_mtd); - - iounmap(rr_map.virt); - rr_map.virt = NULL; -} - -MODULE_AUTHOR("PMC-Sierra, Inc"); -MODULE_DESCRIPTION("MTD map driver for embedded PMC-Sierra MSP filesystem"); -MODULE_LICENSE("GPL"); - -module_init(init_rrmap); -module_exit(cleanup_rrmap); -- cgit v1.2.3 From bf95efd41b1a760128eb25402791b0a4941eb655 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 12 May 2009 13:46:58 -0700 Subject: mtd: plat_nand: add platform probe/remove callbacks Add optional probe and remove callbacks to the plat_nand driver. Some platforms may require additional setup, such as configuring the memory controller, before the nand device can be accessed. This patch provides an optional callback to handle this setup as well as a callback to teardown the setup. Signed-off-by: H Hartley Sweeten Tested-by: Alexander Clouter Signed-off-by: Andrew Morton Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 13 +++++++++++-- include/linux/mtd/nand.h | 7 +++++++ 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 47a2105b9671..22e0ce788419 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -72,6 +72,13 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) platform_set_drvdata(pdev, data); + /* Handle any platform specific setup */ + if (pdata->ctrl.probe) { + res = pdata->ctrl.probe(pdev); + if (res) + goto out; + } + /* Scan to find existance of the device */ if (nand_scan(&data->mtd, 1)) { res = -ENXIO; @@ -101,6 +108,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) nand_release(&data->mtd); out: + if (pdata->ctrl.remove) + pdata->ctrl.remove(pdev); platform_set_drvdata(pdev, NULL); iounmap(data->io_base); kfree(data); @@ -113,15 +122,15 @@ out: static int __devexit plat_nand_remove(struct platform_device *pdev) { struct plat_nand_data *data = platform_get_drvdata(pdev); -#ifdef CONFIG_MTD_PARTITIONS struct platform_nand_data *pdata = pdev->dev.platform_data; -#endif nand_release(&data->mtd); #ifdef CONFIG_MTD_PARTITIONS if (data->parts && data->parts != pdata->chip.partitions) kfree(data->parts); #endif + if (pdata->ctrl.remove) + pdata->ctrl.remove(pdev); iounmap(data->io_base); kfree(data); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 0e35375ea795..7f2d69356554 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -577,8 +577,13 @@ struct platform_nand_chip { void *priv; }; +/* Keep gcc happy */ +struct platform_device; + /** * struct platform_nand_ctrl - controller level device structure + * @probe: platform specific function to probe/setup hardware + * @remove: platform specific function to remove/teardown hardware * @hwcontrol: platform specific hardware control structure * @dev_ready: platform specific function to read ready/busy pin * @select_chip: platform specific chip select function @@ -591,6 +596,8 @@ struct platform_nand_chip { * All fields are optional and depend on the hardware driver requirements */ struct platform_nand_ctrl { + int (*probe)(struct platform_device *pdev); + void (*remove)(struct platform_device *pdev); void (*hwcontrol)(struct mtd_info *mtd, int cmd); int (*dev_ready)(struct mtd_info *mtd); void (*select_chip)(struct mtd_info *mtd, int chip); -- cgit v1.2.3 From f36e20c01ad0104688f2eaebdf2213e749929c97 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 12 May 2009 13:46:59 -0700 Subject: mtd: plat_nand: allow platform to set partitions Add optional callback to allow platform to initialize partitions. Static partitions on a nand device could vary depending on the size of the device. This patch allows an optional platform callback to be used to setup this partition information at runtime. Scan order is: 1) chip.part_probe_types 2) chip.set_parts 3) chip.partitions 4) full mtd device (fallback for no partitions) Some of the existing nand drivers could possibly be replaced by the plat_nand driver by using this patch. These include autcpu12.c and ts7250.c drivers. Signed-off-by: H Hartley Sweeten Signed-off-by: Andrew Morton Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/plat_nand.c | 2 ++ include/linux/mtd/nand.h | 3 +++ 2 files changed, 5 insertions(+) diff --git a/drivers/mtd/nand/plat_nand.c b/drivers/mtd/nand/plat_nand.c index 22e0ce788419..4e16c6f5bdd5 100644 --- a/drivers/mtd/nand/plat_nand.c +++ b/drivers/mtd/nand/plat_nand.c @@ -95,6 +95,8 @@ static int __devinit plat_nand_probe(struct platform_device *pdev) return 0; } } + if (pdata->chip.set_parts) + pdata->chip.set_parts(data->mtd.size, &pdata->chip); if (pdata->chip.partitions) { data->parts = pdata->chip.partitions; res = add_mtd_partitions(&data->mtd, data->parts, diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 7f2d69356554..4030ebada49e 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -563,6 +563,7 @@ extern int nand_do_read(struct mtd_info *mtd, loff_t from, size_t len, * @options: Option flags, e.g. 16bit buswidth * @ecclayout: ecc layout info structure * @part_probe_types: NULL-terminated array of probe types + * @set_parts: platform specific function to set partitions * @priv: hardware controller specific settings */ struct platform_nand_chip { @@ -574,6 +575,8 @@ struct platform_nand_chip { int chip_delay; unsigned int options; const char **part_probe_types; + void (*set_parts)(uint64_t size, + struct platform_nand_chip *chip); void *priv; }; -- cgit v1.2.3 From bd3fd62ecc99c709739cb969be76f44903a4043b Mon Sep 17 00:00:00 2001 From: Vladimir Barinov Date: Mon, 25 May 2009 13:06:17 +0400 Subject: mtd: MXC NAND support for 2KiB page size flashes - Add support for 2KiB page size flashes - Fix page address access for large pages - Detect oob layout at runtime - handle pagesize_2k variable - Fix oob16 layout: reserve location 5 of oob area since it's used for bbt Signed-off-by: Vladimir Barinov Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 60 ++++++++++++++++++++++++++++++++++++++------- 1 file changed, 51 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index d5aea6951d0b..d03bd4eff722 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -138,7 +138,14 @@ static struct nand_ecclayout nand_hw_eccoob_8 = { static struct nand_ecclayout nand_hw_eccoob_16 = { .eccbytes = 5, .eccpos = {6, 7, 8, 9, 10}, - .oobfree = {{0, 6}, {12, 4}, } + .oobfree = {{0, 5}, {11, 5}, } +}; + +static struct nand_ecclayout nand_hw_eccoob_64 = { + .eccbytes = 20, + .eccpos = {6, 7, 8, 9, 10, 22, 23, 24, 25, 26, + 38, 39, 40, 41, 42, 54, 55, 56, 57, 58}, + .oobfree = {{2, 4}, {11, 10}, {27, 10}, {43, 10}, {59, 5}, } }; #ifdef CONFIG_MTD_PARTITIONS @@ -795,9 +802,13 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, send_addr(host, (page_addr & 0xff), false); if (host->pagesize_2k) { - send_addr(host, (page_addr >> 8) & 0xFF, false); - if (mtd->size >= 0x40000000) + if (mtd->size >= 0x10000000) { + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, false); send_addr(host, (page_addr >> 16) & 0xff, true); + } else + /* paddr_8 - paddr_15 */ + send_addr(host, (page_addr >> 8) & 0xff, true); } else { /* One more address cycle for higher density devices */ if (mtd->size >= 0x4000000) { @@ -919,7 +930,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.mode = NAND_ECC_HW; this->ecc.size = 512; this->ecc.bytes = 3; - this->ecc.layout = &nand_hw_eccoob_8; tmp = readw(host->regs + NFC_CONFIG1); tmp |= NFC_ECC_EN; writew(tmp, host->regs + NFC_CONFIG1); @@ -953,12 +963,44 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.layout = &nand_hw_eccoob_16; } - host->pagesize_2k = 0; + /* first scan to find the device and get the page size */ + if (nand_scan_ident(mtd, 1)) { + err = -ENXIO; + goto escan; + } - /* Scan to find existence of the device */ - if (nand_scan(mtd, 1)) { - DEBUG(MTD_DEBUG_LEVEL0, - "MXC_ND: Unable to find any NAND device.\n"); + host->pagesize_2k = (mtd->writesize == 2048) ? 1 : 0; + + if (this->ecc.mode == NAND_ECC_HW) { + switch (mtd->oobsize) { + case 8: + this->ecc.layout = &nand_hw_eccoob_8; + break; + case 16: + this->ecc.layout = &nand_hw_eccoob_16; + break; + case 64: + this->ecc.layout = &nand_hw_eccoob_64; + break; + default: + /* page size not handled by HW ECC */ + /* switching back to soft ECC */ + this->ecc.size = 512; + this->ecc.bytes = 3; + this->ecc.layout = &nand_hw_eccoob_8; + this->ecc.mode = NAND_ECC_SOFT; + this->ecc.calculate = NULL; + this->ecc.correct = NULL; + this->ecc.hwctl = NULL; + tmp = readw(host->regs + NFC_CONFIG1); + tmp &= ~NFC_ECC_EN; + writew(tmp, host->regs + NFC_CONFIG1); + break; + } + } + + /* second phase scan */ + if (nand_scan_tail(mtd)) { err = -ENXIO; goto escan; } -- cgit v1.2.3 From f4fa697c26bcd9e942de26bad970f4de1da5a49b Mon Sep 17 00:00:00 2001 From: Simon Polette Date: Wed, 27 May 2009 18:19:39 +0300 Subject: mtd: add on-flash BBT support for Atmel NAND driver Just add a new on-flash-bbt module parameter. Signed-off-by: Simon Polette Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/atmel_nand.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 47a33cec3793..2802992b39da 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -24,6 +24,7 @@ #include #include +#include #include #include #include @@ -47,6 +48,9 @@ #define no_ecc 0 #endif +static int on_flash_bbt = 0; +module_param(on_flash_bbt, int, 0); + /* Register access macros */ #define ecc_readl(add, reg) \ __raw_readl(add + ATMEL_ECC_##reg) @@ -459,12 +463,17 @@ static int __init atmel_nand_probe(struct platform_device *pdev) if (host->board->det_pin) { if (gpio_get_value(host->board->det_pin)) { - printk("No SmartMedia card inserted.\n"); + printk(KERN_INFO "No SmartMedia card inserted.\n"); res = ENXIO; goto err_no_card; } } + if (on_flash_bbt) { + printk(KERN_INFO "atmel_nand: Use On Flash BBT\n"); + nand_chip->options |= NAND_USE_FLASH_BBT; + } + /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1)) { res = -ENXIO; -- cgit v1.2.3 From 530c3b60658687e2ad7bf98ef83631a8280ae8a6 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 26 May 2009 06:24:13 -0400 Subject: mtd: blackfin NFC: remove pointless return value in bf5xx_nand_dma_rw Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/bf5xx_nand.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 4c2a67ca801e..2ab42d2d4f91 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -458,7 +458,7 @@ static irqreturn_t bf5xx_nand_dma_irq(int irq, void *dev_id) return IRQ_HANDLED; } -static int bf5xx_nand_dma_rw(struct mtd_info *mtd, +static void bf5xx_nand_dma_rw(struct mtd_info *mtd, uint8_t *buf, int is_read) { struct bf5xx_nand_info *info = mtd_to_nand_info(mtd); @@ -512,8 +512,6 @@ static int bf5xx_nand_dma_rw(struct mtd_info *mtd, else bfin_write_NFC_PGCTL(0x2); wait_for_completion(&info->dma_completion); - - return 0; } static void bf5xx_nand_dma_read_buf(struct mtd_info *mtd, -- cgit v1.2.3 From c3a9f35673290f49ec115d36ad283961c82c135a Mon Sep 17 00:00:00 2001 From: Cliff Cai Date: Tue, 26 May 2009 06:24:14 -0400 Subject: mtd: blackfin NFC: fix hang when using NAND on BF527-EZKITs The DMAs have different bit sizes on BF52x and BF54x. From the PHRM: "The 16-bit DMA Access Bus (DAB) connects the DMA controller to the on-chip peripherals, PPI, SPI, Ethernet MAC, the SPORTs, NFC, HOSTDP and the UARTs." 32-bit DMA won't work for BF52x. Signed-off-by: Cliff Cai Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/bf5xx_nand.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 2ab42d2d4f91..8506e7e606fd 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -496,11 +496,20 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, /* setup DMA register with Blackfin DMA API */ set_dma_config(CH_NFC, 0x0); set_dma_start_addr(CH_NFC, (unsigned long) buf); + +/* The DMAs have different size on BF52x and BF54x */ +#ifdef CONFIG_BF52x + set_dma_x_count(CH_NFC, (page_size >> 1)); + set_dma_x_modify(CH_NFC, 2); + val = DI_EN | WDSIZE_16; +#endif + +#ifdef CONFIG_BF54x set_dma_x_count(CH_NFC, (page_size >> 2)); set_dma_x_modify(CH_NFC, 4); - - /* setup write or read operation */ val = DI_EN | WDSIZE_32; +#endif + /* setup write or read operation */ if (is_read) val |= WNR; set_dma_config(CH_NFC, val); -- cgit v1.2.3 From fa254ecbcca713a4aec99478e79f858942b3d4e0 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 26 May 2009 19:33:16 -0400 Subject: mtd: uclinux: allow systems to override map addr/size Due to a processor anomaly (05000263 to be exact), most Blackfin parts cannot keep the embedded filesystem image directly after the kernel in RAM. Instead, the filesystem needs to be relocated to the end of memory. As such, we need to tweak the map addr/size during boot for Blackfin systems. This can be done in any early arch/board init code. Signed-off-by: Mike Frysinger CC: Paul Mundt CC: Greg Ungerer CC: uclinux-dev@uclinux.org CC: linux-mtd@lists.infradead.org Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/uclinux.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 81756e397711..61d4087dd8f7 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -22,8 +22,12 @@ /****************************************************************************/ +extern char _ebss; + struct map_info uclinux_ram_map = { .name = "RAM", + .phys = (unsigned long)&_ebss, + .size = 0, }; struct mtd_info *uclinux_ram_mtdinfo; @@ -55,12 +59,10 @@ static int __init uclinux_mtd_init(void) { struct mtd_info *mtd; struct map_info *mapp; - extern char _ebss; - unsigned long addr = (unsigned long) &_ebss; mapp = &uclinux_ram_map; - mapp->phys = addr; - mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(addr + 8)))); + if (!mapp->size) + mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8)))); mapp->bankwidth = 4; printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", -- cgit v1.2.3 From 6ae392ccadbc622d58a9d01a7ee59e340f82fe85 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 26 May 2009 19:33:18 -0400 Subject: mtd: uclinux: do not allow to be built as a module There isn't any benefit to building the uClinux MTD map as a module as the rootfs it requires in order to actually be usable is appended to the kernel image, not the module. No known system builds it this way either, so change the option to "bool". Signed-off-by: Mike Frysinger CC: Paul Mundt CC: Greg Ungerer CC: uclinux-dev@uclinux.org CC: linux-mtd@lists.infradead.org Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 91544c2c99a6..0b98654d8eed 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -492,7 +492,7 @@ config MTD_BFIN_ASYNC If compiled as a module, it will be called bfin-async-flash. config MTD_UCLINUX - tristate "Generic uClinux RAM/ROM filesystem support" + bool "Generic uClinux RAM/ROM filesystem support" depends on MTD_PARTITIONS && MTD_RAM && !MMU help Map driver to support image based filesystems for uClinux. -- cgit v1.2.3 From 9f31f4b9dccf6fa4a606ae04602ec232b94727fb Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 26 May 2009 19:33:17 -0400 Subject: mtd: uclinux: mark local stuff static The uclinux_ram_mtdinfo, uclinux_romfs, and uclinux_point symbols do not need to be visible outside of this module, so mark them static. Signed-off-by: Mike Frysinger CC: Greg Ungerer CC: uclinux-dev@uclinux.org CC: linux-mtd@lists.infradead.org Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/uclinux.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 61d4087dd8f7..d4314fb88212 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -30,11 +30,11 @@ struct map_info uclinux_ram_map = { .size = 0, }; -struct mtd_info *uclinux_ram_mtdinfo; +static struct mtd_info *uclinux_ram_mtdinfo; /****************************************************************************/ -struct mtd_partition uclinux_romfs[] = { +static struct mtd_partition uclinux_romfs[] = { { .name = "ROMfs" } }; @@ -42,7 +42,7 @@ struct mtd_partition uclinux_romfs[] = { /****************************************************************************/ -int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, +static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, void **virt, resource_size_t *phys) { struct map_info *map = mtd->priv; -- cgit v1.2.3 From 4938c88c922fad23f0a9f404eeda0207a819e4df Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 2 Jun 2009 00:06:23 -0400 Subject: mtd: maps: Blackfin async: fix memory leaks in probe/remove funcs Signed-off-by: Mike Frysinger Signed-off-by: Bryan Wu Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/bfin-async-flash.c | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index 576611f605db..365c77b1b871 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -40,6 +40,9 @@ struct async_state { uint32_t flash_ambctl0, flash_ambctl1; uint32_t save_ambctl0, save_ambctl1; unsigned long irq_flags; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif }; static void switch_to_flash(struct async_state *state) @@ -170,6 +173,7 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) if (ret > 0) { pr_devinit(KERN_NOTICE DRIVER_NAME ": Using commandline partition definition\n"); add_mtd_partitions(state->mtd, pdata->parts, ret); + state->parts = pdata->parts; } else if (pdata->nr_parts) { pr_devinit(KERN_NOTICE DRIVER_NAME ": Using board partition definition\n"); @@ -193,6 +197,7 @@ static int __devexit bfin_flash_remove(struct platform_device *pdev) gpio_free(state->enet_flash_pin); #ifdef CONFIG_MTD_PARTITIONS del_mtd_partitions(state->mtd); + kfree(state->parts); #endif map_destroy(state->mtd); kfree(state); -- cgit v1.2.3 From 67ce04bf2746f8a1f8c2a104b313d20c63f68378 Mon Sep 17 00:00:00 2001 From: Vimal Singh Date: Tue, 12 May 2009 13:47:03 -0700 Subject: mtd: nand: add OMAP2/OMAP3 NAND driver This driver is present in the OMAP tree, now pushing it to MTD. Original author(s): Jian Zhang Signed-off-by: Vimal Singh Cc: Jian Zhang Cc: Artem Bityutskiy Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 6 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/omap2.c | 776 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 783 insertions(+) create mode 100644 drivers/mtd/nand/omap2.c diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 890936d0275e..3b3a21d1a6ba 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -74,6 +74,12 @@ config MTD_NAND_AMS_DELTA help Support for NAND flash on Amstrad E3 (Delta). +config MTD_NAND_OMAP2 + tristate "NAND Flash device on OMAP2 and OMAP3" + depends on ARM && MTD_NAND && (ARCH_OMAP2 || ARCH_OMAP3) + help + Support for NAND flash on Texas Instruments OMAP2 and OMAP3 platforms. + config MTD_NAND_TS7250 tristate "NAND Flash device on TS-7250 board" depends on MACH_TS72XX diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index d33860ac42c3..f3a786b3cff3 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -25,6 +25,7 @@ obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o obj-$(CONFIG_MTD_NAND_ATMEL) += atmel_nand.o obj-$(CONFIG_MTD_NAND_GPIO) += gpio.o +obj-$(CONFIG_MTD_NAND_OMAP2) += omap2.o obj-$(CONFIG_MTD_NAND_CM_X270) += cmx270_nand.o obj-$(CONFIG_MTD_NAND_BASLER_EXCITE) += excite_nandflash.o obj-$(CONFIG_MTD_NAND_PXA3xx) += pxa3xx_nand.o diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c new file mode 100644 index 000000000000..0cd76f89f4b0 --- /dev/null +++ b/drivers/mtd/nand/omap2.c @@ -0,0 +1,776 @@ +/* + * Copyright © 2004 Texas Instruments, Jian Zhang + * Copyright © 2004 Micron Technology Inc. + * Copyright © 2004 David Brownell + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +#define GPMC_IRQ_STATUS 0x18 +#define GPMC_ECC_CONFIG 0x1F4 +#define GPMC_ECC_CONTROL 0x1F8 +#define GPMC_ECC_SIZE_CONFIG 0x1FC +#define GPMC_ECC1_RESULT 0x200 + +#define DRIVER_NAME "omap2-nand" + +/* size (4 KiB) for IO mapping */ +#define NAND_IO_SIZE SZ_4K + +#define NAND_WP_OFF 0 +#define NAND_WP_BIT 0x00000010 +#define WR_RD_PIN_MONITORING 0x00600000 + +#define GPMC_BUF_FULL 0x00000001 +#define GPMC_BUF_EMPTY 0x00000000 + +#define NAND_Ecc_P1e (1 << 0) +#define NAND_Ecc_P2e (1 << 1) +#define NAND_Ecc_P4e (1 << 2) +#define NAND_Ecc_P8e (1 << 3) +#define NAND_Ecc_P16e (1 << 4) +#define NAND_Ecc_P32e (1 << 5) +#define NAND_Ecc_P64e (1 << 6) +#define NAND_Ecc_P128e (1 << 7) +#define NAND_Ecc_P256e (1 << 8) +#define NAND_Ecc_P512e (1 << 9) +#define NAND_Ecc_P1024e (1 << 10) +#define NAND_Ecc_P2048e (1 << 11) + +#define NAND_Ecc_P1o (1 << 16) +#define NAND_Ecc_P2o (1 << 17) +#define NAND_Ecc_P4o (1 << 18) +#define NAND_Ecc_P8o (1 << 19) +#define NAND_Ecc_P16o (1 << 20) +#define NAND_Ecc_P32o (1 << 21) +#define NAND_Ecc_P64o (1 << 22) +#define NAND_Ecc_P128o (1 << 23) +#define NAND_Ecc_P256o (1 << 24) +#define NAND_Ecc_P512o (1 << 25) +#define NAND_Ecc_P1024o (1 << 26) +#define NAND_Ecc_P2048o (1 << 27) + +#define TF(value) (value ? 1 : 0) + +#define P2048e(a) (TF(a & NAND_Ecc_P2048e) << 0) +#define P2048o(a) (TF(a & NAND_Ecc_P2048o) << 1) +#define P1e(a) (TF(a & NAND_Ecc_P1e) << 2) +#define P1o(a) (TF(a & NAND_Ecc_P1o) << 3) +#define P2e(a) (TF(a & NAND_Ecc_P2e) << 4) +#define P2o(a) (TF(a & NAND_Ecc_P2o) << 5) +#define P4e(a) (TF(a & NAND_Ecc_P4e) << 6) +#define P4o(a) (TF(a & NAND_Ecc_P4o) << 7) + +#define P8e(a) (TF(a & NAND_Ecc_P8e) << 0) +#define P8o(a) (TF(a & NAND_Ecc_P8o) << 1) +#define P16e(a) (TF(a & NAND_Ecc_P16e) << 2) +#define P16o(a) (TF(a & NAND_Ecc_P16o) << 3) +#define P32e(a) (TF(a & NAND_Ecc_P32e) << 4) +#define P32o(a) (TF(a & NAND_Ecc_P32o) << 5) +#define P64e(a) (TF(a & NAND_Ecc_P64e) << 6) +#define P64o(a) (TF(a & NAND_Ecc_P64o) << 7) + +#define P128e(a) (TF(a & NAND_Ecc_P128e) << 0) +#define P128o(a) (TF(a & NAND_Ecc_P128o) << 1) +#define P256e(a) (TF(a & NAND_Ecc_P256e) << 2) +#define P256o(a) (TF(a & NAND_Ecc_P256o) << 3) +#define P512e(a) (TF(a & NAND_Ecc_P512e) << 4) +#define P512o(a) (TF(a & NAND_Ecc_P512o) << 5) +#define P1024e(a) (TF(a & NAND_Ecc_P1024e) << 6) +#define P1024o(a) (TF(a & NAND_Ecc_P1024o) << 7) + +#define P8e_s(a) (TF(a & NAND_Ecc_P8e) << 0) +#define P8o_s(a) (TF(a & NAND_Ecc_P8o) << 1) +#define P16e_s(a) (TF(a & NAND_Ecc_P16e) << 2) +#define P16o_s(a) (TF(a & NAND_Ecc_P16o) << 3) +#define P1e_s(a) (TF(a & NAND_Ecc_P1e) << 4) +#define P1o_s(a) (TF(a & NAND_Ecc_P1o) << 5) +#define P2e_s(a) (TF(a & NAND_Ecc_P2e) << 6) +#define P2o_s(a) (TF(a & NAND_Ecc_P2o) << 7) + +#define P4e_s(a) (TF(a & NAND_Ecc_P4e) << 0) +#define P4o_s(a) (TF(a & NAND_Ecc_P4o) << 1) + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL }; +#endif + +struct omap_nand_info { + struct nand_hw_control controller; + struct omap_nand_platform_data *pdata; + struct mtd_info mtd; + struct mtd_partition *parts; + struct nand_chip nand; + struct platform_device *pdev; + + int gpmc_cs; + unsigned long phys_base; + void __iomem *gpmc_cs_baseaddr; + void __iomem *gpmc_baseaddr; +}; + +/** + * omap_nand_wp - This function enable or disable the Write Protect feature + * @mtd: MTD device structure + * @mode: WP ON/OFF + */ +static void omap_nand_wp(struct mtd_info *mtd, int mode) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + + unsigned long config = __raw_readl(info->gpmc_baseaddr + GPMC_CONFIG); + + if (mode) + config &= ~(NAND_WP_BIT); /* WP is ON */ + else + config |= (NAND_WP_BIT); /* WP is OFF */ + + __raw_writel(config, (info->gpmc_baseaddr + GPMC_CONFIG)); +} + +/** + * omap_hwcontrol - hardware specific access to control-lines + * @mtd: MTD device structure + * @cmd: command to device + * @ctrl: + * NAND_NCE: bit 0 -> don't care + * NAND_CLE: bit 1 -> Command Latch + * NAND_ALE: bit 2 -> Address Latch + * + * NOTE: boards may use different bits for these!! + */ +static void omap_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + switch (ctrl) { + case NAND_CTRL_CHANGE | NAND_CTRL_CLE: + info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_COMMAND; + info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_DATA; + break; + + case NAND_CTRL_CHANGE | NAND_CTRL_ALE: + info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_ADDRESS; + info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_DATA; + break; + + case NAND_CTRL_CHANGE | NAND_NCE: + info->nand.IO_ADDR_W = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_DATA; + info->nand.IO_ADDR_R = info->gpmc_cs_baseaddr + + GPMC_CS_NAND_DATA; + break; + } + + if (cmd != NAND_CMD_NONE) + __raw_writeb(cmd, info->nand.IO_ADDR_W); +} + +/** + * omap_read_buf16 - read data from NAND controller into buffer + * @mtd: MTD device structure + * @buf: buffer to store date + * @len: number of bytes to read + */ +static void omap_read_buf16(struct mtd_info *mtd, u_char *buf, int len) +{ + struct nand_chip *nand = mtd->priv; + + __raw_readsw(nand->IO_ADDR_R, buf, len / 2); +} + +/** + * omap_write_buf16 - write buffer to NAND controller + * @mtd: MTD device structure + * @buf: data buffer + * @len: number of bytes to write + */ +static void omap_write_buf16(struct mtd_info *mtd, const u_char * buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, + struct omap_nand_info, mtd); + u16 *p = (u16 *) buf; + + /* FIXME try bursts of writesw() or DMA ... */ + len >>= 1; + + while (len--) { + writew(*p++, info->nand.IO_ADDR_W); + + while (GPMC_BUF_EMPTY == (readl(info->gpmc_baseaddr + + GPMC_STATUS) & GPMC_BUF_FULL)) + ; + } +} +/** + * omap_verify_buf - Verify chip data against buffer + * @mtd: MTD device structure + * @buf: buffer containing the data to compare + * @len: number of bytes to compare + */ +static int omap_verify_buf(struct mtd_info *mtd, const u_char * buf, int len) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + u16 *p = (u16 *) buf; + + len >>= 1; + while (len--) { + if (*p++ != cpu_to_le16(readw(info->nand.IO_ADDR_R))) + return -EFAULT; + } + + return 0; +} + +#ifdef CONFIG_MTD_NAND_OMAP_HWECC +/** + * omap_hwecc_init - Initialize the HW ECC for NAND flash in GPMC controller + * @mtd: MTD device structure + */ +static void omap_hwecc_init(struct mtd_info *mtd) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_chip *chip = mtd->priv; + unsigned long val = 0x0; + + /* Read from ECC Control Register */ + val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONTROL); + /* Clear all ECC | Enable Reg1 */ + val = ((0x00000001<<8) | 0x00000001); + __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONTROL); + + /* Read from ECC Size Config Register */ + val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG); + /* ECCSIZE1=512 | Select eccResultsize[0-3] */ + val = ((((chip->ecc.size >> 1) - 1) << 22) | (0x0000000F)); + __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_SIZE_CONFIG); +} + +/** + * gen_true_ecc - This function will generate true ECC value + * @ecc_buf: buffer to store ecc code + * + * This generated true ECC value can be used when correcting + * data read from NAND flash memory core + */ +static void gen_true_ecc(u8 *ecc_buf) +{ + u32 tmp = ecc_buf[0] | (ecc_buf[1] << 16) | + ((ecc_buf[2] & 0xF0) << 20) | ((ecc_buf[2] & 0x0F) << 8); + + ecc_buf[0] = ~(P64o(tmp) | P64e(tmp) | P32o(tmp) | P32e(tmp) | + P16o(tmp) | P16e(tmp) | P8o(tmp) | P8e(tmp)); + ecc_buf[1] = ~(P1024o(tmp) | P1024e(tmp) | P512o(tmp) | P512e(tmp) | + P256o(tmp) | P256e(tmp) | P128o(tmp) | P128e(tmp)); + ecc_buf[2] = ~(P4o(tmp) | P4e(tmp) | P2o(tmp) | P2e(tmp) | P1o(tmp) | + P1e(tmp) | P2048o(tmp) | P2048e(tmp)); +} + +/** + * omap_compare_ecc - Detect (2 bits) and correct (1 bit) error in data + * @ecc_data1: ecc code from nand spare area + * @ecc_data2: ecc code from hardware register obtained from hardware ecc + * @page_data: page data + * + * This function compares two ECC's and indicates if there is an error. + * If the error can be corrected it will be corrected to the buffer. + */ +static int omap_compare_ecc(u8 *ecc_data1, /* read from NAND memory */ + u8 *ecc_data2, /* read from register */ + u8 *page_data) +{ + uint i; + u8 tmp0_bit[8], tmp1_bit[8], tmp2_bit[8]; + u8 comp0_bit[8], comp1_bit[8], comp2_bit[8]; + u8 ecc_bit[24]; + u8 ecc_sum = 0; + u8 find_bit = 0; + uint find_byte = 0; + int isEccFF; + + isEccFF = ((*(u32 *)ecc_data1 & 0xFFFFFF) == 0xFFFFFF); + + gen_true_ecc(ecc_data1); + gen_true_ecc(ecc_data2); + + for (i = 0; i <= 2; i++) { + *(ecc_data1 + i) = ~(*(ecc_data1 + i)); + *(ecc_data2 + i) = ~(*(ecc_data2 + i)); + } + + for (i = 0; i < 8; i++) { + tmp0_bit[i] = *ecc_data1 % 2; + *ecc_data1 = *ecc_data1 / 2; + } + + for (i = 0; i < 8; i++) { + tmp1_bit[i] = *(ecc_data1 + 1) % 2; + *(ecc_data1 + 1) = *(ecc_data1 + 1) / 2; + } + + for (i = 0; i < 8; i++) { + tmp2_bit[i] = *(ecc_data1 + 2) % 2; + *(ecc_data1 + 2) = *(ecc_data1 + 2) / 2; + } + + for (i = 0; i < 8; i++) { + comp0_bit[i] = *ecc_data2 % 2; + *ecc_data2 = *ecc_data2 / 2; + } + + for (i = 0; i < 8; i++) { + comp1_bit[i] = *(ecc_data2 + 1) % 2; + *(ecc_data2 + 1) = *(ecc_data2 + 1) / 2; + } + + for (i = 0; i < 8; i++) { + comp2_bit[i] = *(ecc_data2 + 2) % 2; + *(ecc_data2 + 2) = *(ecc_data2 + 2) / 2; + } + + for (i = 0; i < 6; i++) + ecc_bit[i] = tmp2_bit[i + 2] ^ comp2_bit[i + 2]; + + for (i = 0; i < 8; i++) + ecc_bit[i + 6] = tmp0_bit[i] ^ comp0_bit[i]; + + for (i = 0; i < 8; i++) + ecc_bit[i + 14] = tmp1_bit[i] ^ comp1_bit[i]; + + ecc_bit[22] = tmp2_bit[0] ^ comp2_bit[0]; + ecc_bit[23] = tmp2_bit[1] ^ comp2_bit[1]; + + for (i = 0; i < 24; i++) + ecc_sum += ecc_bit[i]; + + switch (ecc_sum) { + case 0: + /* Not reached because this function is not called if + * ECC values are equal + */ + return 0; + + case 1: + /* Uncorrectable error */ + DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR 1\n"); + return -1; + + case 11: + /* UN-Correctable error */ + DEBUG(MTD_DEBUG_LEVEL0, "ECC UNCORRECTED_ERROR B\n"); + return -1; + + case 12: + /* Correctable error */ + find_byte = (ecc_bit[23] << 8) + + (ecc_bit[21] << 7) + + (ecc_bit[19] << 6) + + (ecc_bit[17] << 5) + + (ecc_bit[15] << 4) + + (ecc_bit[13] << 3) + + (ecc_bit[11] << 2) + + (ecc_bit[9] << 1) + + ecc_bit[7]; + + find_bit = (ecc_bit[5] << 2) + (ecc_bit[3] << 1) + ecc_bit[1]; + + DEBUG(MTD_DEBUG_LEVEL0, "Correcting single bit ECC error at " + "offset: %d, bit: %d\n", find_byte, find_bit); + + page_data[find_byte] ^= (1 << find_bit); + + return 0; + default: + if (isEccFF) { + if (ecc_data2[0] == 0 && + ecc_data2[1] == 0 && + ecc_data2[2] == 0) + return 0; + } + DEBUG(MTD_DEBUG_LEVEL0, "UNCORRECTED_ERROR default\n"); + return -1; + } +} + +/** + * omap_correct_data - Compares the ECC read with HW generated ECC + * @mtd: MTD device structure + * @dat: page data + * @read_ecc: ecc read from nand flash + * @calc_ecc: ecc read from HW ECC registers + * + * Compares the ecc read from nand spare area with ECC registers values + * and if ECC's mismached, it will call 'omap_compare_ecc' for error detection + * and correction. + */ +static int omap_correct_data(struct mtd_info *mtd, u_char *dat, + u_char *read_ecc, u_char *calc_ecc) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + int blockCnt = 0, i = 0, ret = 0; + + /* Ex NAND_ECC_HW12_2048 */ + if ((info->nand.ecc.mode == NAND_ECC_HW) && + (info->nand.ecc.size == 2048)) + blockCnt = 4; + else + blockCnt = 1; + + for (i = 0; i < blockCnt; i++) { + if (memcmp(read_ecc, calc_ecc, 3) != 0) { + ret = omap_compare_ecc(read_ecc, calc_ecc, dat); + if (ret < 0) + return ret; + } + read_ecc += 3; + calc_ecc += 3; + dat += 512; + } + return 0; +} + +/** + * omap_calcuate_ecc - Generate non-inverted ECC bytes. + * @mtd: MTD device structure + * @dat: The pointer to data on which ecc is computed + * @ecc_code: The ecc_code buffer + * + * Using noninverted ECC can be considered ugly since writing a blank + * page ie. padding will clear the ECC bytes. This is no problem as long + * nobody is trying to write data on the seemingly unused page. Reading + * an erased page will produce an ECC mismatch between generated and read + * ECC bytes that has to be dealt with separately. + */ +static int omap_calculate_ecc(struct mtd_info *mtd, const u_char *dat, + u_char *ecc_code) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + unsigned long val = 0x0; + unsigned long reg; + + /* Start Reading from HW ECC1_Result = 0x200 */ + reg = (unsigned long)(info->gpmc_baseaddr + GPMC_ECC1_RESULT); + val = __raw_readl(reg); + *ecc_code++ = val; /* P128e, ..., P1e */ + *ecc_code++ = val >> 16; /* P128o, ..., P1o */ + /* P2048o, P1024o, P512o, P256o, P2048e, P1024e, P512e, P256e */ + *ecc_code++ = ((val >> 8) & 0x0f) | ((val >> 20) & 0xf0); + reg += 4; + + return 0; +} + +/** + * omap_enable_hwecc - This function enables the hardware ecc functionality + * @mtd: MTD device structure + * @mode: Read/Write mode + */ +static void omap_enable_hwecc(struct mtd_info *mtd, int mode) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + struct nand_chip *chip = mtd->priv; + unsigned int dev_width = (chip->options & NAND_BUSWIDTH_16) ? 1 : 0; + unsigned long val = __raw_readl(info->gpmc_baseaddr + GPMC_ECC_CONFIG); + + switch (mode) { + case NAND_ECC_READ: + __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL); + /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ + val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); + break; + case NAND_ECC_READSYN: + __raw_writel(0x100, info->gpmc_baseaddr + GPMC_ECC_CONTROL); + /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ + val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); + break; + case NAND_ECC_WRITE: + __raw_writel(0x101, info->gpmc_baseaddr + GPMC_ECC_CONTROL); + /* (ECC 16 or 8 bit col) | ( CS ) | ECC Enable */ + val = (dev_width << 7) | (info->gpmc_cs << 1) | (0x1); + break; + default: + DEBUG(MTD_DEBUG_LEVEL0, "Error: Unrecognized Mode[%d]!\n", + mode); + break; + } + + __raw_writel(val, info->gpmc_baseaddr + GPMC_ECC_CONFIG); +} +#endif + +/** + * omap_wait - wait until the command is done + * @mtd: MTD device structure + * @chip: NAND Chip structure + * + * Wait function is called during Program and erase operations and + * the way it is called from MTD layer, we should wait till the NAND + * chip is ready after the programming/erase operation has completed. + * + * Erase can take up to 400ms and program up to 20ms according to + * general NAND and SmartMedia specs + */ +static int omap_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct nand_chip *this = mtd->priv; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + unsigned long timeo = jiffies; + int status, state = this->state; + + if (state == FL_ERASING) + timeo += (HZ * 400) / 1000; + else + timeo += (HZ * 20) / 1000; + + this->IO_ADDR_W = (void *) info->gpmc_cs_baseaddr + + GPMC_CS_NAND_COMMAND; + this->IO_ADDR_R = (void *) info->gpmc_cs_baseaddr + GPMC_CS_NAND_DATA; + + __raw_writeb(NAND_CMD_STATUS & 0xFF, this->IO_ADDR_W); + + while (time_before(jiffies, timeo)) { + status = __raw_readb(this->IO_ADDR_R); + if (!(status & 0x40)) + break; + } + return status; +} + +/** + * omap_dev_ready - calls the platform specific dev_ready function + * @mtd: MTD device structure + */ +static int omap_dev_ready(struct mtd_info *mtd) +{ + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); + unsigned int val = __raw_readl(info->gpmc_baseaddr + GPMC_IRQ_STATUS); + + if ((val & 0x100) == 0x100) { + /* Clear IRQ Interrupt */ + val |= 0x100; + val &= ~(0x0); + __raw_writel(val, info->gpmc_baseaddr + GPMC_IRQ_STATUS); + } else { + unsigned int cnt = 0; + while (cnt++ < 0x1FF) { + if ((val & 0x100) == 0x100) + return 0; + val = __raw_readl(info->gpmc_baseaddr + + GPMC_IRQ_STATUS); + } + } + + return 1; +} + +static int __devinit omap_nand_probe(struct platform_device *pdev) +{ + struct omap_nand_info *info; + struct omap_nand_platform_data *pdata; + int err; + unsigned long val; + + + pdata = pdev->dev.platform_data; + if (pdata == NULL) { + dev_err(&pdev->dev, "platform data missing\n"); + return -ENODEV; + } + + info = kzalloc(sizeof(struct omap_nand_info), GFP_KERNEL); + if (!info) + return -ENOMEM; + + platform_set_drvdata(pdev, info); + + spin_lock_init(&info->controller.lock); + init_waitqueue_head(&info->controller.wq); + + info->pdev = pdev; + + info->gpmc_cs = pdata->cs; + info->gpmc_baseaddr = pdata->gpmc_baseaddr; + info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr; + + info->mtd.priv = &info->nand; + info->mtd.name = dev_name(&pdev->dev); + info->mtd.owner = THIS_MODULE; + + err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base); + if (err < 0) { + dev_err(&pdev->dev, "Cannot request GPMC CS\n"); + goto out_free_info; + } + + /* Enable RD PIN Monitoring Reg */ + if (pdata->dev_ready) { + val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1); + val |= WR_RD_PIN_MONITORING; + gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val); + } + + val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7); + val &= ~(0xf << 8); + val |= (0xc & 0xf) << 8; + gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val); + + /* NAND write protect off */ + omap_nand_wp(&info->mtd, NAND_WP_OFF); + + if (!request_mem_region(info->phys_base, NAND_IO_SIZE, + pdev->dev.driver->name)) { + err = -EBUSY; + goto out_free_cs; + } + + info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE); + if (!info->nand.IO_ADDR_R) { + err = -ENOMEM; + goto out_release_mem_region; + } + info->nand.controller = &info->controller; + + info->nand.IO_ADDR_W = info->nand.IO_ADDR_R; + info->nand.cmd_ctrl = omap_hwcontrol; + + /* REVISIT: only supports 16-bit NAND flash */ + + info->nand.read_buf = omap_read_buf16; + info->nand.write_buf = omap_write_buf16; + info->nand.verify_buf = omap_verify_buf; + + /* + * If RDY/BSY line is connected to OMAP then use the omap ready + * funcrtion and the generic nand_wait function which reads the status + * register after monitoring the RDY/BSY line.Otherwise use a standard + * chip delay which is slightly more than tR (AC Timing) of the NAND + * device and read status register until you get a failure or success + */ + if (pdata->dev_ready) { + info->nand.dev_ready = omap_dev_ready; + info->nand.chip_delay = 0; + } else { + info->nand.waitfunc = omap_wait; + info->nand.chip_delay = 50; + } + + info->nand.options |= NAND_SKIP_BBTSCAN; + if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000) + == 0x1000) + info->nand.options |= NAND_BUSWIDTH_16; + +#ifdef CONFIG_MTD_NAND_OMAP_HWECC + info->nand.ecc.bytes = 3; + info->nand.ecc.size = 512; + info->nand.ecc.calculate = omap_calculate_ecc; + info->nand.ecc.hwctl = omap_enable_hwecc; + info->nand.ecc.correct = omap_correct_data; + info->nand.ecc.mode = NAND_ECC_HW; + + /* init HW ECC */ + omap_hwecc_init(&info->mtd); +#else + info->nand.ecc.mode = NAND_ECC_SOFT; +#endif + + /* DIP switches on some boards change between 8 and 16 bit + * bus widths for flash. Try the other width if the first try fails. + */ + if (nand_scan(&info->mtd, 1)) { + info->nand.options ^= NAND_BUSWIDTH_16; + if (nand_scan(&info->mtd, 1)) { + err = -ENXIO; + goto out_release_mem_region; + } + } + +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(&info->mtd, part_probes, &info->parts, 0); + if (err > 0) + add_mtd_partitions(&info->mtd, info->parts, err); + else if (pdata->parts) + add_mtd_partitions(&info->mtd, pdata->parts, pdata->nr_parts); + else +#endif + add_mtd_device(&info->mtd); + + platform_set_drvdata(pdev, &info->mtd); + + return 0; + +out_release_mem_region: + release_mem_region(info->phys_base, NAND_IO_SIZE); +out_free_cs: + gpmc_cs_free(info->gpmc_cs); +out_free_info: + kfree(info); + + return err; +} + +static int omap_nand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct omap_nand_info *info = mtd->priv; + + platform_set_drvdata(pdev, NULL); + /* Release NAND device, its internal structures and partitions */ + nand_release(&info->mtd); + iounmap(info->nand.IO_ADDR_R); + kfree(&info->mtd); + return 0; +} + +static struct platform_driver omap_nand_driver = { + .probe = omap_nand_probe, + .remove = omap_nand_remove, + .driver = { + .name = DRIVER_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init omap_nand_init(void) +{ + printk(KERN_INFO "%s driver initializing\n", DRIVER_NAME); + return platform_driver_register(&omap_nand_driver); +} + +static void __exit omap_nand_exit(void) +{ + platform_driver_unregister(&omap_nand_driver); +} + +module_init(omap_nand_init); +module_exit(omap_nand_exit); + +MODULE_ALIAS(DRIVER_NAME); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Glue layer for NAND flash on TI OMAP boards"); -- cgit v1.2.3 From 5988af2319781bc8e0ce418affec4e09cfa77907 Mon Sep 17 00:00:00 2001 From: Rohit Hagargundgi Date: Tue, 12 May 2009 13:46:57 -0700 Subject: mtd: Flex-OneNAND support Add support for Samsung Flex-OneNAND devices. Flex-OneNAND combines SLC and MLC technologies into a single device. SLC area provides increased reliability and speed, suitable for storing code such as bootloader, kernel and root file system. MLC area provides high density and is suitable for storing user data. SLC and MLC regions can be configured through kernel parameter. [akpm@linux-foundation.org: export flexoand_region and onenand_addr] Signed-off-by: Rohit Hagargundgi Signed-off-by: Kyungmin Park Cc: Vishak G Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- Documentation/kernel-parameters.txt | 10 + drivers/mtd/onenand/onenand_base.c | 857 ++++++++++++++++++++++++++++++++---- drivers/mtd/onenand/onenand_bbt.c | 14 +- drivers/mtd/onenand/onenand_sim.c | 81 +++- include/linux/mtd/onenand.h | 18 + include/linux/mtd/onenand_regs.h | 20 +- 6 files changed, 913 insertions(+), 87 deletions(-) diff --git a/Documentation/kernel-parameters.txt b/Documentation/kernel-parameters.txt index e87bdbfbcc75..12df135f8af9 100644 --- a/Documentation/kernel-parameters.txt +++ b/Documentation/kernel-parameters.txt @@ -1380,6 +1380,16 @@ and is between 256 and 4096 characters. It is defined in the file mtdparts= [MTD] See drivers/mtd/cmdlinepart.c. + onenand.bdry= [HW,MTD] Flex-OneNAND Boundary Configuration + + Format: [die0_boundary][,die0_lock][,die1_boundary][,die1_lock] + + boundary - index of last SLC block on Flex-OneNAND. + The remaining blocks are configured as MLC blocks. + lock - Configure if Flex-OneNAND boundary should be locked. + Once locked, the boundary cannot be changed. + 1 indicates lock status, 0 indicates unlock status. + mtdset= [ARM] ARM/S3C2412 JIVE boot control diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 2346857a275d..8d4c9c253732 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -9,6 +9,10 @@ * auto-placement support, read-while load support, various fixes * Copyright (C) Nokia Corporation, 2007 * + * Vishak G , Rohit Hagargundgi + * Flex-OneNAND support + * Copyright (C) Samsung Electronics, 2008 + * * 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. @@ -27,6 +31,30 @@ #include +/* Default Flex-OneNAND boundary and lock respectively */ +static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; + +/** + * onenand_oob_128 - oob info for Flex-Onenand with 4KB page + * For now, we expose only 64 out of 80 ecc bytes + */ +static struct nand_ecclayout onenand_oob_128 = { + .eccbytes = 64, + .eccpos = { + 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, + 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, + 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, + 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, + 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, + 102, 103, 104, 105 + }, + .oobfree = { + {2, 4}, {18, 4}, {34, 4}, {50, 4}, + {66, 4}, {82, 4}, {98, 4}, {114, 4} + } +}; + /** * onenand_oob_64 - oob info for large (2KB) page */ @@ -65,6 +93,14 @@ static const unsigned char ffchars[] = { 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 48 */ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 64 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 80 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 96 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 112 */ + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, + 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, /* 128 */ }; /** @@ -170,6 +206,70 @@ static int onenand_buffer_address(int dataram1, int sectors, int count) return ((bsa << ONENAND_BSA_SHIFT) | bsc); } +/** + * flexonenand_block- For given address return block number + * @param this - OneNAND device structure + * @param addr - Address for which block number is needed + */ +static unsigned flexonenand_block(struct onenand_chip *this, loff_t addr) +{ + unsigned boundary, blk, die = 0; + + if (ONENAND_IS_DDP(this) && addr >= this->diesize[0]) { + die = 1; + addr -= this->diesize[0]; + } + + boundary = this->boundary[die]; + + blk = addr >> (this->erase_shift - 1); + if (blk > boundary) + blk = (blk + boundary + 1) >> 1; + + blk += die ? this->density_mask : 0; + return blk; +} + +inline unsigned onenand_block(struct onenand_chip *this, loff_t addr) +{ + if (!FLEXONENAND(this)) + return addr >> this->erase_shift; + return flexonenand_block(this, addr); +} + +/** + * flexonenand_addr - Return address of the block + * @this: OneNAND device structure + * @block: Block number on Flex-OneNAND + * + * Return address of the block + */ +static loff_t flexonenand_addr(struct onenand_chip *this, int block) +{ + loff_t ofs = 0; + int die = 0, boundary; + + if (ONENAND_IS_DDP(this) && block >= this->density_mask) { + block -= this->density_mask; + die = 1; + ofs = this->diesize[0]; + } + + boundary = this->boundary[die]; + ofs += (loff_t)block << (this->erase_shift - 1); + if (block > (boundary + 1)) + ofs += (loff_t)(block - boundary - 1) << (this->erase_shift - 1); + return ofs; +} + +loff_t onenand_addr(struct onenand_chip *this, int block) +{ + if (!FLEXONENAND(this)) + return (loff_t)block << this->erase_shift; + return flexonenand_addr(this, block); +} +EXPORT_SYMBOL(onenand_addr); + /** * onenand_get_density - [DEFAULT] Get OneNAND density * @param dev_id OneNAND device ID @@ -182,6 +282,22 @@ static inline int onenand_get_density(int dev_id) return (density & ONENAND_DEVICE_DENSITY_MASK); } +/** + * flexonenand_region - [Flex-OneNAND] Return erase region of addr + * @param mtd MTD device structure + * @param addr address whose erase region needs to be identified + */ +int flexonenand_region(struct mtd_info *mtd, loff_t addr) +{ + int i; + + for (i = 0; i < mtd->numeraseregions; i++) + if (addr < mtd->eraseregions[i].offset) + break; + return i - 1; +} +EXPORT_SYMBOL(flexonenand_region); + /** * onenand_command - [DEFAULT] Send command to OneNAND device * @param mtd MTD device structure @@ -207,16 +323,28 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le page = -1; break; + case FLEXONENAND_CMD_PI_ACCESS: + /* addr contains die index */ + block = addr * this->density_mask; + page = -1; + break; + case ONENAND_CMD_ERASE: case ONENAND_CMD_BUFFERRAM: case ONENAND_CMD_OTP_ACCESS: - block = (int) (addr >> this->erase_shift); + block = onenand_block(this, addr); page = -1; break; + case FLEXONENAND_CMD_READ_PI: + cmd = ONENAND_CMD_READ; + block = addr * this->density_mask; + page = 0; + break; + default: - block = (int) (addr >> this->erase_shift); - page = (int) (addr >> this->page_shift); + block = onenand_block(this, addr); + page = (int) (addr - onenand_addr(this, block)) >> this->page_shift; if (ONENAND_IS_2PLANE(this)) { /* Make the even block number */ @@ -236,7 +364,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); - if (ONENAND_IS_2PLANE(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this)) /* It is always BufferRAM0 */ ONENAND_SET_BUFFERRAM0(this); else @@ -258,13 +386,18 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le if (page != -1) { /* Now we use page size operation */ - int sectors = 4, count = 4; + int sectors = 0, count = 0; int dataram; switch (cmd) { + case FLEXONENAND_CMD_RECOVER_LSB: case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: - dataram = ONENAND_SET_NEXT_BUFFERRAM(this); + if (ONENAND_IS_MLC(this)) + /* It is always BufferRAM0 */ + dataram = ONENAND_SET_BUFFERRAM0(this); + else + dataram = ONENAND_SET_NEXT_BUFFERRAM(this); break; default: @@ -292,6 +425,30 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le return 0; } +/** + * onenand_read_ecc - return ecc status + * @param this onenand chip structure + */ +static inline int onenand_read_ecc(struct onenand_chip *this) +{ + int ecc, i, result = 0; + + if (!FLEXONENAND(this)) + return this->read_word(this->base + ONENAND_REG_ECC_STATUS); + + for (i = 0; i < 4; i++) { + ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i); + if (likely(!ecc)) + continue; + if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) + return ONENAND_ECC_2BIT_ALL; + else + result = ONENAND_ECC_1BIT_ALL; + } + + return result; +} + /** * onenand_wait - [DEFAULT] wait until the command is done * @param mtd MTD device structure @@ -331,14 +488,14 @@ static int onenand_wait(struct mtd_info *mtd, int state) * power off recovery (POR) test, it should read ECC status first */ if (interrupt & ONENAND_INT_READ) { - int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); + int ecc = onenand_read_ecc(this); if (ecc) { if (ecc & ONENAND_ECC_2BIT_ALL) { printk(KERN_ERR "onenand_wait: ECC error = 0x%04x\n", ecc); mtd->ecc_stats.failed++; return -EBADMSG; } else if (ecc & ONENAND_ECC_1BIT_ALL) { - printk(KERN_INFO "onenand_wait: correctable ECC error = 0x%04x\n", ecc); + printk(KERN_DEBUG "onenand_wait: correctable ECC error = 0x%04x\n", ecc); mtd->ecc_stats.corrected++; } } @@ -656,7 +813,7 @@ static int onenand_check_bufferram(struct mtd_info *mtd, loff_t addr) if (found && ONENAND_IS_DDP(this)) { /* Select DataRAM for DDP */ - int block = (int) (addr >> this->erase_shift); + int block = onenand_block(this, addr); int value = onenand_bufferram_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS2); } @@ -815,6 +972,149 @@ static int onenand_transfer_auto_oob(struct mtd_info *mtd, uint8_t *buf, int col return 0; } +/** + * onenand_recover_lsb - [Flex-OneNAND] Recover LSB page data + * @param mtd MTD device structure + * @param addr address to recover + * @param status return value from onenand_wait / onenand_bbt_wait + * + * MLC NAND Flash cell has paired pages - LSB page and MSB page. LSB page has + * lower page address and MSB page has higher page address in paired pages. + * If power off occurs during MSB page program, the paired LSB page data can + * become corrupt. LSB page recovery read is a way to read LSB page though page + * data are corrupted. When uncorrectable error occurs as a result of LSB page + * read after power up, issue LSB page recovery read. + */ +static int onenand_recover_lsb(struct mtd_info *mtd, loff_t addr, int status) +{ + struct onenand_chip *this = mtd->priv; + int i; + + /* Recovery is only for Flex-OneNAND */ + if (!FLEXONENAND(this)) + return status; + + /* check if we failed due to uncorrectable error */ + if (status != -EBADMSG && status != ONENAND_BBT_READ_ECC_ERROR) + return status; + + /* check if address lies in MLC region */ + i = flexonenand_region(mtd, addr); + if (mtd->eraseregions[i].erasesize < (1 << this->erase_shift)) + return status; + + /* We are attempting to reread, so decrement stats.failed + * which was incremented by onenand_wait due to read failure + */ + printk(KERN_INFO "onenand_recover_lsb: Attempting to recover from uncorrectable read\n"); + mtd->ecc_stats.failed--; + + /* Issue the LSB page recovery command */ + this->command(mtd, FLEXONENAND_CMD_RECOVER_LSB, addr, this->writesize); + return this->wait(mtd, FL_READING); +} + +/** + * onenand_mlc_read_ops_nolock - MLC OneNAND read main and/or out-of-band + * @param mtd MTD device structure + * @param from offset to read from + * @param ops: oob operation description structure + * + * MLC OneNAND / Flex-OneNAND has 4KB page size and 4KB dataram. + * So, read-while-load is not present. + */ +static int onenand_mlc_read_ops_nolock(struct mtd_info *mtd, loff_t from, + struct mtd_oob_ops *ops) +{ + struct onenand_chip *this = mtd->priv; + struct mtd_ecc_stats stats; + size_t len = ops->len; + size_t ooblen = ops->ooblen; + u_char *buf = ops->datbuf; + u_char *oobbuf = ops->oobbuf; + int read = 0, column, thislen; + int oobread = 0, oobcolumn, thisooblen, oobsize; + int ret = 0; + int writesize = this->writesize; + + DEBUG(MTD_DEBUG_LEVEL3, "onenand_mlc_read_ops_nolock: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + + if (ops->mode == MTD_OOB_AUTO) + oobsize = this->ecclayout->oobavail; + else + oobsize = mtd->oobsize; + + oobcolumn = from & (mtd->oobsize - 1); + + /* Do not allow reads past end of device */ + if (from + len > mtd->size) { + printk(KERN_ERR "onenand_mlc_read_ops_nolock: Attempt read beyond end of device\n"); + ops->retlen = 0; + ops->oobretlen = 0; + return -EINVAL; + } + + stats = mtd->ecc_stats; + + while (read < len) { + cond_resched(); + + thislen = min_t(int, writesize, len - read); + + column = from & (writesize - 1); + if (column + thislen > writesize) + thislen = writesize - column; + + if (!onenand_check_bufferram(mtd, from)) { + this->command(mtd, ONENAND_CMD_READ, from, writesize); + + ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + onenand_update_bufferram(mtd, from, !ret); + if (ret == -EBADMSG) + ret = 0; + } + + this->read_bufferram(mtd, ONENAND_DATARAM, buf, column, thislen); + if (oobbuf) { + thisooblen = oobsize - oobcolumn; + thisooblen = min_t(int, thisooblen, ooblen - oobread); + + if (ops->mode == MTD_OOB_AUTO) + onenand_transfer_auto_oob(mtd, oobbuf, oobcolumn, thisooblen); + else + this->read_bufferram(mtd, ONENAND_SPARERAM, oobbuf, oobcolumn, thisooblen); + oobread += thisooblen; + oobbuf += thisooblen; + oobcolumn = 0; + } + + read += thislen; + if (read == len) + break; + + from += thislen; + buf += thislen; + } + + /* + * Return success, if no ECC failures, else -EBADMSG + * fs driver will take care of that, because + * retlen == desired len and result == -EBADMSG + */ + ops->retlen = read; + ops->oobretlen = oobread; + + if (ret) + return ret; + + if (mtd->ecc_stats.failed - stats.failed) + return -EBADMSG; + + return mtd->ecc_stats.corrected - stats.corrected ? -EUCLEAN : 0; +} + /** * onenand_read_ops_nolock - [OneNAND Interface] OneNAND read main and/or out-of-band * @param mtd MTD device structure @@ -962,7 +1262,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, size_t len = ops->ooblen; mtd_oob_mode_t mode = ops->mode; u_char *buf = ops->oobbuf; - int ret = 0; + int ret = 0, readcmd; from += ops->ooboffs; @@ -993,17 +1293,22 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, stats = mtd->ecc_stats; + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + while (read < len) { cond_resched(); thislen = oobsize - column; thislen = min_t(int, thislen, len); - this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); ret = this->wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + if (ret && ret != -EBADMSG) { printk(KERN_ERR "onenand_read_oob_nolock: read failed = 0x%x\n", ret); break; @@ -1053,6 +1358,7 @@ static int onenand_read_oob_nolock(struct mtd_info *mtd, loff_t from, static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { + struct onenand_chip *this = mtd->priv; struct mtd_oob_ops ops = { .len = len, .ooblen = 0, @@ -1062,7 +1368,9 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, int ret; onenand_get_device(mtd, FL_READING); - ret = onenand_read_ops_nolock(mtd, from, &ops); + ret = ONENAND_IS_MLC(this) ? + onenand_mlc_read_ops_nolock(mtd, from, &ops) : + onenand_read_ops_nolock(mtd, from, &ops); onenand_release_device(mtd); *retlen = ops.retlen; @@ -1080,6 +1388,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, static int onenand_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops) { + struct onenand_chip *this = mtd->priv; int ret; switch (ops->mode) { @@ -1094,7 +1403,9 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, onenand_get_device(mtd, FL_READING); if (ops->datbuf) - ret = onenand_read_ops_nolock(mtd, from, ops); + ret = ONENAND_IS_MLC(this) ? + onenand_mlc_read_ops_nolock(mtd, from, ops) : + onenand_read_ops_nolock(mtd, from, ops); else ret = onenand_read_oob_nolock(mtd, from, ops); onenand_release_device(mtd); @@ -1128,11 +1439,11 @@ static int onenand_bbt_wait(struct mtd_info *mtd, int state) ctrl = this->read_word(this->base + ONENAND_REG_CTRL_STATUS); if (interrupt & ONENAND_INT_READ) { - int ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS); + int ecc = onenand_read_ecc(this); if (ecc & ONENAND_ECC_2BIT_ALL) { printk(KERN_INFO "onenand_bbt_wait: ecc error = 0x%04x" ", controller error 0x%04x\n", ecc, ctrl); - return ONENAND_BBT_READ_ERROR; + return ONENAND_BBT_READ_ECC_ERROR; } } else { printk(KERN_ERR "onenand_bbt_wait: read timeout!" @@ -1163,7 +1474,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, { struct onenand_chip *this = mtd->priv; int read = 0, thislen, column; - int ret = 0; + int ret = 0, readcmd; size_t len = ops->ooblen; u_char *buf = ops->oobbuf; @@ -1183,17 +1494,22 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, column = from & (mtd->oobsize - 1); + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; + while (read < len) { cond_resched(); thislen = mtd->oobsize - column; thislen = min_t(int, thislen, len); - this->command(mtd, ONENAND_CMD_READOOB, from, mtd->oobsize); + this->command(mtd, readcmd, from, mtd->oobsize); onenand_update_bufferram(mtd, from, 0); ret = onenand_bbt_wait(mtd, FL_READING); + if (unlikely(ret)) + ret = onenand_recover_lsb(mtd, from, ret); + if (ret) break; @@ -1230,9 +1546,11 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to { struct onenand_chip *this = mtd->priv; u_char *oob_buf = this->oob_buf; - int status, i; + int status, i, readcmd; + + readcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_READ : ONENAND_CMD_READOOB; - this->command(mtd, ONENAND_CMD_READOOB, to, mtd->oobsize); + this->command(mtd, readcmd, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); status = this->wait(mtd, FL_READING); if (status) @@ -1633,7 +1951,7 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, { struct onenand_chip *this = mtd->priv; int column, ret = 0, oobsize; - int written = 0; + int written = 0, oobcmd; u_char *oobbuf; size_t len = ops->ooblen; const u_char *buf = ops->oobbuf; @@ -1675,6 +1993,8 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, oobbuf = this->oob_buf; + oobcmd = ONENAND_IS_MLC(this) ? ONENAND_CMD_PROG : ONENAND_CMD_PROGOOB; + /* Loop until all data write */ while (written < len) { int thislen = min_t(int, oobsize, len - written); @@ -1692,7 +2012,14 @@ static int onenand_write_oob_nolock(struct mtd_info *mtd, loff_t to, memcpy(oobbuf + column, buf, thislen); this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize); - this->command(mtd, ONENAND_CMD_PROGOOB, to, mtd->oobsize); + if (ONENAND_IS_MLC(this)) { + /* Set main area of DataRAM to 0xff*/ + memset(this->page_buf, 0xff, mtd->writesize); + this->write_bufferram(mtd, ONENAND_DATARAM, + this->page_buf, 0, mtd->writesize); + } + + this->command(mtd, oobcmd, to, mtd->oobsize); onenand_update_bufferram(mtd, to, 0); if (ONENAND_IS_2PLANE(this)) { @@ -1815,29 +2142,48 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) { struct onenand_chip *this = mtd->priv; unsigned int block_size; - loff_t addr; - int len; - int ret = 0; + loff_t addr = instr->addr; + loff_t len = instr->len; + int ret = 0, i; + struct mtd_erase_region_info *region = NULL; + loff_t region_end = 0; DEBUG(MTD_DEBUG_LEVEL3, "onenand_erase: start = 0x%012llx, len = %llu\n", (unsigned long long) instr->addr, (unsigned long long) instr->len); - block_size = (1 << this->erase_shift); - - /* Start address must align on block boundary */ - if (unlikely(instr->addr & (block_size - 1))) { - printk(KERN_ERR "onenand_erase: Unaligned address\n"); + /* Do not allow erase past end of device */ + if (unlikely((len + addr) > mtd->size)) { + printk(KERN_ERR "onenand_erase: Erase past end of device\n"); return -EINVAL; } - /* Length must align on block boundary */ - if (unlikely(instr->len & (block_size - 1))) { - printk(KERN_ERR "onenand_erase: Length not block aligned\n"); - return -EINVAL; + if (FLEXONENAND(this)) { + /* Find the eraseregion of this address */ + i = flexonenand_region(mtd, addr); + region = &mtd->eraseregions[i]; + + block_size = region->erasesize; + region_end = region->offset + region->erasesize * region->numblocks; + + /* Start address within region must align on block boundary. + * Erase region's start offset is always block start address. + */ + if (unlikely((addr - region->offset) & (block_size - 1))) { + printk(KERN_ERR "onenand_erase: Unaligned address\n"); + return -EINVAL; + } + } else { + block_size = 1 << this->erase_shift; + + /* Start address must align on block boundary */ + if (unlikely(addr & (block_size - 1))) { + printk(KERN_ERR "onenand_erase: Unaligned address\n"); + return -EINVAL; + } } - /* Do not allow erase past end of device */ - if (unlikely((instr->len + instr->addr) > mtd->size)) { - printk(KERN_ERR "onenand_erase: Erase past end of device\n"); + /* Length must align on block boundary */ + if (unlikely(len & (block_size - 1))) { + printk(KERN_ERR "onenand_erase: Length not block aligned\n"); return -EINVAL; } @@ -1847,9 +2193,6 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) onenand_get_device(mtd, FL_ERASING); /* Loop throught the pages */ - len = instr->len; - addr = instr->addr; - instr->state = MTD_ERASING; while (len) { @@ -1869,7 +2212,8 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) ret = this->wait(mtd, FL_ERASING); /* Check, if it is write protected */ if (ret) { - printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", (unsigned) (addr >> this->erase_shift)); + printk(KERN_ERR "onenand_erase: Failed erase, block %d\n", + onenand_block(this, addr)); instr->state = MTD_ERASE_FAILED; instr->fail_addr = addr; goto erase_exit; @@ -1877,6 +2221,22 @@ static int onenand_erase(struct mtd_info *mtd, struct erase_info *instr) len -= block_size; addr += block_size; + + if (addr == region_end) { + if (!len) + break; + region++; + + block_size = region->erasesize; + region_end = region->offset + region->erasesize * region->numblocks; + + if (len & (block_size - 1)) { + /* FIXME: This should be handled at MTD partitioning level. */ + printk(KERN_ERR "onenand_erase: Unaligned address\n"); + goto erase_exit; + } + } + } instr->state = MTD_ERASE_DONE; @@ -1955,13 +2315,17 @@ static int onenand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) int block; /* Get block number */ - block = ((int) ofs) >> bbm->bbt_erase_shift; + block = onenand_block(this, ofs); 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 onenand_write_oob_nolock(mtd, ofs, &ops); + /* FIXME : What to do when marking SLC block in partition + * with MLC erasesize? For now, it is not advisable to + * create partitions containing both SLC and MLC regions. + */ + return onenand_write_oob_nolock(mtd, ofs, &ops); } /** @@ -2005,8 +2369,8 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int int start, end, block, value, status; int wp_status_mask; - start = ofs >> this->erase_shift; - end = len >> this->erase_shift; + start = onenand_block(this, ofs); + end = onenand_block(this, ofs + len) - 1; if (cmd == ONENAND_CMD_LOCK) wp_status_mask = ONENAND_WP_LS; @@ -2018,7 +2382,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int /* Set start block address */ this->write_word(start, this->base + ONENAND_REG_START_BLOCK_ADDRESS); /* Set end block address */ - this->write_word(start + end - 1, this->base + ONENAND_REG_END_BLOCK_ADDRESS); + this->write_word(end, this->base + ONENAND_REG_END_BLOCK_ADDRESS); /* Write lock command */ this->command(mtd, cmd, 0, 0); @@ -2039,7 +2403,7 @@ static int onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, size_t len, int } /* Block lock scheme */ - for (block = start; block < start + end; block++) { + for (block = start; block < end + 1; block++) { /* Set block address */ value = onenand_block_address(this, block); this->write_word(value, this->base + ONENAND_REG_START_ADDRESS1); @@ -2147,7 +2511,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) { struct onenand_chip *this = mtd->priv; loff_t ofs = 0; - size_t len = this->chipsize; + loff_t len = mtd->size; if (this->options & ONENAND_HAS_UNLOCK_ALL) { /* Set start block address */ @@ -2168,7 +2532,7 @@ static void onenand_unlock_all(struct mtd_info *mtd) return; /* Workaround for all block unlock in DDP */ - if (ONENAND_IS_DDP(this)) { + if (ONENAND_IS_DDP(this) && !FLEXONENAND(this)) { /* All blocks on another chip */ ofs = this->chipsize >> 1; len = this->chipsize >> 1; @@ -2210,7 +2574,9 @@ static int do_otp_read(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_read_ops_nolock(mtd, from, &ops); + ret = ONENAND_IS_MLC(this) ? + onenand_mlc_read_ops_nolock(mtd, from, &ops) : + onenand_read_ops_nolock(mtd, from, &ops); /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2277,21 +2643,32 @@ static int do_otp_lock(struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) { struct onenand_chip *this = mtd->priv; - struct mtd_oob_ops ops = { - .mode = MTD_OOB_PLACE, - .ooblen = len, - .oobbuf = buf, - .ooboffs = 0, - }; + struct mtd_oob_ops ops; int ret; /* Enter OTP access mode */ this->command(mtd, ONENAND_CMD_OTP_ACCESS, 0, 0); this->wait(mtd, FL_OTPING); - ret = onenand_write_oob_nolock(mtd, from, &ops); - - *retlen = ops.oobretlen; + if (FLEXONENAND(this)) { + /* + * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of + * main area of page 49. + */ + ops.len = mtd->writesize; + ops.ooblen = 0; + ops.datbuf = buf; + ops.oobbuf = NULL; + ret = onenand_write_ops_nolock(mtd, mtd->writesize * 49, &ops); + *retlen = ops.retlen; + } else { + ops.mode = MTD_OOB_PLACE; + ops.ooblen = len; + ops.oobbuf = buf; + ops.ooboffs = 0; + ret = onenand_write_oob_nolock(mtd, from, &ops); + *retlen = ops.oobretlen; + } /* Exit OTP access mode */ this->command(mtd, ONENAND_CMD_RESET, 0, 0); @@ -2475,27 +2852,34 @@ static int onenand_lock_user_prot_reg(struct mtd_info *mtd, loff_t from, size_t len) { struct onenand_chip *this = mtd->priv; - u_char *oob_buf = this->oob_buf; + u_char *buf = FLEXONENAND(this) ? this->page_buf : this->oob_buf; size_t retlen; int ret; - memset(oob_buf, 0xff, mtd->oobsize); + memset(buf, 0xff, FLEXONENAND(this) ? this->writesize + : mtd->oobsize); /* * Note: OTP lock operation * OTP block : 0xXXFC * 1st block : 0xXXF3 (If chip support) * Both : 0xXXF0 (If chip support) */ - oob_buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; + if (FLEXONENAND(this)) + buf[FLEXONENAND_OTP_LOCK_OFFSET] = 0xFC; + else + buf[ONENAND_OTP_LOCK_OFFSET] = 0xFC; /* * Write lock mark to 8th word of sector0 of page0 of the spare0. * We write 16 bytes spare area instead of 2 bytes. + * For Flex-OneNAND, we write lock mark to 1st word of sector 4 of + * main area of page 49. */ + from = 0; - len = 16; + len = FLEXONENAND(this) ? mtd->writesize : 16; - ret = onenand_otp_walk(mtd, from, len, &retlen, oob_buf, do_otp_lock, MTD_OTP_USER); + ret = onenand_otp_walk(mtd, from, len, &retlen, buf, do_otp_lock, MTD_OTP_USER); return ret ? : retlen; } @@ -2542,6 +2926,14 @@ static void onenand_check_features(struct mtd_info *mtd) break; } + if (ONENAND_IS_MLC(this)) + this->options &= ~ONENAND_HAS_2PLANE; + + if (FLEXONENAND(this)) { + this->options &= ~ONENAND_HAS_CONT_LOCK; + this->options |= ONENAND_HAS_UNLOCK_ALL; + } + if (this->options & ONENAND_HAS_CONT_LOCK) printk(KERN_DEBUG "Lock scheme is Continuous Lock\n"); if (this->options & ONENAND_HAS_UNLOCK_ALL) @@ -2559,14 +2951,16 @@ static void onenand_check_features(struct mtd_info *mtd) */ static void onenand_print_device_info(int device, int version) { - int vcc, demuxed, ddp, density; + int vcc, demuxed, ddp, density, flexonenand; vcc = device & ONENAND_DEVICE_VCC_MASK; demuxed = device & ONENAND_DEVICE_IS_DEMUX; ddp = device & ONENAND_DEVICE_IS_DDP; density = onenand_get_density(device); - printk(KERN_INFO "%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", - demuxed ? "" : "Muxed ", + flexonenand = device & DEVICE_IS_FLEXONENAND; + printk(KERN_INFO "%s%sOneNAND%s %dMB %sV 16-bit (0x%02x)\n", + demuxed ? "" : "Muxed ", + flexonenand ? "Flex-" : "", ddp ? "(DDP)" : "", (16 << density), vcc ? "2.65/3.3" : "1.8", @@ -2605,6 +2999,280 @@ static int onenand_check_maf(int manuf) return (i == size); } +/** +* flexonenand_get_boundary - Reads the SLC boundary +* @param onenand_info - onenand info structure +**/ +static int flexonenand_get_boundary(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + unsigned die, bdry; + int ret, syscfg, locked; + + /* Disable ECC */ + syscfg = this->read_word(this->base + ONENAND_REG_SYS_CFG1); + this->write_word((syscfg | 0x0100), this->base + ONENAND_REG_SYS_CFG1); + + for (die = 0; die < this->dies; die++) { + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + ret = this->wait(mtd, FL_READING); + + bdry = this->read_word(this->base + ONENAND_DATARAM); + if ((bdry >> FLEXONENAND_PI_UNLOCK_SHIFT) == 3) + locked = 0; + else + locked = 1; + this->boundary[die] = bdry & FLEXONENAND_PI_MASK; + + this->command(mtd, ONENAND_CMD_RESET, 0, 0); + ret = this->wait(mtd, FL_RESETING); + + printk(KERN_INFO "Die %d boundary: %d%s\n", die, + this->boundary[die], locked ? "(Locked)" : "(Unlocked)"); + } + + /* Enable ECC */ + this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + return 0; +} + +/** + * flexonenand_get_size - Fill up fields in onenand_chip and mtd_info + * boundary[], diesize[], mtd->size, mtd->erasesize + * @param mtd - MTD device structure + */ +static void flexonenand_get_size(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + int die, i, eraseshift, density; + int blksperdie, maxbdry; + loff_t ofs; + + density = onenand_get_density(this->device_id); + blksperdie = ((loff_t)(16 << density) << 20) >> (this->erase_shift); + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + maxbdry = blksperdie - 1; + eraseshift = this->erase_shift - 1; + + mtd->numeraseregions = this->dies << 1; + + /* This fills up the device boundary */ + flexonenand_get_boundary(mtd); + die = ofs = 0; + i = -1; + for (; die < this->dies; die++) { + if (!die || this->boundary[die-1] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = + this->boundary[die] + 1; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift++; + } else { + mtd->numeraseregions -= 1; + mtd->eraseregions[i].numblocks += + this->boundary[die] + 1; + ofs += (this->boundary[die] + 1) << (eraseshift - 1); + } + if (this->boundary[die] != maxbdry) { + i++; + mtd->eraseregions[i].offset = ofs; + mtd->eraseregions[i].erasesize = 1 << eraseshift; + mtd->eraseregions[i].numblocks = maxbdry ^ + this->boundary[die]; + ofs += mtd->eraseregions[i].numblocks << eraseshift; + eraseshift--; + } else + mtd->numeraseregions -= 1; + } + + /* Expose MLC erase size except when all blocks are SLC */ + mtd->erasesize = 1 << this->erase_shift; + if (mtd->numeraseregions == 1) + mtd->erasesize >>= 1; + + printk(KERN_INFO "Device has %d eraseregions\n", mtd->numeraseregions); + for (i = 0; i < mtd->numeraseregions; i++) + printk(KERN_INFO "[offset: 0x%08x, erasesize: 0x%05x," + " numblocks: %04u]\n", + (unsigned int) mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + + for (die = 0, mtd->size = 0; die < this->dies; die++) { + this->diesize[die] = (loff_t)blksperdie << this->erase_shift; + this->diesize[die] -= (loff_t)(this->boundary[die] + 1) + << (this->erase_shift - 1); + mtd->size += this->diesize[die]; + } +} + +/** + * flexonenand_check_blocks_erased - Check if blocks are erased + * @param mtd_info - mtd info structure + * @param start - first erase block to check + * @param end - last erase block to check + * + * Converting an unerased block from MLC to SLC + * causes byte values to change. Since both data and its ECC + * have changed, reads on the block give uncorrectable error. + * This might lead to the block being detected as bad. + * + * Avoid this by ensuring that the block to be converted is + * erased. + */ +static int flexonenand_check_blocks_erased(struct mtd_info *mtd, int start, int end) +{ + struct onenand_chip *this = mtd->priv; + int i, ret; + int block; + struct mtd_oob_ops ops = { + .mode = MTD_OOB_PLACE, + .ooboffs = 0, + .ooblen = mtd->oobsize, + .datbuf = NULL, + .oobbuf = this->oob_buf, + }; + loff_t addr; + + printk(KERN_DEBUG "Check blocks from %d to %d\n", start, end); + + for (block = start; block <= end; block++) { + addr = flexonenand_addr(this, block); + if (onenand_block_isbad_nolock(mtd, addr, 0)) + continue; + + /* + * Since main area write results in ECC write to spare, + * it is sufficient to check only ECC bytes for change. + */ + ret = onenand_read_oob_nolock(mtd, addr, &ops); + if (ret) + return ret; + + for (i = 0; i < mtd->oobsize; i++) + if (this->oob_buf[i] != 0xff) + break; + + if (i != mtd->oobsize) { + printk(KERN_WARNING "Block %d not erased.\n", block); + return 1; + } + } + + return 0; +} + +/** + * flexonenand_set_boundary - Writes the SLC boundary + * @param mtd - mtd info structure + */ +int flexonenand_set_boundary(struct mtd_info *mtd, int die, + int boundary, int lock) +{ + struct onenand_chip *this = mtd->priv; + int ret, density, blksperdie, old, new, thisboundary; + loff_t addr; + + /* Change only once for SDP Flex-OneNAND */ + if (die && (!ONENAND_IS_DDP(this))) + return 0; + + /* boundary value of -1 indicates no required change */ + if (boundary < 0 || boundary == this->boundary[die]) + return 0; + + density = onenand_get_density(this->device_id); + blksperdie = ((16 << density) << 20) >> this->erase_shift; + blksperdie >>= ONENAND_IS_DDP(this) ? 1 : 0; + + if (boundary >= blksperdie) { + printk(KERN_ERR "flexonenand_set_boundary: Invalid boundary value. " + "Boundary not changed.\n"); + return -EINVAL; + } + + /* Check if converting blocks are erased */ + old = this->boundary[die] + (die * this->density_mask); + new = boundary + (die * this->density_mask); + ret = flexonenand_check_blocks_erased(mtd, min(old, new) + 1, max(old, new)); + if (ret) { + printk(KERN_ERR "flexonenand_set_boundary: Please erase blocks before boundary change\n"); + return ret; + } + + this->command(mtd, FLEXONENAND_CMD_PI_ACCESS, die, 0); + this->wait(mtd, FL_SYNCING); + + /* Check is boundary is locked */ + this->command(mtd, FLEXONENAND_CMD_READ_PI, die, 0); + ret = this->wait(mtd, FL_READING); + + thisboundary = this->read_word(this->base + ONENAND_DATARAM); + if ((thisboundary >> FLEXONENAND_PI_UNLOCK_SHIFT) != 3) { + printk(KERN_ERR "flexonenand_set_boundary: boundary locked\n"); + ret = 1; + goto out; + } + + printk(KERN_INFO "flexonenand_set_boundary: Changing die %d boundary: %d%s\n", + die, boundary, lock ? "(Locked)" : "(Unlocked)"); + + addr = die ? this->diesize[0] : 0; + + boundary &= FLEXONENAND_PI_MASK; + boundary |= lock ? 0 : (3 << FLEXONENAND_PI_UNLOCK_SHIFT); + + this->command(mtd, ONENAND_CMD_ERASE, addr, 0); + ret = this->wait(mtd, FL_ERASING); + if (ret) { + printk(KERN_ERR "flexonenand_set_boundary: Failed PI erase for Die %d\n", die); + goto out; + } + + this->write_word(boundary, this->base + ONENAND_DATARAM); + this->command(mtd, ONENAND_CMD_PROG, addr, 0); + ret = this->wait(mtd, FL_WRITING); + if (ret) { + printk(KERN_ERR "flexonenand_set_boundary: Failed PI write for Die %d\n", die); + goto out; + } + + this->command(mtd, FLEXONENAND_CMD_PI_UPDATE, die, 0); + ret = this->wait(mtd, FL_WRITING); +out: + this->write_word(ONENAND_CMD_RESET, this->base + ONENAND_REG_COMMAND); + this->wait(mtd, FL_RESETING); + if (!ret) + /* Recalculate device size on boundary change*/ + flexonenand_get_size(mtd); + + return ret; +} + +/** + * flexonenand_setup - capture Flex-OneNAND boundary and lock + * values passed as kernel parameters + * @param s kernel parameter string + */ +static int flexonenand_setup(char *s) +{ + int ints[5], i; + + s = get_options(s, 5, ints); + + for (i = 0; i < ints[0]; i++) + flex_bdry[i] = ints[i + 1]; + + return 1; +} + +__setup("onenand.bdry=", flexonenand_setup); + /** * onenand_probe - [OneNAND Interface] Probe the OneNAND device * @param mtd MTD device structure @@ -2647,6 +3315,7 @@ static int onenand_probe(struct mtd_info *mtd) maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); ver_id = this->read_word(this->base + ONENAND_REG_VERSION_ID); + this->technology = this->read_word(this->base + ONENAND_REG_TECHNOLOGY); /* Check OneNAND device */ if (maf_id != bram_maf_id || dev_id != bram_dev_id) @@ -2658,29 +3327,55 @@ static int onenand_probe(struct mtd_info *mtd) this->version_id = ver_id; density = onenand_get_density(dev_id); + if (FLEXONENAND(this)) { + this->dies = ONENAND_IS_DDP(this) ? 2 : 1; + /* Maximum possible erase regions */ + mtd->numeraseregions = this->dies << 1; + mtd->eraseregions = kzalloc(sizeof(struct mtd_erase_region_info) + * (this->dies << 1), GFP_KERNEL); + if (!mtd->eraseregions) + return -ENOMEM; + } + + /* + * For Flex-OneNAND, chipsize represents maximum possible device size. + * mtd->size represents the actual device size. + */ this->chipsize = (16 << density) << 20; - /* Set density mask. it is used for DDP */ - 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); + /* We use the full BufferRAM */ + if (ONENAND_IS_MLC(this)) + mtd->writesize <<= 1; + mtd->oobsize = mtd->writesize >> 5; /* Pages per a block are always 64 in OneNAND */ mtd->erasesize = mtd->writesize << 6; + /* + * Flex-OneNAND SLC area has 64 pages per block. + * Flex-OneNAND MLC area has 128 pages per block. + * Expose MLC erase size to find erase_shift and page_mask. + */ + if (FLEXONENAND(this)) + mtd->erasesize <<= 1; this->erase_shift = ffs(mtd->erasesize) - 1; this->page_shift = ffs(mtd->writesize) - 1; this->page_mask = (1 << (this->erase_shift - this->page_shift)) - 1; + /* Set density mask. it is used for DDP */ + if (ONENAND_IS_DDP(this)) + this->density_mask = this->chipsize >> (this->erase_shift + 1); /* It's real page size */ this->writesize = mtd->writesize; /* REVIST: Multichip handling */ - mtd->size = this->chipsize; + if (FLEXONENAND(this)) + flexonenand_get_size(mtd); + else + mtd->size = this->chipsize; /* Check OneNAND features */ onenand_check_features(mtd); @@ -2735,7 +3430,7 @@ static void onenand_resume(struct mtd_info *mtd) */ int onenand_scan(struct mtd_info *mtd, int maxchips) { - int i; + int i, ret; struct onenand_chip *this = mtd->priv; if (!this->read_word) @@ -2797,6 +3492,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) * Allow subpage writes up to oobsize. */ switch (mtd->oobsize) { + case 128: + this->ecclayout = &onenand_oob_128; + mtd->subpage_sft = 0; + break; case 64: this->ecclayout = &onenand_oob_64; mtd->subpage_sft = 2; @@ -2862,7 +3561,16 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) /* Unlock whole block */ onenand_unlock_all(mtd); - return this->scan_bbt(mtd); + ret = this->scan_bbt(mtd); + if ((!FLEXONENAND(this)) || ret) + return ret; + + /* Change Flex-OneNAND boundaries if required */ + for (i = 0; i < MAX_DIES; i++) + flexonenand_set_boundary(mtd, i, flex_bdry[2 * i], + flex_bdry[(2 * i) + 1]); + + return 0; } /** @@ -2891,6 +3599,7 @@ void onenand_release(struct mtd_info *mtd) kfree(this->page_buf); if (this->options & ONENAND_OOBBUF_ALLOC) kfree(this->oob_buf); + kfree(mtd->eraseregions); } EXPORT_SYMBOL_GPL(onenand_scan); diff --git a/drivers/mtd/onenand/onenand_bbt.c b/drivers/mtd/onenand/onenand_bbt.c index 2f53b51c6805..a91fcac1af01 100644 --- a/drivers/mtd/onenand/onenand_bbt.c +++ b/drivers/mtd/onenand/onenand_bbt.c @@ -63,6 +63,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr loff_t from; size_t readlen, ooblen; struct mtd_oob_ops ops; + int rgn; printk(KERN_INFO "Scanning device for bad blocks\n"); @@ -76,7 +77,7 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr /* 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); + numblocks = this->chipsize >> (bbm->bbt_erase_shift - 1); startblock = 0; from = 0; @@ -106,7 +107,12 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, struct nand_bbt_descr } } i += 2; - from += (1 << bbm->bbt_erase_shift); + + if (FLEXONENAND(this)) { + rgn = flexonenand_region(mtd, from); + from += mtd->eraseregions[rgn].erasesize; + } else + from += (1 << bbm->bbt_erase_shift); } return 0; @@ -143,7 +149,7 @@ static int onenand_isbad_bbt(struct mtd_info *mtd, loff_t offs, int allowbbt) uint8_t res; /* Get block number * 2 */ - block = (int) (offs >> (bbm->bbt_erase_shift - 1)); + block = (int) (onenand_block(this, offs) << 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", @@ -178,7 +184,7 @@ int onenand_scan_bbt(struct mtd_info *mtd, struct nand_bbt_descr *bd) struct bbm_info *bbm = this->bbm; int len, ret = 0; - len = mtd->size >> (this->erase_shift + 2); + len = this->chipsize >> (this->erase_shift + 2); /* Allocate memory (2bit per block) and clear the memory bad block table */ bbm->bbt = kzalloc(len, GFP_KERNEL); if (!bbm->bbt) { diff --git a/drivers/mtd/onenand/onenand_sim.c b/drivers/mtd/onenand/onenand_sim.c index d64200b7c94b..f6e3c8aebd3a 100644 --- a/drivers/mtd/onenand/onenand_sim.c +++ b/drivers/mtd/onenand/onenand_sim.c @@ -6,6 +6,10 @@ * Copyright © 2005-2007 Samsung Electronics * Kyungmin Park * + * Vishak G , Rohit Hagargundgi + * Flex-OneNAND simulator support + * Copyright (C) Samsung Electronics, 2008 + * * 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. @@ -24,16 +28,38 @@ #ifndef CONFIG_ONENAND_SIM_MANUFACTURER #define CONFIG_ONENAND_SIM_MANUFACTURER 0xec #endif + #ifndef CONFIG_ONENAND_SIM_DEVICE_ID #define CONFIG_ONENAND_SIM_DEVICE_ID 0x04 #endif + +#define CONFIG_FLEXONENAND ((CONFIG_ONENAND_SIM_DEVICE_ID >> 9) & 1) + #ifndef CONFIG_ONENAND_SIM_VERSION_ID #define CONFIG_ONENAND_SIM_VERSION_ID 0x1e #endif +#ifndef CONFIG_ONENAND_SIM_TECHNOLOGY_ID +#define CONFIG_ONENAND_SIM_TECHNOLOGY_ID CONFIG_FLEXONENAND +#endif + +/* Initial boundary values for Flex-OneNAND Simulator */ +#ifndef CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY +#define CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY 0x01 +#endif + +#ifndef CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY +#define CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY 0x01 +#endif + static int manuf_id = CONFIG_ONENAND_SIM_MANUFACTURER; static int device_id = CONFIG_ONENAND_SIM_DEVICE_ID; static int version_id = CONFIG_ONENAND_SIM_VERSION_ID; +static int technology_id = CONFIG_ONENAND_SIM_TECHNOLOGY_ID; +static int boundary[] = { + CONFIG_FLEXONENAND_SIM_DIE0_BOUNDARY, + CONFIG_FLEXONENAND_SIM_DIE1_BOUNDARY, +}; struct onenand_flash { void __iomem *base; @@ -57,12 +83,18 @@ struct onenand_flash { (writew(v, this->base + ONENAND_REG_WP_STATUS)) /* It has all 0xff chars */ -#define MAX_ONENAND_PAGESIZE (2048 + 64) +#define MAX_ONENAND_PAGESIZE (4096 + 128) static unsigned char *ffchars; +#if CONFIG_FLEXONENAND +#define PARTITION_NAME "Flex-OneNAND simulator partition" +#else +#define PARTITION_NAME "OneNAND simulator partition" +#endif + static struct mtd_partition os_partitions[] = { { - .name = "OneNAND simulator partition", + .name = PARTITION_NAME, .offset = 0, .size = MTDPART_SIZ_FULL, }, @@ -104,6 +136,7 @@ static void onenand_lock_handle(struct onenand_chip *this, int cmd) switch (cmd) { case ONENAND_CMD_UNLOCK: + case ONENAND_CMD_UNLOCK_ALL: if (block_lock_scheme) ONENAND_SET_WP_STATUS(ONENAND_WP_US, this); else @@ -228,10 +261,12 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, { struct mtd_info *mtd = &info->mtd; struct onenand_flash *flash = this->priv; - int main_offset, spare_offset; + int main_offset, spare_offset, die = 0; void __iomem *src; void __iomem *dest; unsigned int i; + static int pi_operation; + int erasesize, rgn; if (dataram) { main_offset = mtd->writesize; @@ -241,10 +276,27 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, spare_offset = 0; } + if (pi_operation) { + die = readw(this->base + ONENAND_REG_START_ADDRESS2); + die >>= ONENAND_DDP_SHIFT; + } + switch (cmd) { + case FLEXONENAND_CMD_PI_ACCESS: + pi_operation = 1; + break; + + case ONENAND_CMD_RESET: + pi_operation = 0; + break; + case ONENAND_CMD_READ: src = ONENAND_CORE(flash) + offset; dest = ONENAND_MAIN_AREA(this, main_offset); + if (pi_operation) { + writew(boundary[die], this->base + ONENAND_DATARAM); + break; + } memcpy(dest, src, mtd->writesize); /* Fall through */ @@ -257,6 +309,10 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, case ONENAND_CMD_PROG: src = ONENAND_MAIN_AREA(this, main_offset); dest = ONENAND_CORE(flash) + offset; + if (pi_operation) { + boundary[die] = readw(this->base + ONENAND_DATARAM); + break; + } /* To handle partial write */ for (i = 0; i < (1 << mtd->subpage_sft); i++) { int off = i * this->subpagesize; @@ -284,9 +340,18 @@ static void onenand_data_handle(struct onenand_chip *this, int cmd, break; case ONENAND_CMD_ERASE: - memset(ONENAND_CORE(flash) + offset, 0xff, mtd->erasesize); + if (pi_operation) + break; + + if (FLEXONENAND(this)) { + rgn = flexonenand_region(mtd, offset); + erasesize = mtd->eraseregions[rgn].erasesize; + } else + erasesize = mtd->erasesize; + + memset(ONENAND_CORE(flash) + offset, 0xff, erasesize); memset(ONENAND_CORE_SPARE(flash, this, offset), 0xff, - (mtd->erasesize >> 5)); + (erasesize >> 5)); break; default: @@ -339,7 +404,7 @@ static void onenand_command_handle(struct onenand_chip *this, int cmd) } if (block != -1) - offset += block << this->erase_shift; + offset = onenand_addr(this, block); if (page != -1) offset += page << this->page_shift; @@ -390,6 +455,7 @@ static int __init flash_init(struct onenand_flash *flash) } density = device_id >> ONENAND_DEVICE_DENSITY_SHIFT; + density &= ONENAND_DEVICE_DENSITY_MASK; size = ((16 << 20) << density); ONENAND_CORE(flash) = vmalloc(size + (size >> 5)); @@ -405,8 +471,9 @@ static int __init flash_init(struct onenand_flash *flash) writew(manuf_id, flash->base + ONENAND_REG_MANUFACTURER_ID); writew(device_id, flash->base + ONENAND_REG_DEVICE_ID); writew(version_id, flash->base + ONENAND_REG_VERSION_ID); + writew(technology_id, flash->base + ONENAND_REG_TECHNOLOGY); - if (density < 2) + if (density < 2 && (!CONFIG_FLEXONENAND)) buffer_size = 0x0400; /* 1KiB page */ else buffer_size = 0x0800; /* 2KiB page */ diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 0fa3ac4ad576..9aab82c1c743 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -17,6 +17,7 @@ #include #include +#define MAX_DIES 2 #define MAX_BUFFERRAM 2 /* Scan and identify a OneNAND device */ @@ -51,7 +52,12 @@ struct onenand_bufferram { /** * struct onenand_chip - OneNAND Private Flash Chip Data * @base: [BOARDSPECIFIC] address to access OneNAND + * @dies: [INTERN][FLEX-ONENAND] number of dies on chip + * @boundary: [INTERN][FLEX-ONENAND] Boundary of the dies + * @diesize: [INTERN][FLEX-ONENAND] Size of the dies * @chipsize: [INTERN] the size of one chip for multichip arrays + * FIXME For Flex-OneNAND, chipsize holds maximum possible + * device size ie when all blocks are considered MLC * @device_id: [INTERN] device ID * @density_mask: chip density, used for DDP devices * @verstion_id: [INTERN] version ID @@ -92,9 +98,13 @@ struct onenand_bufferram { */ struct onenand_chip { void __iomem *base; + unsigned dies; + unsigned boundary[MAX_DIES]; + loff_t diesize[MAX_DIES]; unsigned int chipsize; unsigned int device_id; unsigned int version_id; + unsigned int technology; unsigned int density_mask; unsigned int options; @@ -145,6 +155,8 @@ struct onenand_chip { #define ONENAND_SET_BUFFERRAM0(this) (this->bufferram_index = 0) #define ONENAND_SET_BUFFERRAM1(this) (this->bufferram_index = 1) +#define FLEXONENAND(this) \ + (this->device_id & DEVICE_IS_FLEXONENAND) #define ONENAND_GET_SYS_CFG1(this) \ (this->read_word(this->base + ONENAND_REG_SYS_CFG1)) #define ONENAND_SET_SYS_CFG1(v, this) \ @@ -153,6 +165,9 @@ struct onenand_chip { #define ONENAND_IS_DDP(this) \ (this->device_id & ONENAND_DEVICE_IS_DDP) +#define ONENAND_IS_MLC(this) \ + (this->technology & ONENAND_TECHNOLOGY_IS_MLC) + #ifdef CONFIG_MTD_ONENAND_2X_PROGRAM #define ONENAND_IS_2PLANE(this) \ (this->options & ONENAND_HAS_2PLANE) @@ -190,5 +205,8 @@ struct onenand_manufacturers { int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, struct mtd_oob_ops *ops); +unsigned onenand_block(struct onenand_chip *this, loff_t addr); +loff_t onenand_addr(struct onenand_chip *this, int block); +int flexonenand_region(struct mtd_info *mtd, loff_t addr); #endif /* __LINUX_MTD_ONENAND_H */ diff --git a/include/linux/mtd/onenand_regs.h b/include/linux/mtd/onenand_regs.h index 0c6bbe28f38c..86a6bbef6465 100644 --- a/include/linux/mtd/onenand_regs.h +++ b/include/linux/mtd/onenand_regs.h @@ -67,6 +67,9 @@ /* * Device ID Register F001h (R) */ +#define DEVICE_IS_FLEXONENAND (1 << 9) +#define FLEXONENAND_PI_MASK (0x3ff) +#define FLEXONENAND_PI_UNLOCK_SHIFT (14) #define ONENAND_DEVICE_DENSITY_MASK (0xf) #define ONENAND_DEVICE_DENSITY_SHIFT (4) #define ONENAND_DEVICE_IS_DDP (1 << 3) @@ -83,6 +86,11 @@ */ #define ONENAND_VERSION_PROCESS_SHIFT (8) +/* + * Technology Register F006h (R) + */ +#define ONENAND_TECHNOLOGY_IS_MLC (1 << 0) + /* * Start Address 1 F100h (R/W) & Start Address 2 F101h (R/W) */ @@ -93,7 +101,8 @@ /* * Start Address 8 F107h (R/W) */ -#define ONENAND_FPA_MASK (0x3f) +/* Note: It's actually 0x3f in case of SLC */ +#define ONENAND_FPA_MASK (0x7f) #define ONENAND_FPA_SHIFT (2) #define ONENAND_FSA_MASK (0x03) @@ -105,7 +114,8 @@ #define ONENAND_BSA_BOOTRAM (0 << 2) #define ONENAND_BSA_DATARAM0 (2 << 2) #define ONENAND_BSA_DATARAM1 (3 << 2) -#define ONENAND_BSC_MASK (0x03) +/* Note: It's actually 0x03 in case of SLC */ +#define ONENAND_BSC_MASK (0x07) /* * Command Register F220h (R/W) @@ -124,9 +134,13 @@ #define ONENAND_CMD_RESET (0xF0) #define ONENAND_CMD_OTP_ACCESS (0x65) #define ONENAND_CMD_READID (0x90) +#define FLEXONENAND_CMD_PI_UPDATE (0x05) +#define FLEXONENAND_CMD_PI_ACCESS (0x66) +#define FLEXONENAND_CMD_RECOVER_LSB (0x05) /* NOTE: Those are not *REAL* commands */ #define ONENAND_CMD_BUFFERRAM (0x1978) +#define FLEXONENAND_CMD_READ_PI (0x1985) /* * System Configuration 1 Register F221h (R, R/W) @@ -192,10 +206,12 @@ #define ONENAND_ECC_1BIT_ALL (0x5555) #define ONENAND_ECC_2BIT (1 << 1) #define ONENAND_ECC_2BIT_ALL (0xAAAA) +#define FLEXONENAND_UNCORRECTABLE_ERROR (0x1010) /* * One-Time Programmable (OTP) */ +#define FLEXONENAND_OTP_LOCK_OFFSET (2048) #define ONENAND_OTP_LOCK_OFFSET (14) #endif /* __ONENAND_REG_H */ -- cgit v1.2.3 From 31bb999ee73748068ddc271dd99b22dcc418efe3 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Tue, 12 May 2009 13:46:57 -0700 Subject: mtd: onenand: add bbt_wait & unlock_all as replaceable for some platform Add bbt_wait & unlock_all as replaceable for some platform such as s3c64xx s3c64xx has its own OneNAND controller and another interface Signed-off-by: Kyungmin Park Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 12 ++++++++++-- include/linux/mtd/onenand.h | 5 +++++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 8d4c9c253732..864327ed7fb3 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1506,7 +1506,7 @@ int onenand_bbt_read_oob(struct mtd_info *mtd, loff_t from, onenand_update_bufferram(mtd, from, 0); - ret = onenand_bbt_wait(mtd, FL_READING); + ret = this->bbt_wait(mtd, FL_READING); if (unlikely(ret)) ret = onenand_recover_lsb(mtd, from, ret); @@ -2527,6 +2527,10 @@ static void onenand_unlock_all(struct mtd_info *mtd) & ONENAND_CTRL_ONGO) continue; + /* Don't check lock status */ + if (this->options & ONENAND_SKIP_UNLOCK_CHECK) + return; + /* Check lock status */ if (onenand_check_lock_status(this)) return; @@ -3442,6 +3446,10 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) this->command = onenand_command; if (!this->wait) onenand_setup_wait(mtd); + if (!this->bbt_wait) + this->bbt_wait = onenand_bbt_wait; + if (!this->unlock_all) + this->unlock_all = onenand_unlock_all; if (!this->read_bufferram) this->read_bufferram = onenand_read_bufferram; @@ -3559,7 +3567,7 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) mtd->owner = THIS_MODULE; /* Unlock whole block */ - onenand_unlock_all(mtd); + this->unlock_all(mtd); ret = this->scan_bbt(mtd); if ((!FLEXONENAND(this)) || ret) diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 9aab82c1c743..8ed873374381 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -74,6 +74,8 @@ struct onenand_bufferram { * @command: [REPLACEABLE] hardware specific function for writing * commands to the chip * @wait: [REPLACEABLE] hardware specific function for wait on ready + * @bbt_wait: [REPLACEABLE] hardware specific function for bbt wait on ready + * @unlock_all: [REPLACEABLE] hardware specific function for unlock all * @read_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area * @write_bufferram: [REPLACEABLE] hardware specific function for BufferRAM Area * @read_word: [REPLACEABLE] hardware specific function for read @@ -118,6 +120,8 @@ struct onenand_chip { int (*command)(struct mtd_info *mtd, int cmd, loff_t address, size_t len); int (*wait)(struct mtd_info *mtd, int state); + int (*bbt_wait)(struct mtd_info *mtd, int state); + void (*unlock_all)(struct mtd_info *mtd); int (*read_bufferram)(struct mtd_info *mtd, int area, unsigned char *buffer, int offset, size_t count); int (*write_bufferram)(struct mtd_info *mtd, int area, @@ -184,6 +188,7 @@ struct onenand_chip { #define ONENAND_HAS_CONT_LOCK (0x0001) #define ONENAND_HAS_UNLOCK_ALL (0x0002) #define ONENAND_HAS_2PLANE (0x0004) +#define ONENAND_SKIP_UNLOCK_CHECK (0x0100) #define ONENAND_PAGEBUF_ALLOC (0x1000) #define ONENAND_OOBBUF_ALLOC (0x2000) -- cgit v1.2.3 From a755a3858f96ea7e8762ecaac451adfad45321bd Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Wed, 3 Jun 2009 13:46:54 +0200 Subject: mtd: nand: s3c2410_nand_setrate(): use correct macros for 2412/2440 Use the correct S3C2440_NFCONF_* macros for the mask for the 2412/2440 variants instead of the 2410 ones which use wrong bit positions. Signed-off-by: Peter Korsgaard Signed-off-by: David Woodhouse --- drivers/mtd/nand/s3c2410.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8e375d5fe231..776756e4ebe5 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -215,9 +215,9 @@ static int s3c2410_nand_setrate(struct s3c2410_nand_info *info) case TYPE_S3C2440: case TYPE_S3C2412: - mask = (S3C2410_NFCONF_TACLS(tacls_max - 1) | - S3C2410_NFCONF_TWRPH0(7) | - S3C2410_NFCONF_TWRPH1(7)); + mask = (S3C2440_NFCONF_TACLS(tacls_max - 1) | + S3C2440_NFCONF_TWRPH0(7) | + S3C2440_NFCONF_TWRPH1(7)); set = S3C2440_NFCONF_TACLS(tacls - 1); set |= S3C2440_NFCONF_TWRPH0(twrph0 - 1); -- cgit v1.2.3 From 43950a605dc76677f0c74dcd818a57d4df040e12 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Thu, 4 Jun 2009 16:24:59 +0200 Subject: mtd: nand: max_retries off by one in mxc_nand with `while (max_retries-- > 0)' max_retries reaches -1 after the loop. Signed-off-by: Roel Kluin Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index d03bd4eff722..da41ea82941a 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -199,7 +199,7 @@ static void wait_op_done(struct mxc_nand_host *host, int max_retries, } udelay(1); } - if (max_retries <= 0) + if (max_retries < 0) DEBUG(MTD_DEBUG_LEVEL0, "%s(%d): INT not set\n", __func__, param); } -- cgit v1.2.3 From 143070e74630b9557e1bb64d899ff2cc5a1dcb48 Mon Sep 17 00:00:00 2001 From: Stefan Roese Date: Thu, 16 Apr 2009 14:10:45 +0200 Subject: mtd: physmap_of: Add multiple regions and concatenation support This patch adds support to handle multiple non-identical chips in one flash device tree node. It also adds concat support to physmap_of. This makes it possible to support e.g. the Intel P30 48F4400 chips which internally consists of 2 non-identical NOR chips on one die. Additionally partitions now can span over multiple chips. To describe such a chip's, multiple "reg" tuples are now supported in one flash device tree node. Here an dts example: flash@f0000000,0 { #address-cells = <1>; #size-cells = <1>; compatible = "cfi-flash"; reg = <0 0x00000000 0x02000000 0 0x02000000 0x02000000>; bank-width = <2>; partition@0 { label = "test-part1"; reg = <0 0x04000000>; }; }; Signed-off-by: Stefan Roese Reviewed-by: Grant Likely Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap_of.c | 199 ++++++++++++++++++++++++++++++------------ 1 file changed, 143 insertions(+), 56 deletions(-) diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index c83a60fada53..39d357b2eb47 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -20,16 +20,23 @@ #include #include #include +#include #include #include +struct of_flash_list { + struct mtd_info *mtd; + struct map_info map; + struct resource *res; +}; + struct of_flash { - struct mtd_info *mtd; - struct map_info map; - struct resource *res; + struct mtd_info *cmtd; #ifdef CONFIG_MTD_PARTITIONS struct mtd_partition *parts; #endif + int list_size; /* number of elements in of_flash_list */ + struct of_flash_list list[0]; }; #ifdef CONFIG_MTD_PARTITIONS @@ -88,30 +95,44 @@ static int parse_obsolete_partitions(struct of_device *dev, static int of_flash_remove(struct of_device *dev) { struct of_flash *info; + int i; info = dev_get_drvdata(&dev->dev); if (!info) return 0; dev_set_drvdata(&dev->dev, NULL); - if (info->mtd) { +#ifdef CONFIG_MTD_CONCAT + if (info->cmtd != info->list[0].mtd) { + del_mtd_device(info->cmtd); + mtd_concat_destroy(info->cmtd); + } +#endif + + if (info->cmtd) { if (OF_FLASH_PARTS(info)) { - del_mtd_partitions(info->mtd); + del_mtd_partitions(info->cmtd); kfree(OF_FLASH_PARTS(info)); } else { - del_mtd_device(info->mtd); + del_mtd_device(info->cmtd); } - map_destroy(info->mtd); } - if (info->map.virt) - iounmap(info->map.virt); + for (i = 0; i < info->list_size; i++) { + if (info->list[i].mtd) + map_destroy(info->list[i].mtd); - if (info->res) { - release_resource(info->res); - kfree(info->res); + if (info->list[i].map.virt) + iounmap(info->list[i].map.virt); + + if (info->list[i].res) { + release_resource(info->list[i].res); + kfree(info->list[i].res); + } } + kfree(info); + return 0; } @@ -164,68 +185,130 @@ static int __devinit of_flash_probe(struct of_device *dev, const char *probe_type = match->data; const u32 *width; int err; - - err = -ENXIO; - if (of_address_to_resource(dp, 0, &res)) { - dev_err(&dev->dev, "Can't get IO address from device tree\n"); + int i; + int count; + const u32 *p; + int reg_tuple_size; + struct mtd_info **mtd_list = NULL; + + reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32); + + /* + * Get number of "reg" tuples. Scan for MTD devices on area's + * described by each "reg" region. This makes it possible (including + * the concat support) to support the Intel P30 48F4400 chips which + * consists internally of 2 non-identical NOR chips on one die. + */ + p = of_get_property(dp, "reg", &count); + if (count % reg_tuple_size != 0) { + dev_err(&dev->dev, "Malformed reg property on %s\n", + dev->node->full_name); + err = -EINVAL; goto err_out; } - - dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n", - (unsigned long long)res.start, (unsigned long long)res.end); + count /= reg_tuple_size; err = -ENOMEM; - info = kzalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc(sizeof(struct of_flash) + + sizeof(struct of_flash_list) * count, GFP_KERNEL); + if (!info) + goto err_out; + + mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); if (!info) goto err_out; dev_set_drvdata(&dev->dev, info); - err = -EBUSY; - info->res = request_mem_region(res.start, res.end - res.start + 1, - dev_name(&dev->dev)); - if (!info->res) - goto err_out; + for (i = 0; i < count; i++) { + err = -ENXIO; + if (of_address_to_resource(dp, i, &res)) { + dev_err(&dev->dev, "Can't get IO address from device" + " tree\n"); + goto err_out; + } - err = -ENXIO; - width = of_get_property(dp, "bank-width", NULL); - if (!width) { - dev_err(&dev->dev, "Can't get bank width from device tree\n"); - goto err_out; - } + dev_dbg(&dev->dev, "of_flash device: %.8llx-%.8llx\n", + (unsigned long long)res.start, + (unsigned long long)res.end); + + err = -EBUSY; + info->list[i].res = request_mem_region(res.start, res.end - + res.start + 1, + dev_name(&dev->dev)); + if (!info->list[i].res) + goto err_out; + + err = -ENXIO; + width = of_get_property(dp, "bank-width", NULL); + if (!width) { + dev_err(&dev->dev, "Can't get bank width from device" + " tree\n"); + goto err_out; + } - info->map.name = dev_name(&dev->dev); - info->map.phys = res.start; - info->map.size = res.end - res.start + 1; - info->map.bankwidth = *width; + info->list[i].map.name = dev_name(&dev->dev); + info->list[i].map.phys = res.start; + info->list[i].map.size = res.end - res.start + 1; + info->list[i].map.bankwidth = *width; + + err = -ENOMEM; + info->list[i].map.virt = ioremap(info->list[i].map.phys, + info->list[i].map.size); + if (!info->list[i].map.virt) { + dev_err(&dev->dev, "Failed to ioremap() flash" + " region\n"); + goto err_out; + } - err = -ENOMEM; - info->map.virt = ioremap(info->map.phys, info->map.size); - if (!info->map.virt) { - dev_err(&dev->dev, "Failed to ioremap() flash region\n"); - goto err_out; - } + simple_map_init(&info->list[i].map); - simple_map_init(&info->map); + if (probe_type) { + info->list[i].mtd = do_map_probe(probe_type, + &info->list[i].map); + } else { + info->list[i].mtd = obsolete_probe(dev, + &info->list[i].map); + } + mtd_list[i] = info->list[i].mtd; - if (probe_type) - info->mtd = do_map_probe(probe_type, &info->map); - else - info->mtd = obsolete_probe(dev, &info->map); + err = -ENXIO; + if (!info->list[i].mtd) { + dev_err(&dev->dev, "do_map_probe() failed\n"); + goto err_out; + } else { + info->list_size++; + } + info->list[i].mtd->owner = THIS_MODULE; + info->list[i].mtd->dev.parent = &dev->dev; + } - err = -ENXIO; - if (!info->mtd) { - dev_err(&dev->dev, "do_map_probe() failed\n"); - goto err_out; + err = 0; + if (info->list_size == 1) { + info->cmtd = info->list[0].mtd; + } else if (info->list_size > 1) { + /* + * We detected multiple devices. Concatenate them together. + */ +#ifdef CONFIG_MTD_CONCAT + info->cmtd = mtd_concat_create(mtd_list, info->list_size, + dev_name(&dev->dev)); + if (info->cmtd == NULL) + err = -ENXIO; +#else + printk(KERN_ERR "physmap_of: multiple devices " + "found but MTD concat support disabled.\n"); + err = -ENXIO; +#endif } - info->mtd->owner = THIS_MODULE; - info->mtd->dev.parent = &dev->dev; + if (err) + goto err_out; #ifdef CONFIG_MTD_PARTITIONS /* First look for RedBoot table or partitions on the command * line, these take precedence over device tree information */ - err = parse_mtd_partitions(info->mtd, part_probe_types, - &info->parts, 0); + err = parse_mtd_partitions(info->cmtd, part_probe_types, + &info->parts, 0); if (err < 0) return err; @@ -244,15 +327,19 @@ static int __devinit of_flash_probe(struct of_device *dev, } if (err > 0) - add_mtd_partitions(info->mtd, info->parts, err); + add_mtd_partitions(info->cmtd, info->parts, err); else #endif - add_mtd_device(info->mtd); + add_mtd_device(info->cmtd); + + kfree(mtd_list); return 0; err_out: + kfree(mtd_list); of_flash_remove(dev); + return err; } -- cgit v1.2.3 From a4536b19df92adda215f6fc225b52dc4cee4cf83 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Wed, 13 May 2009 16:54:13 +0100 Subject: [MTD] [NAND] S3C2410: Added a kerneldoc for s3c2410_nand_set Converted the old comnent to kerneldoc. Signed-off-by: Michel Pollet [ben-linux@fluff.org: updated subject, spello fix] Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/include/plat/nand.h | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/arch/arm/plat-s3c/include/plat/nand.h b/arch/arm/plat-s3c/include/plat/nand.h index 985546750c6d..935651664969 100644 --- a/arch/arm/plat-s3c/include/plat/nand.h +++ b/arch/arm/plat-s3c/include/plat/nand.h @@ -10,17 +10,19 @@ * published by the Free Software Foundation. */ -/* struct s3c2410_nand_set +/** + * struct s3c2410_nand_set - define a set of one or more nand chips + * @disable_ecc: Entirely disable ECC - Dangerous + * @nr_chips: Number of chips in this set + * @nr_partitions: Number of partitions pointed to by @partitions + * @name: Name of set (optional) + * @nr_map: Map for low-layer logical to physical chip numbers (option) + * @partitions: The mtd partition list * - * define an set of one or more nand chips registered with an unique mtd - * - * nr_chips = number of chips in this set - * nr_partitions = number of partitions pointed to be partitoons (or zero) - * name = name of set (optional) - * nr_map = map for low-layer logical to physical chip numbers (option) - * partitions = mtd partition list -*/ - + * define a set of one or more nand chips registered with an unique mtd. Also + * allows to pass flag to the underlying NAND layer. 'disable_ecc' will trigger + * a warning at boot time. + */ struct s3c2410_nand_set { unsigned int disable_ecc:1; -- cgit v1.2.3 From 9db41f9edcb87ae050fcb171c44be7f212728d54 Mon Sep 17 00:00:00 2001 From: Michel Pollet Date: Wed, 13 May 2009 16:54:14 +0100 Subject: [MTD] [NAND] S3C2410: Allow the machine code to get the BBT table from NAND Added a flag to allow the machine code to tell the NAND subsystem that it should try to pickup a BBT from the flash, and also skip the NAND full scan at startup. Signed-off-by: Michel Pollet Signed-off-by: Ben Dooks --- arch/arm/plat-s3c/include/plat/nand.h | 5 +++++ drivers/mtd/nand/s3c2410.c | 6 ++++++ 2 files changed, 11 insertions(+) diff --git a/arch/arm/plat-s3c/include/plat/nand.h b/arch/arm/plat-s3c/include/plat/nand.h index 935651664969..18f958801e64 100644 --- a/arch/arm/plat-s3c/include/plat/nand.h +++ b/arch/arm/plat-s3c/include/plat/nand.h @@ -13,6 +13,10 @@ /** * struct s3c2410_nand_set - define a set of one or more nand chips * @disable_ecc: Entirely disable ECC - Dangerous + * @flash_bbt: Openmoko u-boot can create a Bad Block Table + * Setting this flag will allow the kernel to + * look for it at boot time and also skip the NAND + * scan. * @nr_chips: Number of chips in this set * @nr_partitions: Number of partitions pointed to by @partitions * @name: Name of set (optional) @@ -25,6 +29,7 @@ */ struct s3c2410_nand_set { unsigned int disable_ecc:1; + unsigned int flash_bbt:1; int nr_chips; int nr_partitions; diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index ef5665258968..d315b513db5c 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -845,6 +845,12 @@ static void s3c2410_nand_init_chip(struct s3c2410_nand_info *info, dev_info(info->device, "NAND ECC UNKNOWN\n"); break; } + + /* If you use u-boot BBT creation code, specifying this flag will + * let the kernel fish out the BBT from the NAND, and also skip the + * full NAND scan that can take 1/2s or so. Little things... */ + if (set->flash_bbt) + chip->options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; } /** -- cgit v1.2.3 From dea2aa6fd7d46c43c840ad77905f3c161d5bc59d Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sat, 30 May 2009 18:30:18 +0100 Subject: [MTD] [NAND] S3C2410: Deal with unaligned lengths in S3C2440 buffer read/write Add code to deal with fractional lengths, as reported by Werner Almesberger. Re-work of his original patch. Signed-off-by: Ben Dooks --- drivers/mtd/nand/s3c2410.c | 22 ++++++++++++++++++++-- 1 file changed, 20 insertions(+), 2 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index d315b513db5c..8a7f960a0df5 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -584,7 +584,16 @@ static void s3c2410_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) static void s3c2440_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - readsl(info->regs + S3C2440_NFDATA, buf, len / 4); + + readsl(info->regs + S3C2440_NFDATA, buf, len >> 2); + + /* cleanup if we've got less than a word to do */ + if (len & 3) { + buf += len & ~3; + + for (; len & 3; len--) + *buf++ = readb(info->regs + S3C2440_NFDATA); + } } static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) @@ -596,7 +605,16 @@ static void s3c2410_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int static void s3c2440_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) { struct s3c2410_nand_info *info = s3c2410_nand_mtd_toinfo(mtd); - writesl(info->regs + S3C2440_NFDATA, buf, len / 4); + + writesl(info->regs + S3C2440_NFDATA, buf, len >> 2); + + /* cleanup any fractional write */ + if (len & 3) { + buf += len & ~3; + + for (; len & 3; len--, buf++) + writeb(*buf, info->regs + S3C2440_NFDATA); + } } /* cpufreq driver support */ -- cgit v1.2.3 From 947391cfbaa3b08558844c0b187bcd0223c3f660 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Sat, 30 May 2009 18:34:16 +0100 Subject: [MTD] [NAND] S3C2410: Use DIV_ROUND_UP Change to using DIV_ROUND_UP() in the timing calculation instead of blindly doing result++ Signed-off-by: Ben Dooks --- drivers/mtd/nand/s3c2410.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 8a7f960a0df5..89b79051cc68 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -180,8 +180,7 @@ static int s3c_nand_calc_rate(int wanted, unsigned long clk, int max) { int result; - result = (wanted * clk) / NS_IN_KHZ; - result++; + result = DIV_ROUND_UP((wanted * clk), NS_IN_KHZ); pr_debug("result %d from %ld, %d\n", result, clk, wanted); -- cgit v1.2.3 From 9dbc090274668abe3fc9f3a5de490d7d412cd74a Mon Sep 17 00:00:00 2001 From: Peter Korsgaard Date: Sun, 7 Jun 2009 06:04:23 -0700 Subject: mtd/nand: s3c6400 support for s3c2410 driver Add s3c6400 support to the s3c2410 driver. The nand controller in the s3c64xx devices is compatible with the one in the s3c2412, so simply reuse that code. Signed-off-by: Peter Korsgaard Acked-by: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 18 +++++++++--------- drivers/mtd/nand/s3c2410.c | 3 +++ 2 files changed, 12 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 3b3a21d1a6ba..1823212c6b46 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -145,27 +145,27 @@ config MTD_NAND_PPCHAMELEONEVB This enables the NAND flash driver on the PPChameleon EVB Board. config MTD_NAND_S3C2410 - tristate "NAND Flash support for S3C2410/S3C2440 SoC" - depends on ARCH_S3C2410 + tristate "NAND Flash support for Samsung S3C SoCs" + depends on ARCH_S3C2410 || ARCH_S3C64XX help - This enables the NAND flash controller on the S3C2410 and S3C2440 + This enables the NAND flash controller on the S3C24xx and S3C64xx SoCs No board specific support is done by this driver, each board must advertise a platform_device for the driver to attach. config MTD_NAND_S3C2410_DEBUG - bool "S3C2410 NAND driver debug" + bool "Samsung S3C NAND driver debug" depends on MTD_NAND_S3C2410 help - Enable debugging of the S3C2410 NAND driver + Enable debugging of the S3C NAND driver config MTD_NAND_S3C2410_HWECC - bool "S3C2410 NAND Hardware ECC" + bool "Samsung S3C NAND Hardware ECC" depends on MTD_NAND_S3C2410 help - Enable the use of the S3C2410's internal ECC generator when - using NAND. Early versions of the chip have had problems with + Enable the use of the controller's internal ECC generator when + using NAND. Early versions of the chips have had problems with incorrect ECC generation, and if using these, the default of software ECC is preferable. @@ -177,7 +177,7 @@ config MTD_NAND_NDFC NDFC Nand Flash Controllers are integrated in IBM/AMCC's 4xx SoCs config MTD_NAND_S3C2410_CLKSTOP - bool "S3C2410 NAND IDLE clock stop" + bool "Samsung S3C NAND IDLE clock stop" depends on MTD_NAND_S3C2410 default n help diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index 01a105eda3f4..11dc7e69c4fb 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -1111,6 +1111,9 @@ static struct platform_device_id s3c24xx_driver_ids[] = { }, { .name = "s3c2412-nand", .driver_data = TYPE_S3C2412, + }, { + .name = "s3c6400-nand", + .driver_data = TYPE_S3C2412, /* compatible with 2412 */ }, { } }; -- cgit v1.2.3 From bfee1a4311702c9fdecd8264ffd1126fd0ce92fb Mon Sep 17 00:00:00 2001 From: Nicolas Pitre Date: Sun, 31 May 2009 22:25:40 -0400 Subject: mtd: orion_nand: use burst reads with double word accesses This is not 8 times faster than byte access, but still around 60% faster. Signed-off-by: Nicolas Pitre Signed-off-by: David Woodhouse --- drivers/mtd/nand/orion_nand.c | 23 +++++++++++++++++++++++ 1 file changed, 23 insertions(+) diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index c2dfd3ea353d..7ad972229db4 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -47,6 +47,28 @@ static void orion_nand_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl writeb(cmd, nc->IO_ADDR_W + offs); } +static void orion_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct nand_chip *chip = mtd->priv; + void __iomem *io_base = chip->IO_ADDR_R; + uint64_t *buf64; + int i = 0; + + while (len && (unsigned long)buf & 7) { + *buf++ = readb(io_base); + len--; + } + buf64 = (uint64_t *)buf; + while (i < len/8) { + uint64_t x; + asm ("ldrd\t%0, [%1]" : "=r" (x) : "r" (io_base)); + buf64[i++] = x; + } + i *= 8; + while (i < len) + buf[i++] = readb(io_base); +} + static int __init orion_nand_probe(struct platform_device *pdev) { struct mtd_info *mtd; @@ -83,6 +105,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) nc->priv = board; nc->IO_ADDR_R = nc->IO_ADDR_W = io_base; nc->cmd_ctrl = orion_nand_cmd_ctrl; + nc->read_buf = orion_nand_read_buf; nc->ecc.mode = NAND_ECC_SOFT; if (board->chip_delay) -- cgit v1.2.3 From 272023df26da2668ecc3937f8eeb48c8683b64fa Mon Sep 17 00:00:00 2001 From: Atsushi Nemoto Date: Tue, 9 Jun 2009 14:31:15 +0100 Subject: mtd: nand: Fix memory leak on txx9ndfmc probe failure. Commit 81933046ef2a615031c46171013bde2c5225ee69 ('mtd: Fix handling of mtdname in txx9ndfmc.c') introduced a potential memory leak. The 'mtdname' member of the private data structure is now allocated separately, but was not freed on certain error paths. Fix that, and make things simpler by _always_ allocating it separately so that we don't need 'if (mtdname != dev_name()) kfree(mtdname);'... which gets ugly now that we're doing it more than once, and more likely that we'll get it wrong some time. Signed-off-by: Atsushi Nemoto Signed-off-by: David Woodhouse --- drivers/mtd/nand/txx9ndfmc.c | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 5f919e63b29b..488088eff2ca 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -336,20 +336,21 @@ static int __init txx9ndfmc_probe(struct platform_device *dev) txx9_priv->cs = i; txx9_priv->mtdname = kasprintf(GFP_KERNEL, "%s.%u", dev_name(&dev->dev), i); - if (!txx9_priv->mtdname) { - kfree(txx9_priv); - dev_err(&dev->dev, - "Unable to allocate TXx9 NDFMC MTD device name.\n"); - continue; - } } else { txx9_priv->cs = -1; - txx9_priv->mtdname = dev_name(&dev->dev); + txx9_priv->mtdname = kstrdup(dev_name(&dev->dev), + GFP_KERNEL); + } + if (!txx9_priv->mtdname) { + kfree(txx9_priv); + dev_err(&dev->dev, "Unable to allocate MTD name.\n"); + continue; } if (plat->wide_mask & (1 << i)) chip->options |= NAND_BUSWIDTH_16; if (nand_scan(mtd, 1)) { + kfree(txx9_priv->mtdname); kfree(txx9_priv); continue; } @@ -391,8 +392,7 @@ static int __exit txx9ndfmc_remove(struct platform_device *dev) kfree(drvdata->parts[i]); #endif del_mtd_device(mtd); - if (txx9_priv->mtdname != dev_name(&dev->dev)) - kfree(txx9_priv->mtdname); + kfree(txx9_priv->mtdname); kfree(txx9_priv); } return 0; -- cgit v1.2.3 From a2ab0ce09edf20b5228208405dd14bc8790fbdbd Mon Sep 17 00:00:00 2001 From: Christian Engelmayer Date: Sat, 13 Jun 2009 23:06:29 +0200 Subject: jffs2: leaking jffs2_summary in function jffs2_scan_medium In case of an error returned by file_dirty() 's' is not freed as the cleanup path is skipped. Reported by Coverity. Signed-off-by: Christian Engelmayer Signed-off-by: David Woodhouse --- fs/jffs2/scan.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 1d437de1e9a8..7515e73e2bfb 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -196,7 +196,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) if (c->nextblock) { ret = file_dirty(c, c->nextblock); if (ret) - return ret; + goto out; /* deleting summary information of the old nextblock */ jffs2_sum_reset_collected(c->summary); } @@ -207,7 +207,7 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) } else { ret = file_dirty(c, jeb); if (ret) - return ret; + goto out; } break; -- cgit v1.2.3 From c90173f0907486fe4010c2a8cef534e2473db43f Mon Sep 17 00:00:00 2001 From: Amul Saha Date: Tue, 16 Jun 2009 11:24:01 +0530 Subject: mtd: OneNAND: Allow setting of boundary information when built as module This patch unifies the flex_bdry setting for module vs. built-in configuration of OneNAND. Signed-off-by: Amul Kumar Saha Signed-off-by: Vishak G Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 28 +++++++++------------------- 1 file changed, 9 insertions(+), 19 deletions(-) diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 864327ed7fb3..6e829095ea9d 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -20,6 +20,7 @@ #include #include +#include #include #include #include @@ -34,6 +35,14 @@ /* Default Flex-OneNAND boundary and lock respectively */ static int flex_bdry[MAX_DIES * 2] = { -1, 0, -1, 0 }; +module_param_array(flex_bdry, int, NULL, 0400); +MODULE_PARM_DESC(flex_bdry, "SLC Boundary information for Flex-OneNAND" + "Syntax:flex_bdry=DIE_BDRY,LOCK,..." + "DIE_BDRY: SLC boundary of the die" + "LOCK: Locking information for SLC boundary" + " : 0->Set boundary in unlocked status" + " : 1->Set boundary in locked status"); + /** * onenand_oob_128 - oob info for Flex-Onenand with 4KB page * For now, we expose only 64 out of 80 ecc bytes @@ -3258,25 +3267,6 @@ out: return ret; } -/** - * flexonenand_setup - capture Flex-OneNAND boundary and lock - * values passed as kernel parameters - * @param s kernel parameter string - */ -static int flexonenand_setup(char *s) -{ - int ints[5], i; - - s = get_options(s, 5, ints); - - for (i = 0; i < ints[0]; i++) - flex_bdry[i] = ints[i + 1]; - - return 1; -} - -__setup("onenand.bdry=", flexonenand_setup); - /** * onenand_probe - [OneNAND Interface] Probe the OneNAND device * @param mtd MTD device structure -- cgit v1.2.3