From 34970a7db5c73f4c83b72ce989d297a95efb3a6d Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:04:07 -0500 Subject: mtd: au1550nd.c: use kzalloc() Use kzalloc() instead of kmalloc()/memset(). Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/au1550nd.c | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 92c334ff450..de190b8f8a1 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -450,7 +450,7 @@ static int __init au1xxx_nand_init(void) u32 nand_phys; /* Allocate memory for MTD device structure and private data */ - au1550_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); + au1550_mtd = kzalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); if (!au1550_mtd) { printk("Unable to allocate NAND MTD dev structure.\n"); return -ENOMEM; @@ -459,10 +459,6 @@ static int __init au1xxx_nand_init(void) /* Get pointer to private data */ this = (struct nand_chip *)(&au1550_mtd[1]); - /* Initialize structures */ - memset(au1550_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - /* Link the private data with the MTD structure */ au1550_mtd->priv = this; au1550_mtd->owner = THIS_MODULE; -- cgit v1.2.3-70-g09d2 From 440d4f9fb62dad0d5ed1635d099cedaa7a25d96d Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:08:37 -0500 Subject: mtd: au1550nd.c: remove unnecessary casts Remove unnecessary casts for p_nand, it is already a void __iomem *. Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/au1550nd.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index de190b8f8a1..58c55db504e 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -538,7 +538,7 @@ static int __init au1xxx_nand_init(void) } nand_phys = (mem_staddr << 4) & 0xFFFC0000; - p_nand = (void __iomem *)ioremap(nand_phys, 0x1000); + p_nand = ioremap(nand_phys, 0x1000); /* make controller and MTD agree */ if (NAND_CS == 0) @@ -583,7 +583,7 @@ static int __init au1xxx_nand_init(void) return 0; outio: - iounmap((void *)p_nand); + iounmap(p_nand); outmem: kfree(au1550_mtd); @@ -604,7 +604,7 @@ static void __exit au1550_cleanup(void) kfree(au1550_mtd); /* Unmap */ - iounmap((void *)p_nand); + iounmap(p_nand); } module_exit(au1550_cleanup); -- cgit v1.2.3-70-g09d2 From d8bc55553c416c877267c1efd65b099164acbe3f Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:13:13 -0500 Subject: mtd: davinci_nand.c: use resource_size() The ioremap'ed sizes are off by 1; use resource_size() for correct value. Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/davinci_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index fe3eba87de4..e2eeaf1e51a 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -566,8 +566,8 @@ static int __init nand_davinci_probe(struct platform_device *pdev) goto err_nomem; } - vaddr = ioremap(res1->start, res1->end - res1->start); - base = ioremap(res2->start, res2->end - res2->start); + vaddr = ioremap(res1->start, resource_size(res1)); + base = ioremap(res2->start, resource_size(res2)); if (!vaddr || !base) { dev_err(&pdev->dev, "ioremap failed\n"); ret = -EINVAL; -- cgit v1.2.3-70-g09d2 From 8a19b5581862650ab1735d4699491ac92bd2e212 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:19:44 -0500 Subject: mtd: fsl_elbc_nand.c: user resource_size() Use resource_size(). Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_elbc_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index ae30fb6eed9..1b8328fbb9d 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -874,7 +874,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, priv->ctrl = ctrl; priv->dev = ctrl->dev; - priv->vbase = ioremap(res.start, res.end - res.start + 1); + priv->vbase = ioremap(res.start, resource_size(&res)); if (!priv->vbase) { dev_err(ctrl->dev, "failed to map chip region\n"); ret = -ENOMEM; -- cgit v1.2.3-70-g09d2 From 58e6a84dfbd6f125b69e8b105c2cdbf22f97d5de Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:22:49 -0500 Subject: mtd: fls_upm.c: use resource_size() Use resource_size(). Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_upm.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index 071a60cb420..ab06a5b514a 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -302,7 +302,7 @@ static int __devinit fun_probe(struct of_device *ofdev, FSL_UPM_WAIT_WRITE_BYTE; fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start, - io_res.end - io_res.start + 1); + resource_size(&io_res)); if (!fun->io_base) { ret = -ENOMEM; goto err2; -- cgit v1.2.3-70-g09d2 From db5a5ae25aae66354712674b1643759897ff0325 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:30:46 -0500 Subject: mtd: drivers/mtd/nand/gpio.c: use resource_size() Use resource_size(). Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/gpio.c | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/gpio.c b/drivers/mtd/nand/gpio.c index 8f902e75aa8..0cde618bcc1 100644 --- a/drivers/mtd/nand/gpio.c +++ b/drivers/mtd/nand/gpio.c @@ -181,11 +181,11 @@ static int __devexit gpio_nand_remove(struct platform_device *dev) res = platform_get_resource(dev, IORESOURCE_MEM, 1); iounmap(gpiomtd->io_sync); if (res) - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); res = platform_get_resource(dev, IORESOURCE_MEM, 0); iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); if (gpio_is_valid(gpiomtd->plat.gpio_nwp)) gpio_set_value(gpiomtd->plat.gpio_nwp, 0); @@ -208,14 +208,14 @@ static void __iomem *request_and_remap(struct resource *res, size_t size, { void __iomem *ptr; - if (!request_mem_region(res->start, res->end - res->start + 1, name)) { + if (!request_mem_region(res->start, resource_size(res), name)) { *err = -EBUSY; return NULL; } ptr = ioremap(res->start, size); if (!ptr) { - release_mem_region(res->start, res->end - res->start + 1); + release_mem_region(res->start, resource_size(res)); *err = -ENOMEM; } return ptr; @@ -338,10 +338,10 @@ err_nwp: err_nce: iounmap(gpiomtd->io_sync); if (res1) - release_mem_region(res1->start, res1->end - res1->start + 1); + release_mem_region(res1->start, resource_size(res1)); err_sync: iounmap(gpiomtd->nand_chip.IO_ADDR_R); - release_mem_region(res0->start, res0->end - res0->start + 1); + release_mem_region(res0->start, resource_size(res0)); err_map: kfree(gpiomtd); return ret; -- cgit v1.2.3-70-g09d2 From 4442241ef6ed4d53c13d1c4b18fd57918bb4c850 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:36:37 -0500 Subject: mtd: nomadik_nand.c: use resource_size() Use resource_size(). Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/nomadik_nand.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nomadik_nand.c b/drivers/mtd/nand/nomadik_nand.c index 66123419f65..59cbf66607c 100644 --- a/drivers/mtd/nand/nomadik_nand.c +++ b/drivers/mtd/nand/nomadik_nand.c @@ -104,21 +104,21 @@ static int nomadik_nand_probe(struct platform_device *pdev) ret = -EIO; goto err_unmap; } - host->addr_va = ioremap(res->start, res->end - res->start + 1); + host->addr_va = ioremap(res->start, resource_size(res)); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_data"); if (!res) { ret = -EIO; goto err_unmap; } - host->data_va = ioremap(res->start, res->end - res->start + 1); + host->data_va = ioremap(res->start, resource_size(res)); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "nand_cmd"); if (!res) { ret = -EIO; goto err_unmap; } - host->cmd_va = ioremap(res->start, res->end - res->start + 1); + host->cmd_va = ioremap(res->start, resource_size(res)); if (!host->addr_va || !host->data_va || !host->cmd_va) { ret = -ENOMEM; -- cgit v1.2.3-70-g09d2 From e99030609e27abff7e1a868cb56384c678b09984 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:48:34 -0500 Subject: mtd: orion_nand.c: add error handling and use resource_size() Use platform_get_resource() to fetch the memory resource and add error handling for when it is missing. Use resource_size() for the ioremap(). Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/orion_nand.c | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index f59c07427af..990346036d3 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -74,6 +74,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) struct mtd_info *mtd; struct nand_chip *nc; struct orion_nand_data *board; + struct resource *res; void __iomem *io_base; int ret = 0; #ifdef CONFIG_MTD_PARTITIONS @@ -89,8 +90,13 @@ static int __init orion_nand_probe(struct platform_device *pdev) } mtd = (struct mtd_info *)(nc + 1); - io_base = ioremap(pdev->resource[0].start, - pdev->resource[0].end - pdev->resource[0].start + 1); + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + err = -ENODEV; + goto no_res; + } + + io_base = ioremap(res->start, resource_size(res)); if (!io_base) { printk(KERN_ERR "orion_nand: ioremap failed\n"); ret = -EIO; -- cgit v1.2.3-70-g09d2 From fc161c4e8ec9b12d42b10d510a9de8562ea3afac Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:56:22 -0500 Subject: mtd: drivers/mtd/nand/s3c2410.c: use resource_size() Use resource_size(). Signed-off-by: H Hartley Sweeten Cc: Ben Dooks Signed-off-by: David Woodhouse --- drivers/mtd/nand/s3c2410.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index fa6e9c7fe51..c41ad2285c6 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -957,7 +957,7 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) /* currently we assume we have the one resource */ res = pdev->resource; - size = res->end - res->start + 1; + size = resource_size(res); info->area = request_mem_region(res->start, size, pdev->name); -- cgit v1.2.3-70-g09d2 From 448791abfb64f097e6d6c5f71df68fd072def5b3 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 17:11:44 -0500 Subject: mtd: tmio_nand.c: use dev_get_platdata() and resource_size() Remove unnecessary casts and use dev_get_platdata() to retrieve the struct mfd_cell data from the platform. Use resource_size() for the ioremap()'s. Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/nand/tmio_nand.c | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/tmio_nand.c b/drivers/mtd/nand/tmio_nand.c index 92c73344a66..65fa46957db 100644 --- a/drivers/mtd/nand/tmio_nand.c +++ b/drivers/mtd/nand/tmio_nand.c @@ -318,7 +318,7 @@ static int tmio_nand_correct_data(struct mtd_info *mtd, unsigned char *buf, static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); int ret; if (cell->enable) { @@ -362,7 +362,7 @@ static int tmio_hw_init(struct platform_device *dev, struct tmio_nand *tmio) static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); tmio_iowrite8(FCR_MODE_POWER_OFF, tmio->fcr + FCR_MODE); if (cell->disable) @@ -371,7 +371,7 @@ static void tmio_hw_stop(struct platform_device *dev, struct tmio_nand *tmio) static int tmio_probe(struct platform_device *dev) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); struct tmio_nand_data *data = cell->driver_data; struct resource *fcr = platform_get_resource(dev, IORESOURCE_MEM, 0); @@ -404,14 +404,14 @@ static int tmio_probe(struct platform_device *dev) mtd->priv = nand_chip; mtd->name = "tmio-nand"; - tmio->ccr = ioremap(ccr->start, ccr->end - ccr->start + 1); + tmio->ccr = ioremap(ccr->start, resource_size(ccr)); if (!tmio->ccr) { retval = -EIO; goto err_iomap_ccr; } tmio->fcr_base = fcr->start & 0xfffff; - tmio->fcr = ioremap(fcr->start, fcr->end - fcr->start + 1); + tmio->fcr = ioremap(fcr->start, resource_size(fcr)); if (!tmio->fcr) { retval = -EIO; goto err_iomap_fcr; @@ -515,7 +515,7 @@ static int tmio_remove(struct platform_device *dev) #ifdef CONFIG_PM static int tmio_suspend(struct platform_device *dev, pm_message_t state) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); if (cell->suspend) cell->suspend(dev); @@ -526,7 +526,7 @@ static int tmio_suspend(struct platform_device *dev, pm_message_t state) static int tmio_resume(struct platform_device *dev) { - struct mfd_cell *cell = (struct mfd_cell *)dev->dev.platform_data; + struct mfd_cell *cell = dev_get_platdata(&dev->dev); /* FIXME - is this required or merely another attack of the broken * SHARP platform? Looks suspicious. -- cgit v1.2.3-70-g09d2 From cbd38a875fd890c3c2f196dd3370d90fd3ecb7f5 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Mon, 14 Dec 2009 16:59:27 -0500 Subject: mtd: drivers/mtd/nand/sh_flctl.c: use resource_size() Use resource_size(). Signed-off-by: H Hartley Sweeten Acked-by: Yoshihiro Shimoda Signed-off-by: David Woodhouse --- drivers/mtd/nand/sh_flctl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 02bef21f2e4..4260ab78f95 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -797,7 +797,7 @@ static int __init flctl_probe(struct platform_device *pdev) goto err; } - flctl->reg = ioremap(res->start, res->end - res->start + 1); + flctl->reg = ioremap(res->start, resource_size(res)); if (flctl->reg == NULL) { printk(KERN_ERR "%s: ioremap error.\n", __func__); ret = -ENOMEM; -- cgit v1.2.3-70-g09d2 From 49f37b74d077edff355f1c3390fc9fd0c418ef9b Mon Sep 17 00:00:00 2001 From: Wan ZongShun Date: Fri, 1 Jan 2010 18:03:47 +0800 Subject: ARM: NUC900: rename mtd nand driver name Due to I have renamed the platform_device.name,so this patch changes this nand driver platform_driver name. Signed-off-by: Wan ZongShun Signed-off-by: David Woodhouse --- drivers/mtd/nand/w90p910_nand.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/w90p910_nand.c b/drivers/mtd/nand/w90p910_nand.c index 7680e731348..c2e9b68e4ff 100644 --- a/drivers/mtd/nand/w90p910_nand.c +++ b/drivers/mtd/nand/w90p910_nand.c @@ -358,7 +358,7 @@ static struct platform_driver w90p910_nand_driver = { .probe = w90p910_nand_probe, .remove = __devexit_p(w90p910_nand_remove), .driver = { - .name = "w90p910-fmi", + .name = "nuc900-fmi", .owner = THIS_MODULE, }, }; @@ -379,4 +379,4 @@ module_exit(w90p910_nand_exit); MODULE_AUTHOR("Wan ZongShun "); MODULE_DESCRIPTION("w90p910 nand driver!"); MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:w90p910-fmi"); +MODULE_ALIAS("platform:nuc900-fmi"); -- cgit v1.2.3-70-g09d2 From bb6a77554935a86686039097cdda2b2a38891c78 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 1 Jan 2010 12:16:47 +0000 Subject: mtd: nand: rename w90p910_nand.c to nuc900_nand.c Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 6 +- drivers/mtd/nand/Makefile | 2 +- drivers/mtd/nand/nuc900_nand.c | 382 ++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/w90p910_nand.c | 382 ---------------------------------------- 4 files changed, 386 insertions(+), 386 deletions(-) create mode 100644 drivers/mtd/nand/nuc900_nand.c delete mode 100644 drivers/mtd/nand/w90p910_nand.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7678538344f..2dcbccb50da 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -489,11 +489,11 @@ config MTD_NAND_SOCRATES help Enables support for NAND Flash chips wired onto Socrates board. -config MTD_NAND_W90P910 - tristate "Support for NAND on w90p910 evaluation board." +config MTD_NAND_NUC900 + tristate "Support for NAND on Nuvoton NUC9xx/w90p910 evaluation boards." depends on ARCH_W90X900 && MTD_PARTITIONS help This enables the driver for the NAND Flash on evaluation board based - on w90p910. + on w90p910 / NUC9xx. endif # MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 460a1f39a8d..bba5addadb9 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -40,7 +40,7 @@ obj-$(CONFIG_MTD_NAND_SH_FLCTL) += sh_flctl.o obj-$(CONFIG_MTD_NAND_MXC) += mxc_nand.o obj-$(CONFIG_MTD_NAND_SOCRATES) += socrates_nand.o obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o -obj-$(CONFIG_MTD_NAND_W90P910) += w90p910_nand.o +obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o diff --git a/drivers/mtd/nand/nuc900_nand.c b/drivers/mtd/nand/nuc900_nand.c new file mode 100644 index 00000000000..6eddf7361ed --- /dev/null +++ b/drivers/mtd/nand/nuc900_nand.c @@ -0,0 +1,382 @@ +/* + * Copyright © 2009 Nuvoton technology corporation. + * + * Wan ZongShun + * + * 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;version 2 of the License. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#define REG_FMICSR 0x00 +#define REG_SMCSR 0xa0 +#define REG_SMISR 0xac +#define REG_SMCMD 0xb0 +#define REG_SMADDR 0xb4 +#define REG_SMDATA 0xb8 + +#define RESET_FMI 0x01 +#define NAND_EN 0x08 +#define READYBUSY (0x01 << 18) + +#define SWRST 0x01 +#define PSIZE (0x01 << 3) +#define DMARWEN (0x03 << 1) +#define BUSWID (0x01 << 4) +#define ECC4EN (0x01 << 5) +#define WP (0x01 << 24) +#define NANDCS (0x01 << 25) +#define ENDADDR (0x01 << 31) + +#define read_data_reg(dev) \ + __raw_readl((dev)->reg + REG_SMDATA) + +#define write_data_reg(dev, val) \ + __raw_writel((val), (dev)->reg + REG_SMDATA) + +#define write_cmd_reg(dev, val) \ + __raw_writel((val), (dev)->reg + REG_SMCMD) + +#define write_addr_reg(dev, val) \ + __raw_writel((val), (dev)->reg + REG_SMADDR) + +struct nuc900_nand { + struct mtd_info mtd; + struct nand_chip chip; + void __iomem *reg; + struct clk *clk; + spinlock_t lock; +}; + +static const struct mtd_partition partitions[] = { + { + .name = "NAND FS 0", + .offset = 0, + .size = 8 * 1024 * 1024 + }, + { + .name = "NAND FS 1", + .offset = MTDPART_OFS_APPEND, + .size = MTDPART_SIZ_FULL + } +}; + +static unsigned char nuc900_nand_read_byte(struct mtd_info *mtd) +{ + unsigned char ret; + struct nuc900_nand *nand; + + nand = container_of(mtd, struct nuc900_nand, mtd); + + ret = (unsigned char)read_data_reg(nand); + + return ret; +} + +static void nuc900_nand_read_buf(struct mtd_info *mtd, + unsigned char *buf, int len) +{ + int i; + struct nuc900_nand *nand; + + nand = container_of(mtd, struct nuc900_nand, mtd); + + for (i = 0; i < len; i++) + buf[i] = (unsigned char)read_data_reg(nand); +} + +static void nuc900_nand_write_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) +{ + int i; + struct nuc900_nand *nand; + + nand = container_of(mtd, struct nuc900_nand, mtd); + + for (i = 0; i < len; i++) + write_data_reg(nand, buf[i]); +} + +static int nuc900_verify_buf(struct mtd_info *mtd, + const unsigned char *buf, int len) +{ + int i; + struct nuc900_nand *nand; + + nand = container_of(mtd, struct nuc900_nand, mtd); + + for (i = 0; i < len; i++) { + if (buf[i] != (unsigned char)read_data_reg(nand)) + return -EFAULT; + } + + return 0; +} + +static int nuc900_check_rb(struct nuc900_nand *nand) +{ + unsigned int val; + spin_lock(&nand->lock); + val = __raw_readl(REG_SMISR); + val &= READYBUSY; + spin_unlock(&nand->lock); + + return val; +} + +static int nuc900_nand_devready(struct mtd_info *mtd) +{ + struct nuc900_nand *nand; + int ready; + + nand = container_of(mtd, struct nuc900_nand, mtd); + + ready = (nuc900_check_rb(nand)) ? 1 : 0; + return ready; +} + +static void nuc900_nand_command_lp(struct mtd_info *mtd, unsigned int command, + int column, int page_addr) +{ + register struct nand_chip *chip = mtd->priv; + struct nuc900_nand *nand; + + nand = container_of(mtd, struct nuc900_nand, mtd); + + if (command == NAND_CMD_READOOB) { + column += mtd->writesize; + command = NAND_CMD_READ0; + } + + write_cmd_reg(nand, command & 0xff); + + if (column != -1 || page_addr != -1) { + + if (column != -1) { + if (chip->options & NAND_BUSWIDTH_16) + column >>= 1; + write_addr_reg(nand, column); + write_addr_reg(nand, column >> 8 | ENDADDR); + } + if (page_addr != -1) { + write_addr_reg(nand, page_addr); + + if (chip->chipsize > (128 << 20)) { + write_addr_reg(nand, page_addr >> 8); + write_addr_reg(nand, page_addr >> 16 | ENDADDR); + } else { + write_addr_reg(nand, page_addr >> 8 | ENDADDR); + } + } + } + + switch (command) { + case NAND_CMD_CACHEDPROG: + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_RNDIN: + case NAND_CMD_STATUS: + case NAND_CMD_DEPLETE1: + return; + + case NAND_CMD_STATUS_ERROR: + case NAND_CMD_STATUS_ERROR0: + case NAND_CMD_STATUS_ERROR1: + case NAND_CMD_STATUS_ERROR2: + case NAND_CMD_STATUS_ERROR3: + udelay(chip->chip_delay); + return; + + case NAND_CMD_RESET: + if (chip->dev_ready) + break; + udelay(chip->chip_delay); + + write_cmd_reg(nand, NAND_CMD_STATUS); + write_cmd_reg(nand, command); + + while (!nuc900_check_rb(nand)) + ; + + return; + + case NAND_CMD_RNDOUT: + write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); + return; + + case NAND_CMD_READ0: + + write_cmd_reg(nand, NAND_CMD_READSTART); + default: + + if (!chip->dev_ready) { + udelay(chip->chip_delay); + return; + } + } + + /* Apply this short delay always to ensure that we do wait tWB in + * any case on any machine. */ + ndelay(100); + + while (!chip->dev_ready(mtd)) + ; +} + + +static void nuc900_nand_enable(struct nuc900_nand *nand) +{ + unsigned int val; + spin_lock(&nand->lock); + __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); + + val = __raw_readl(nand->reg + REG_FMICSR); + + if (!(val & NAND_EN)) + __raw_writel(val | NAND_EN, REG_FMICSR); + + val = __raw_readl(nand->reg + REG_SMCSR); + + val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); + val |= WP; + + __raw_writel(val, nand->reg + REG_SMCSR); + + spin_unlock(&nand->lock); +} + +static int __devinit nuc900_nand_probe(struct platform_device *pdev) +{ + struct nuc900_nand *nuc900_nand; + struct nand_chip *chip; + int retval; + struct resource *res; + + retval = 0; + + nuc900_nand = kzalloc(sizeof(struct nuc900_nand), GFP_KERNEL); + if (!nuc900_nand) + return -ENOMEM; + chip = &(nuc900_nand->chip); + + nuc900_nand->mtd.priv = chip; + nuc900_nand->mtd.owner = THIS_MODULE; + spin_lock_init(&nuc900_nand->lock); + + nuc900_nand->clk = clk_get(&pdev->dev, NULL); + if (IS_ERR(nuc900_nand->clk)) { + retval = -ENOENT; + goto fail1; + } + clk_enable(nuc900_nand->clk); + + chip->cmdfunc = nuc900_nand_command_lp; + chip->dev_ready = nuc900_nand_devready; + chip->read_byte = nuc900_nand_read_byte; + chip->write_buf = nuc900_nand_write_buf; + chip->read_buf = nuc900_nand_read_buf; + chip->verify_buf = nuc900_verify_buf; + chip->chip_delay = 50; + chip->options = 0; + chip->ecc.mode = NAND_ECC_SOFT; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!res) { + retval = -ENXIO; + goto fail1; + } + + if (!request_mem_region(res->start, resource_size(res), pdev->name)) { + retval = -EBUSY; + goto fail1; + } + + nuc900_nand->reg = ioremap(res->start, resource_size(res)); + if (!nuc900_nand->reg) { + retval = -ENOMEM; + goto fail2; + } + + nuc900_nand_enable(nuc900_nand); + + if (nand_scan(&(nuc900_nand->mtd), 1)) { + retval = -ENXIO; + goto fail3; + } + + add_mtd_partitions(&(nuc900_nand->mtd), partitions, + ARRAY_SIZE(partitions)); + + platform_set_drvdata(pdev, nuc900_nand); + + return retval; + +fail3: iounmap(nuc900_nand->reg); +fail2: release_mem_region(res->start, resource_size(res)); +fail1: kfree(nuc900_nand); + return retval; +} + +static int __devexit nuc900_nand_remove(struct platform_device *pdev) +{ + struct nuc900_nand *nuc900_nand = platform_get_drvdata(pdev); + struct resource *res; + + iounmap(nuc900_nand->reg); + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + release_mem_region(res->start, resource_size(res)); + + clk_disable(nuc900_nand->clk); + clk_put(nuc900_nand->clk); + + kfree(nuc900_nand); + + platform_set_drvdata(pdev, NULL); + + return 0; +} + +static struct platform_driver nuc900_nand_driver = { + .probe = nuc900_nand_probe, + .remove = __devexit_p(nuc900_nand_remove), + .driver = { + .name = "nuc900-fmi", + .owner = THIS_MODULE, + }, +}; + +static int __init nuc900_nand_init(void) +{ + return platform_driver_register(&nuc900_nand_driver); +} + +static void __exit nuc900_nand_exit(void) +{ + platform_driver_unregister(&nuc900_nand_driver); +} + +module_init(nuc900_nand_init); +module_exit(nuc900_nand_exit); + +MODULE_AUTHOR("Wan ZongShun "); +MODULE_DESCRIPTION("w90p910/NUC9xx nand driver!"); +MODULE_LICENSE("GPL"); +MODULE_ALIAS("platform:nuc900-fmi"); diff --git a/drivers/mtd/nand/w90p910_nand.c b/drivers/mtd/nand/w90p910_nand.c deleted file mode 100644 index c2e9b68e4ff..00000000000 --- a/drivers/mtd/nand/w90p910_nand.c +++ /dev/null @@ -1,382 +0,0 @@ -/* - * Copyright (c) 2009 Nuvoton technology corporation. - * - * Wan ZongShun - * - * 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;version 2 of the License. - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -#define REG_FMICSR 0x00 -#define REG_SMCSR 0xa0 -#define REG_SMISR 0xac -#define REG_SMCMD 0xb0 -#define REG_SMADDR 0xb4 -#define REG_SMDATA 0xb8 - -#define RESET_FMI 0x01 -#define NAND_EN 0x08 -#define READYBUSY (0x01 << 18) - -#define SWRST 0x01 -#define PSIZE (0x01 << 3) -#define DMARWEN (0x03 << 1) -#define BUSWID (0x01 << 4) -#define ECC4EN (0x01 << 5) -#define WP (0x01 << 24) -#define NANDCS (0x01 << 25) -#define ENDADDR (0x01 << 31) - -#define read_data_reg(dev) \ - __raw_readl((dev)->reg + REG_SMDATA) - -#define write_data_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMDATA) - -#define write_cmd_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMCMD) - -#define write_addr_reg(dev, val) \ - __raw_writel((val), (dev)->reg + REG_SMADDR) - -struct w90p910_nand { - struct mtd_info mtd; - struct nand_chip chip; - void __iomem *reg; - struct clk *clk; - spinlock_t lock; -}; - -static const struct mtd_partition partitions[] = { - { - .name = "NAND FS 0", - .offset = 0, - .size = 8 * 1024 * 1024 - }, - { - .name = "NAND FS 1", - .offset = MTDPART_OFS_APPEND, - .size = MTDPART_SIZ_FULL - } -}; - -static unsigned char w90p910_nand_read_byte(struct mtd_info *mtd) -{ - unsigned char ret; - struct w90p910_nand *nand; - - nand = container_of(mtd, struct w90p910_nand, mtd); - - ret = (unsigned char)read_data_reg(nand); - - return ret; -} - -static void w90p910_nand_read_buf(struct mtd_info *mtd, - unsigned char *buf, int len) -{ - int i; - struct w90p910_nand *nand; - - nand = container_of(mtd, struct w90p910_nand, mtd); - - for (i = 0; i < len; i++) - buf[i] = (unsigned char)read_data_reg(nand); -} - -static void w90p910_nand_write_buf(struct mtd_info *mtd, - const unsigned char *buf, int len) -{ - int i; - struct w90p910_nand *nand; - - nand = container_of(mtd, struct w90p910_nand, mtd); - - for (i = 0; i < len; i++) - write_data_reg(nand, buf[i]); -} - -static int w90p910_verify_buf(struct mtd_info *mtd, - const unsigned char *buf, int len) -{ - int i; - struct w90p910_nand *nand; - - nand = container_of(mtd, struct w90p910_nand, mtd); - - for (i = 0; i < len; i++) { - if (buf[i] != (unsigned char)read_data_reg(nand)) - return -EFAULT; - } - - return 0; -} - -static int w90p910_check_rb(struct w90p910_nand *nand) -{ - unsigned int val; - spin_lock(&nand->lock); - val = __raw_readl(REG_SMISR); - val &= READYBUSY; - spin_unlock(&nand->lock); - - return val; -} - -static int w90p910_nand_devready(struct mtd_info *mtd) -{ - struct w90p910_nand *nand; - int ready; - - nand = container_of(mtd, struct w90p910_nand, mtd); - - ready = (w90p910_check_rb(nand)) ? 1 : 0; - return ready; -} - -static void w90p910_nand_command_lp(struct mtd_info *mtd, - unsigned int command, int column, int page_addr) -{ - register struct nand_chip *chip = mtd->priv; - struct w90p910_nand *nand; - - nand = container_of(mtd, struct w90p910_nand, mtd); - - if (command == NAND_CMD_READOOB) { - column += mtd->writesize; - command = NAND_CMD_READ0; - } - - write_cmd_reg(nand, command & 0xff); - - if (column != -1 || page_addr != -1) { - - if (column != -1) { - if (chip->options & NAND_BUSWIDTH_16) - column >>= 1; - write_addr_reg(nand, column); - write_addr_reg(nand, column >> 8 | ENDADDR); - } - if (page_addr != -1) { - write_addr_reg(nand, page_addr); - - if (chip->chipsize > (128 << 20)) { - write_addr_reg(nand, page_addr >> 8); - write_addr_reg(nand, page_addr >> 16 | ENDADDR); - } else { - write_addr_reg(nand, page_addr >> 8 | ENDADDR); - } - } - } - - switch (command) { - case NAND_CMD_CACHEDPROG: - case NAND_CMD_PAGEPROG: - case NAND_CMD_ERASE1: - case NAND_CMD_ERASE2: - case NAND_CMD_SEQIN: - case NAND_CMD_RNDIN: - case NAND_CMD_STATUS: - case NAND_CMD_DEPLETE1: - return; - - case NAND_CMD_STATUS_ERROR: - case NAND_CMD_STATUS_ERROR0: - case NAND_CMD_STATUS_ERROR1: - case NAND_CMD_STATUS_ERROR2: - case NAND_CMD_STATUS_ERROR3: - udelay(chip->chip_delay); - return; - - case NAND_CMD_RESET: - if (chip->dev_ready) - break; - udelay(chip->chip_delay); - - write_cmd_reg(nand, NAND_CMD_STATUS); - write_cmd_reg(nand, command); - - while (!w90p910_check_rb(nand)) - ; - - return; - - case NAND_CMD_RNDOUT: - write_cmd_reg(nand, NAND_CMD_RNDOUTSTART); - return; - - case NAND_CMD_READ0: - - write_cmd_reg(nand, NAND_CMD_READSTART); - default: - - if (!chip->dev_ready) { - udelay(chip->chip_delay); - return; - } - } - - /* Apply this short delay always to ensure that we do wait tWB in - * any case on any machine. */ - ndelay(100); - - while (!chip->dev_ready(mtd)) - ; -} - - -static void w90p910_nand_enable(struct w90p910_nand *nand) -{ - unsigned int val; - spin_lock(&nand->lock); - __raw_writel(RESET_FMI, (nand->reg + REG_FMICSR)); - - val = __raw_readl(nand->reg + REG_FMICSR); - - if (!(val & NAND_EN)) - __raw_writel(val | NAND_EN, REG_FMICSR); - - val = __raw_readl(nand->reg + REG_SMCSR); - - val &= ~(SWRST|PSIZE|DMARWEN|BUSWID|ECC4EN|NANDCS); - val |= WP; - - __raw_writel(val, nand->reg + REG_SMCSR); - - spin_unlock(&nand->lock); -} - -static int __devinit w90p910_nand_probe(struct platform_device *pdev) -{ - struct w90p910_nand *w90p910_nand; - struct nand_chip *chip; - int retval; - struct resource *res; - - retval = 0; - - w90p910_nand = kzalloc(sizeof(struct w90p910_nand), GFP_KERNEL); - if (!w90p910_nand) - return -ENOMEM; - chip = &(w90p910_nand->chip); - - w90p910_nand->mtd.priv = chip; - w90p910_nand->mtd.owner = THIS_MODULE; - spin_lock_init(&w90p910_nand->lock); - - w90p910_nand->clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(w90p910_nand->clk)) { - retval = -ENOENT; - goto fail1; - } - clk_enable(w90p910_nand->clk); - - chip->cmdfunc = w90p910_nand_command_lp; - chip->dev_ready = w90p910_nand_devready; - chip->read_byte = w90p910_nand_read_byte; - chip->write_buf = w90p910_nand_write_buf; - chip->read_buf = w90p910_nand_read_buf; - chip->verify_buf = w90p910_verify_buf; - chip->chip_delay = 50; - chip->options = 0; - chip->ecc.mode = NAND_ECC_SOFT; - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - if (!res) { - retval = -ENXIO; - goto fail1; - } - - if (!request_mem_region(res->start, resource_size(res), pdev->name)) { - retval = -EBUSY; - goto fail1; - } - - w90p910_nand->reg = ioremap(res->start, resource_size(res)); - if (!w90p910_nand->reg) { - retval = -ENOMEM; - goto fail2; - } - - w90p910_nand_enable(w90p910_nand); - - if (nand_scan(&(w90p910_nand->mtd), 1)) { - retval = -ENXIO; - goto fail3; - } - - add_mtd_partitions(&(w90p910_nand->mtd), partitions, - ARRAY_SIZE(partitions)); - - platform_set_drvdata(pdev, w90p910_nand); - - return retval; - -fail3: iounmap(w90p910_nand->reg); -fail2: release_mem_region(res->start, resource_size(res)); -fail1: kfree(w90p910_nand); - return retval; -} - -static int __devexit w90p910_nand_remove(struct platform_device *pdev) -{ - struct w90p910_nand *w90p910_nand = platform_get_drvdata(pdev); - struct resource *res; - - iounmap(w90p910_nand->reg); - - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - release_mem_region(res->start, resource_size(res)); - - clk_disable(w90p910_nand->clk); - clk_put(w90p910_nand->clk); - - kfree(w90p910_nand); - - platform_set_drvdata(pdev, NULL); - - return 0; -} - -static struct platform_driver w90p910_nand_driver = { - .probe = w90p910_nand_probe, - .remove = __devexit_p(w90p910_nand_remove), - .driver = { - .name = "nuc900-fmi", - .owner = THIS_MODULE, - }, -}; - -static int __init w90p910_nand_init(void) -{ - return platform_driver_register(&w90p910_nand_driver); -} - -static void __exit w90p910_nand_exit(void) -{ - platform_driver_unregister(&w90p910_nand_driver); -} - -module_init(w90p910_nand_init); -module_exit(w90p910_nand_exit); - -MODULE_AUTHOR("Wan ZongShun "); -MODULE_DESCRIPTION("w90p910 nand driver!"); -MODULE_LICENSE("GPL"); -MODULE_ALIAS("platform:nuc900-fmi"); -- cgit v1.2.3-70-g09d2 From e026255f7d0e56006a147b190ae23be95cb0a9bd Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Tue, 29 Dec 2009 20:15:23 +0100 Subject: mtd: physmap_of: Correct the size argument to kzalloc mtd_list has type struct mtd_info **, not struct mtd_info *, so the elements of the array should have pointer type, not structure type. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @disable sizeof_type_expr@ type T; T **x; @@ x = <+...sizeof( - T + *x )...+> // Signed-off-by: Julia Lawall Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap_of.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index 61e4eb48bb2..1d91333010b 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -217,7 +217,7 @@ static int __devinit of_flash_probe(struct of_device *dev, dev_set_drvdata(&dev->dev, info); - mtd_list = kzalloc(sizeof(struct mtd_info) * count, GFP_KERNEL); + mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL); if (!mtd_list) goto err_flash_remove; -- cgit v1.2.3-70-g09d2 From 7603757993e7ce3e63b2280ccf61d8058b7b2414 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Tue, 5 Jan 2010 14:59:58 -0700 Subject: mtd: Remove now-defunct ts7250 nand driver The ts72xx platform has been updated to use the generic platform nand driver (plat_nand.c). This removes the now-defunct ts7250.c nand driver. Signed-off-by: H Hartley Sweeten Cc: Matthieu Crapet Cc: Jesse Off Signed-off-by: David Woodhouse --- arch/arm/mach-ep93xx/include/mach/ts72xx.h | 19 --- drivers/mtd/nand/Kconfig | 6 - drivers/mtd/nand/Makefile | 1 - drivers/mtd/nand/ts7250.c | 207 ----------------------------- 4 files changed, 233 deletions(-) delete mode 100644 drivers/mtd/nand/ts7250.c (limited to 'drivers') diff --git a/arch/arm/mach-ep93xx/include/mach/ts72xx.h b/arch/arm/mach-ep93xx/include/mach/ts72xx.h index 3bd934e9a7f..61c0e132c63 100644 --- a/arch/arm/mach-ep93xx/include/mach/ts72xx.h +++ b/arch/arm/mach-ep93xx/include/mach/ts72xx.h @@ -9,9 +9,6 @@ * febff000 22000000 4K model number register * febfe000 22400000 4K options register * febfd000 22800000 4K options register #2 - * febfc000 [67]0000000 4K NAND data register - * febfb000 [67]0400000 4K NAND control register - * febfa000 [67]0800000 4K NAND busy register * febf9000 10800000 4K TS-5620 RTC index register * febf8000 11700000 4K TS-5620 RTC data register */ @@ -41,22 +38,6 @@ #define TS72XX_OPTIONS2_TS9420_BOOT 0x02 -#define TS72XX_NAND1_DATA_PHYS_BASE 0x60000000 -#define TS72XX_NAND2_DATA_PHYS_BASE 0x70000000 -#define TS72XX_NAND_DATA_VIRT_BASE 0xfebfc000 -#define TS72XX_NAND_DATA_SIZE 0x00001000 - -#define TS72XX_NAND1_CONTROL_PHYS_BASE 0x60400000 -#define TS72XX_NAND2_CONTROL_PHYS_BASE 0x70400000 -#define TS72XX_NAND_CONTROL_VIRT_BASE 0xfebfb000 -#define TS72XX_NAND_CONTROL_SIZE 0x00001000 - -#define TS72XX_NAND1_BUSY_PHYS_BASE 0x60800000 -#define TS72XX_NAND2_BUSY_PHYS_BASE 0x70800000 -#define TS72XX_NAND_BUSY_VIRT_BASE 0xfebfa000 -#define TS72XX_NAND_BUSY_SIZE 0x00001000 - - #define TS72XX_RTC_INDEX_VIRT_BASE 0xfebf9000 #define TS72XX_RTC_INDEX_PHYS_BASE 0x10800000 #define TS72XX_RTC_INDEX_SIZE 0x00001000 diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 2dcbccb50da..318ef2f2194 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -95,12 +95,6 @@ config MTD_NAND_OMAP_PREFETCH_DMA or in DMA interrupt mode. Say y for DMA mode or MPU mode will be used -config MTD_NAND_TS7250 - tristate "NAND Flash device on TS-7250 board" - depends on MACH_TS72XX - help - Support for NAND flash on Technologic Systems TS-7250 platform. - config MTD_NAND_IDS tristate diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index bba5addadb9..355786846bc 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -19,7 +19,6 @@ obj-$(CONFIG_MTD_NAND_DISKONCHIP) += diskonchip.o obj-$(CONFIG_MTD_NAND_H1900) += h1910.o obj-$(CONFIG_MTD_NAND_RTC_FROM4) += rtc_from4.o obj-$(CONFIG_MTD_NAND_SHARPSL) += sharpsl.o -obj-$(CONFIG_MTD_NAND_TS7250) += ts7250.o obj-$(CONFIG_MTD_NAND_NANDSIM) += nandsim.o obj-$(CONFIG_MTD_NAND_CS553X) += cs553x_nand.o obj-$(CONFIG_MTD_NAND_NDFC) += ndfc.o diff --git a/drivers/mtd/nand/ts7250.c b/drivers/mtd/nand/ts7250.c deleted file mode 100644 index 0f5562aeedc..00000000000 --- a/drivers/mtd/nand/ts7250.c +++ /dev/null @@ -1,207 +0,0 @@ -/* - * drivers/mtd/nand/ts7250.c - * - * Copyright (C) 2004 Technologic Systems (support@embeddedARM.com) - * - * Derived from drivers/mtd/nand/edb7312.c - * Copyright (C) 2004 Marius Gröger (mag@sysgo.de) - * - * Derived from drivers/mtd/nand/autcpu12.c - * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - * - * Overview: - * This is a device driver for the NAND flash device found on the - * TS-7250 board which utilizes a Samsung 32 Mbyte part. - */ - -#include -#include -#include -#include -#include -#include -#include - -#include -#include - -#include -#include - -/* - * MTD structure for TS7250 board - */ -static struct mtd_info *ts7250_mtd = NULL; - -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { "cmdlinepart", NULL }; - -#define NUM_PARTITIONS 3 - -/* - * Define static partitions for flash device - */ -static struct mtd_partition partition_info32[] = { - { - .name = "TS-BOOTROM", - .offset = 0x00000000, - .size = 0x00004000, - }, { - .name = "Linux", - .offset = 0x00004000, - .size = 0x01d00000, - }, { - .name = "RedBoot", - .offset = 0x01d04000, - .size = 0x002fc000, - }, -}; - -/* - * Define static partitions for flash device - */ -static struct mtd_partition partition_info128[] = { - { - .name = "TS-BOOTROM", - .offset = 0x00000000, - .size = 0x00004000, - }, { - .name = "Linux", - .offset = 0x00004000, - .size = 0x07d00000, - }, { - .name = "RedBoot", - .offset = 0x07d04000, - .size = 0x002fc000, - }, -}; -#endif - - -/* - * hardware specific access to control-lines - * - * ctrl: - * NAND_NCE: bit 0 -> bit 2 - * NAND_CLE: bit 1 -> bit 1 - * NAND_ALE: bit 2 -> bit 0 - */ -static void ts7250_hwcontrol(struct mtd_info *mtd, int cmd, unsigned int ctrl) -{ - struct nand_chip *chip = mtd->priv; - - if (ctrl & NAND_CTRL_CHANGE) { - unsigned long addr = TS72XX_NAND_CONTROL_VIRT_BASE; - unsigned char bits; - - bits = (ctrl & NAND_NCE) << 2; - bits |= ctrl & NAND_CLE; - bits |= (ctrl & NAND_ALE) >> 2; - - __raw_writeb((__raw_readb(addr) & ~0x7) | bits, addr); - } - - if (cmd != NAND_CMD_NONE) - writeb(cmd, chip->IO_ADDR_W); -} - -/* - * read device ready pin - */ -static int ts7250_device_ready(struct mtd_info *mtd) -{ - return __raw_readb(TS72XX_NAND_BUSY_VIRT_BASE) & 0x20; -} - -/* - * Main initialization routine - */ -static int __init ts7250_init(void) -{ - struct nand_chip *this; - const char *part_type = 0; - int mtd_parts_nb = 0; - struct mtd_partition *mtd_parts = 0; - - if (!machine_is_ts72xx() || board_is_ts7200()) - return -ENXIO; - - /* Allocate memory for MTD device structure and private data */ - ts7250_mtd = kmalloc(sizeof(struct mtd_info) + sizeof(struct nand_chip), GFP_KERNEL); - if (!ts7250_mtd) { - printk("Unable to allocate TS7250 NAND MTD device structure.\n"); - return -ENOMEM; - } - - /* Get pointer to private data */ - this = (struct nand_chip *)(&ts7250_mtd[1]); - - /* Initialize structures */ - memset(ts7250_mtd, 0, sizeof(struct mtd_info)); - memset(this, 0, sizeof(struct nand_chip)); - - /* Link the private data with the MTD structure */ - ts7250_mtd->priv = this; - ts7250_mtd->owner = THIS_MODULE; - - /* insert callbacks */ - this->IO_ADDR_R = (void *)TS72XX_NAND_DATA_VIRT_BASE; - this->IO_ADDR_W = (void *)TS72XX_NAND_DATA_VIRT_BASE; - this->cmd_ctrl = ts7250_hwcontrol; - this->dev_ready = ts7250_device_ready; - this->chip_delay = 15; - this->ecc.mode = NAND_ECC_SOFT; - - printk("Searching for NAND flash...\n"); - /* Scan to find existence of the device */ - if (nand_scan(ts7250_mtd, 1)) { - kfree(ts7250_mtd); - return -ENXIO; - } -#ifdef CONFIG_MTD_PARTITIONS - ts7250_mtd->name = "ts7250-nand"; - mtd_parts_nb = parse_mtd_partitions(ts7250_mtd, part_probes, &mtd_parts, 0); - if (mtd_parts_nb > 0) - part_type = "command line"; - else - mtd_parts_nb = 0; -#endif - if (mtd_parts_nb == 0) { - mtd_parts = partition_info32; - if (ts7250_mtd->size >= (128 * 0x100000)) - mtd_parts = partition_info128; - mtd_parts_nb = NUM_PARTITIONS; - part_type = "static"; - } - - /* Register the partitions */ - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(ts7250_mtd, mtd_parts, mtd_parts_nb); - - /* Return happy */ - return 0; -} - -module_init(ts7250_init); - -/* - * Clean up routine - */ -static void __exit ts7250_cleanup(void) -{ - /* Unregister the device */ - del_mtd_device(ts7250_mtd); - - /* Free the MTD device structure */ - kfree(ts7250_mtd); -} - -module_exit(ts7250_cleanup); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Jesse Off "); -MODULE_DESCRIPTION("MTD map driver for Technologic Systems TS-7250 board"); -- cgit v1.2.3-70-g09d2 From 2d6bfc261a02662ec7a3a78df3e05e453e8b168d Mon Sep 17 00:00:00 2001 From: Peter Huewe Date: Thu, 7 Jan 2010 02:51:13 +0100 Subject: mtd: orion_nand: Fix build failure caused by typo MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Commit e99030609e27abff7e1a868cb56384c678b09984 ("mtd: orion_nand.c: add error handling and use resource_size()") introduced a build error -- it assigns something to a undeclared variable 'err', whereas the rest of the code uses 'ret' for this task. This patch fixes this typo and thus removes the build failure. Signed-off-by: Peter Huewe Reviewed-by: H Hartley Sweeten Acked-by: Uwe Kleine-König Signed-off-by: David Woodhouse Signed-off-by: Andrew Morton --- drivers/mtd/nand/orion_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index 990346036d3..f16050c61c5 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -92,7 +92,7 @@ static int __init orion_nand_probe(struct platform_device *pdev) res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - err = -ENODEV; + ret = -ENODEV; goto no_res; } -- cgit v1.2.3-70-g09d2 From 6029a3a4625e485e01cf9a024d190c9d4e6b6681 Mon Sep 17 00:00:00 2001 From: Andrey Yurovsky Date: Thu, 17 Dec 2009 12:31:20 -0800 Subject: mtd: nandsim: fix spelling s/nanodeconds/nanoseconds Signed-off-by: Andrey Yurovsky Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nandsim.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 7281000fef2..3cf91804867 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -135,8 +135,8 @@ MODULE_PARM_DESC(fourth_id_byte, "The fourth byte returned by NAND Flash 'read I MODULE_PARM_DESC(access_delay, "Initial page access delay (microseconds)"); MODULE_PARM_DESC(programm_delay, "Page programm delay (microseconds"); MODULE_PARM_DESC(erase_delay, "Sector erase delay (milliseconds)"); -MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanodeconds)"); -MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanodeconds)"); +MODULE_PARM_DESC(output_cycle, "Word output (from flash) time (nanoseconds)"); +MODULE_PARM_DESC(input_cycle, "Word input (to flash) time (nanoseconds)"); MODULE_PARM_DESC(bus_width, "Chip's bus width (8- or 16-bit)"); MODULE_PARM_DESC(do_delays, "Simulate NAND delays using busy-waits if not zero"); MODULE_PARM_DESC(log, "Perform logging if not zero"); -- cgit v1.2.3-70-g09d2 From 377ace08eaaa3fb0c05b3fb7a86a5a1e9eb04673 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 9 Jan 2010 15:10:34 +0100 Subject: mtd: nand: make PCI device id constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The id_table field of the struct pci_driver is constant in so it is worth to make cafe_nand_tbl also constant. The semantic match that finds this kind of pattern is as follows: (http://coccinelle.lip6.fr/) // @r@ disable decl_init,const_decl_init; identifier I1, I2, x; @@ struct I1 { ... const struct I2 *x; ... }; @s@ identifier r.I1, y; identifier r.x, E; @@ struct I1 y = { .x = E, }; @c@ identifier r.I2; identifier s.E; @@ const struct I2 E[] = ... ; @depends on !c@ identifier r.I2; identifier s.E; @@ + const struct I2 E[] = ...; // Signed-off-by: Márton Németh Cc: Julia Lawall Cc: cocci@diku.dk Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/cafe_nand.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index c828d9ac7bd..67e2b33f7ef 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -848,7 +848,7 @@ static void __devexit cafe_nand_remove(struct pci_dev *pdev) kfree(mtd); } -static struct pci_device_id cafe_nand_tbl[] = { +static const struct pci_device_id cafe_nand_tbl[] = { { PCI_VENDOR_ID_MARVELL, PCI_DEVICE_ID_MARVELL_88ALP01_NAND, PCI_ANY_ID, PCI_ANY_ID }, { } -- cgit v1.2.3-70-g09d2 From b3acd638a2b8b9c3cd1b52b83e285c88d34ef7f3 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 9 Jan 2010 15:10:40 +0100 Subject: mtd: nand: make USB device id constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The id_table field of the struct usb_device_id is constant in so it is worth to make alauda_table also constant. The semantic match that finds this kind of pattern is as follows: (http://coccinelle.lip6.fr/) // @r@ disable decl_init,const_decl_init; identifier I1, I2, x; @@ struct I1 { ... const struct I2 *x; ... }; @s@ identifier r.I1, y; identifier r.x, E; @@ struct I1 y = { .x = E, }; @c@ identifier r.I2; identifier s.E; @@ const struct I2 E[] = ... ; @depends on !c@ identifier r.I2; identifier s.E; @@ + const struct I2 E[] = ...; // Signed-off-by: Márton Németh Cc: Julia Lawall Cc: cocci@diku.dk Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/alauda.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/alauda.c b/drivers/mtd/nand/alauda.c index 2d6773281fd..8691e0482ed 100644 --- a/drivers/mtd/nand/alauda.c +++ b/drivers/mtd/nand/alauda.c @@ -49,7 +49,7 @@ #define TIMEOUT HZ -static struct usb_device_id alauda_table [] = { +static const struct usb_device_id alauda_table[] = { { USB_DEVICE(0x0584, 0x0008) }, /* Fujifilm DPC-R1 */ { USB_DEVICE(0x07b4, 0x010a) }, /* Olympus MAUSB-10 */ { } -- cgit v1.2.3-70-g09d2 From b2d4fbab79bd2b121c56db757c3a0f06ec7e0868 Mon Sep 17 00:00:00 2001 From: Márton Németh Date: Sat, 9 Jan 2010 15:10:46 +0100 Subject: mtd: nand: make Open Firmware device id constant MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The match_table field of the struct of_device_id is constant in so it is worth to make xps2_of_match also constant. The semantic match that finds this kind of pattern is as follows: (http://coccinelle.lip6.fr/) // @r@ disable decl_init,const_decl_init; identifier I1, I2, x; @@ struct I1 { ... const struct I2 *x; ... }; @s@ identifier r.I1, y; identifier r.x, E; @@ struct I1 y = { .x = E, }; @c@ identifier r.I2; identifier s.E; @@ const struct I2 E[] = ... ; @depends on !c@ identifier r.I2; identifier s.E; @@ + const struct I2 E[] = ...; // Signed-off-by: Márton Németh Cc: Julia Lawall Cc: cocci@diku.dk Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_upm.c | 2 +- drivers/mtd/nand/pasemi_nand.c | 2 +- drivers/mtd/nand/socrates_nand.c | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index ab06a5b514a..d721ec055cb 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -349,7 +349,7 @@ static int __devexit fun_remove(struct of_device *ofdev) return 0; } -static struct of_device_id of_fun_match[] = { +static const struct of_device_id of_fun_match[] = { { .compatible = "fsl,upm-nand" }, {}, }; diff --git a/drivers/mtd/nand/pasemi_nand.c b/drivers/mtd/nand/pasemi_nand.c index a8b9376cf32..090a05c12cb 100644 --- a/drivers/mtd/nand/pasemi_nand.c +++ b/drivers/mtd/nand/pasemi_nand.c @@ -209,7 +209,7 @@ static int __devexit pasemi_nand_remove(struct of_device *ofdev) return 0; } -static struct of_device_id pasemi_nand_match[] = +static const struct of_device_id pasemi_nand_match[] = { { .compatible = "pasemi,localbus-nand", diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index a4519a7bd68..65748ea2b34 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -290,7 +290,7 @@ static int __devexit socrates_nand_remove(struct of_device *ofdev) return 0; } -static struct of_device_id socrates_nand_match[] = +static const struct of_device_id socrates_nand_match[] = { { .compatible = "abb,socrates-nand", -- cgit v1.2.3-70-g09d2 From 17fabf156507ec0f688f1e58be02f38e04de0c6e Mon Sep 17 00:00:00 2001 From: Jiri Slaby Date: Sun, 10 Jan 2010 10:01:19 +0100 Subject: mtd: cfi: remove unneeded NULL checks In cfi_intelext_setup and cfi_amdstd_setup, mtd is never NULL. Remove unnecessary checks. Signed-off-by: Jiri Slaby Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 6 ++---- drivers/mtd/chips/cfi_cmdset_0002.c | 6 ++---- 2 files changed, 4 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 5fbf29e1e64..92530433c11 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -615,10 +615,8 @@ static struct mtd_info *cfi_intelext_setup(struct mtd_info *mtd) return mtd; setup_err: - if(mtd) { - kfree(mtd->eraseregions); - kfree(mtd); - } + kfree(mtd->eraseregions); + kfree(mtd); kfree(cfi->cmdset_priv); return NULL; } diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index f3600e8d538..1ebdcdd72d8 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -494,10 +494,8 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) return mtd; setup_err: - if(mtd) { - kfree(mtd->eraseregions); - kfree(mtd); - } + kfree(mtd->eraseregions); + kfree(mtd); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); return NULL; -- cgit v1.2.3-70-g09d2 From b840bc11b5062803c204d8a9625a1a1c5812d6d6 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 11 Jan 2010 15:05:35 +0100 Subject: mtd: mxc-nand: no need to check for validity of platform driver data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The probe function calls platform_set_drvdata with a valid pointer when the probe is successful. As mxcnd_suspend and mxcnd_resume are only called on bound devices, platform_get_drvdata always returns non-NULL. This fix isn't critical as the pointer is always valid so it doesn't matter if the compiler generated code for it or not. Signed-off-by: Uwe Kleine-König Reported-by: David Binderman Signed-off-by: Artem Bityutskiy Acked-by: Sascha Hauer Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 17 +++++++---------- 1 file changed, 7 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 45dec5770da..84f363571c2 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -886,11 +886,10 @@ 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 (mtd) { - ret = mtd->suspend(mtd); - /* Disable the NFC clock */ - clk_disable(host->clk); - } + + ret = mtd->suspend(mtd); + /* Disable the NFC clock */ + clk_disable(host->clk); return ret; } @@ -904,11 +903,9 @@ static int mxcnd_resume(struct platform_device *pdev) DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); - if (mtd) { - /* Enable the NFC clock */ - clk_enable(host->clk); - mtd->resume(mtd); - } + /* Enable the NFC clock */ + clk_enable(host->clk); + mtd->resume(mtd); return ret; } -- cgit v1.2.3-70-g09d2 From 9c14b153e6af1301f022d34f1f63888f333e3ef5 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 11 Jan 2010 17:53:16 +0100 Subject: mtd: mxc-nand: don't disable clock in mxcnd-suspend MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The clock must already be off after mtd->suspend. Disabling it again results in an negative overflow of the clock usage count. This didn't hurt as mxcnd_resume undid it after wake up. Signed-off-by: Uwe Kleine-König Acked-by: Sascha Hauer Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 84f363571c2..970ce6bd06a 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -888,8 +888,12 @@ static int mxcnd_suspend(struct platform_device *pdev, pm_message_t state) DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND suspend\n"); ret = mtd->suspend(mtd); - /* Disable the NFC clock */ - clk_disable(host->clk); + + /* + * nand_suspend locks the device for exclusive access, so + * the clock must already be off. + */ + BUG_ON(!ret && host->clk_act); return ret; } @@ -903,8 +907,6 @@ static int mxcnd_resume(struct platform_device *pdev) DEBUG(MTD_DEBUG_LEVEL0, "MXC_ND : NAND resume\n"); - /* Enable the NFC clock */ - clk_enable(host->clk); mtd->resume(mtd); return ret; -- cgit v1.2.3-70-g09d2 From 53f2b1c86a1fa1414be93571062ac4c263fa9fbc Mon Sep 17 00:00:00 2001 From: Jon Ringle Date: Wed, 13 Jan 2010 09:36:10 -0500 Subject: mtd: ixp4xx: fix reading from half-word boundary Fix handling of reads that don't start on a half-word boundary. Signed-off-by: Jon Ringle Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/ixp4xx.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 7b051529741..7513d90fee6 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -107,8 +107,8 @@ static void ixp4xx_copy_from(struct map_info *map, void *to, return; if (from & 1) { - *dest++ = BYTE1(flash_read16(src)); - src++; + *dest++ = BYTE1(flash_read16(src-1)); + src++; --len; } -- cgit v1.2.3-70-g09d2 From bcc98a46eafd38968b05e793326f031988c2b2a8 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 15 Jan 2010 11:25:38 -0700 Subject: mtd: fix different address space noise In mtd_ioctl MEMGETREGIONINFO the region_user_info pointer ur is cast in __kernel space. This produces a number of sparse warnings like: warning: cast removes address space of expression warning: incorrect type in initializer (different address spaces) expected unsigned int const [noderef] *register __p got unsigned int * Since argp is already a void __user * just use it dirrectly without the cast and make ur a __user *. Signed-off-by: H Hartley Sweeten Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 5b081cb8435..0a85085fe69 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -482,7 +482,7 @@ static int mtd_ioctl(struct inode *inode, struct file *file, { uint32_t ur_idx; struct mtd_erase_region_info *kr; - struct region_info_user *ur = (struct region_info_user *) argp; + struct region_info_user __user *ur = argp; if (get_user(ur_idx, &(ur->regionindex))) return -EFAULT; -- cgit v1.2.3-70-g09d2 From 4335c1003ed05d5d5a386cd8008fc06a6d424ca2 Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Sun, 17 Jan 2010 10:52:46 -0500 Subject: mtd: maps: Blackfin async: rename local funcs to avoid common clashes There are new Blackfin MMR helper functions that use the same name as some of the local functions in this driver, so have the driver use more specific names to avoid the issue. Signed-off-by: Mike Frysinger Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/bfin-async-flash.c | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/bfin-async-flash.c b/drivers/mtd/maps/bfin-async-flash.c index a7c808b577d..5824fd49800 100644 --- a/drivers/mtd/maps/bfin-async-flash.c +++ b/drivers/mtd/maps/bfin-async-flash.c @@ -69,7 +69,7 @@ static void switch_back(struct async_state *state) local_irq_restore(state->irq_flags); } -static map_word bfin_read(struct map_info *map, unsigned long ofs) +static map_word bfin_flash_read(struct map_info *map, unsigned long ofs) { struct async_state *state = (struct async_state *)map->map_priv_1; uint16_t word; @@ -85,7 +85,7 @@ static map_word bfin_read(struct map_info *map, unsigned long ofs) return test; } -static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static void bfin_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) { struct async_state *state = (struct async_state *)map->map_priv_1; @@ -96,7 +96,7 @@ static void bfin_copy_from(struct map_info *map, void *to, unsigned long from, s switch_back(state); } -static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs) +static void bfin_flash_write(struct map_info *map, map_word d1, unsigned long ofs) { struct async_state *state = (struct async_state *)map->map_priv_1; uint16_t d; @@ -111,7 +111,7 @@ static void bfin_write(struct map_info *map, map_word d1, unsigned long ofs) switch_back(state); } -static void bfin_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) { struct async_state *state = (struct async_state *)map->map_priv_1; @@ -140,10 +140,10 @@ static int __devinit bfin_flash_probe(struct platform_device *pdev) return -ENOMEM; state->map.name = DRIVER_NAME; - state->map.read = bfin_read; - state->map.copy_from = bfin_copy_from; - state->map.write = bfin_write; - state->map.copy_to = bfin_copy_to; + state->map.read = bfin_flash_read; + state->map.copy_from = bfin_flash_copy_from; + state->map.write = bfin_flash_write; + state->map.copy_to = bfin_flash_copy_to; state->map.bankwidth = pdata->width; state->map.size = memory->end - memory->start + 1; state->map.virt = (void __iomem *)memory->start; -- cgit v1.2.3-70-g09d2 From 0040476b0efa99ad0d4ffb81d8e882095420d288 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 29 Jan 2010 10:35:04 +0100 Subject: mtd: change positive error return into negative Signed-off-by: Roel Kluin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 0a85085fe69..bce0a07cbac 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -373,7 +373,7 @@ static int mtd_do_writeoob(struct file *file, struct mtd_info *mtd, if (!mtd->write_oob) ret = -EOPNOTSUPP; else - ret = access_ok(VERIFY_READ, ptr, length) ? 0 : EFAULT; + ret = access_ok(VERIFY_READ, ptr, length) ? 0 : -EFAULT; if (ret) return ret; -- cgit v1.2.3-70-g09d2 From f1332ba2f23800bb5d52457ac150c568dfb1f3bf Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 29 Jan 2010 20:57:11 +0000 Subject: mtd: Introduce and use iteration macro for reading the MTD device table Signed-off-by: Ben Hutchings Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 10 ++++----- drivers/mtd/mtdcore.c | 54 ++++++++++++++++++++++------------------------- drivers/mtd/mtdcore.h | 15 +++++++++++++ 3 files changed, 45 insertions(+), 34 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index c82e09bbc5f..85a52b3c769 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -335,7 +335,8 @@ static struct mtd_notifier blktrans_notifier = { int register_mtd_blktrans(struct mtd_blktrans_ops *tr) { - int ret, i; + struct mtd_info *mtd; + int ret; /* Register the notifier if/when the first device type is registered, to prevent the link/init ordering from fucking @@ -389,10 +390,9 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) INIT_LIST_HEAD(&tr->devs); list_add(&tr->list, &blktrans_majors); - for (i=0; itype != MTD_ABSENT) - tr->add_mtd(tr, mtd_table[i]); - } + mtd_for_each_device(mtd) + if (mtd->type != MTD_ABSENT) + tr->add_mtd(tr, mtd); mutex_unlock(&mtd_table_mutex); diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index c356c0a30c3..402d41723c3 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -381,7 +381,7 @@ int del_mtd_device (struct mtd_info *mtd) void register_mtd_user (struct mtd_notifier *new) { - int i; + struct mtd_info *mtd; mutex_lock(&mtd_table_mutex); @@ -389,9 +389,8 @@ void register_mtd_user (struct mtd_notifier *new) __module_get(THIS_MODULE); - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i]) - new->add(mtd_table[i]); + mtd_for_each_device(mtd) + new->add(mtd); mutex_unlock(&mtd_table_mutex); } @@ -408,15 +407,14 @@ void register_mtd_user (struct mtd_notifier *new) int unregister_mtd_user (struct mtd_notifier *old) { - int i; + struct mtd_info *mtd; mutex_lock(&mtd_table_mutex); module_put(THIS_MODULE); - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i]) - old->remove(mtd_table[i]); + mtd_for_each_device(mtd) + old->remove(mtd); list_del(&old->list); mutex_unlock(&mtd_table_mutex); @@ -438,15 +436,18 @@ int unregister_mtd_user (struct mtd_notifier *old) struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) { - struct mtd_info *ret = NULL; - int i, err = -ENODEV; + struct mtd_info *ret = NULL, *other; + int err = -ENODEV; mutex_lock(&mtd_table_mutex); if (num == -1) { - for (i=0; i< MAX_MTD_DEVICES; i++) - if (mtd_table[i] == mtd) - ret = mtd_table[i]; + mtd_for_each_device(other) { + if (other == mtd) { + ret = mtd; + break; + } + } } else if (num >= 0 && num < MAX_MTD_DEVICES) { ret = mtd_table[num]; if (mtd && mtd != ret) @@ -487,14 +488,14 @@ out_unlock: struct mtd_info *get_mtd_device_nm(const char *name) { - int i, err = -ENODEV; - struct mtd_info *mtd = NULL; + int err = -ENODEV; + struct mtd_info *mtd = NULL, *other; mutex_lock(&mtd_table_mutex); - for (i = 0; i < MAX_MTD_DEVICES; i++) { - if (mtd_table[i] && !strcmp(name, mtd_table[i]->name)) { - mtd = mtd_table[i]; + mtd_for_each_device(other) { + if (!strcmp(name, other->name)) { + mtd = other; break; } } @@ -581,14 +582,9 @@ EXPORT_SYMBOL_GPL(default_mtd_writev); static struct proc_dir_entry *proc_mtd; -static inline int mtd_proc_info (char *buf, int i) +static inline int mtd_proc_info(char *buf, struct mtd_info *this) { - struct mtd_info *this = mtd_table[i]; - - if (!this) - return 0; - - return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", i, + return sprintf(buf, "mtd%d: %8.8llx %8.8x \"%s\"\n", this->index, (unsigned long long)this->size, this->erasesize, this->name); } @@ -596,15 +592,15 @@ static inline int mtd_proc_info (char *buf, int i) static int mtd_read_proc (char *page, char **start, off_t off, int count, int *eof, void *data_unused) { - int len, l, i; + struct mtd_info *mtd; + int len, l; off_t begin = 0; mutex_lock(&mtd_table_mutex); len = sprintf(page, "dev: size erasesize name\n"); - for (i=0; i< MAX_MTD_DEVICES; i++) { - - l = mtd_proc_info(page + len, i); + mtd_for_each_device(mtd) { + l = mtd_proc_info(page + len, mtd); len += l; if (len+begin > off+count) goto done; diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index a33251f4b87..e2f93a30073 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -9,3 +9,18 @@ extern struct mutex mtd_table_mutex; extern struct mtd_info *mtd_table[MAX_MTD_DEVICES]; + +static inline struct mtd_info *__mtd_next_device(int i) +{ + while (i < MAX_MTD_DEVICES) { + if (mtd_table[i]) + return mtd_table[i]; + i++; + } + return NULL; +} + +#define mtd_for_each_device(mtd) \ + for ((mtd) = __mtd_next_device(0); \ + (mtd) != NULL; \ + (mtd) = __mtd_next_device(mtd->index + 1)) -- cgit v1.2.3-70-g09d2 From 677c2aec8cdd5ae33b5fab266941cf6c6dc4d59f Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 29 Jan 2010 20:57:18 +0000 Subject: mtd: Use get_mtd_device_nm() to find named device in get_sb_mtd() This removes the need to know the number of MTD devices. Signed-off-by: Ben Hutchings Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdsuper.c | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdsuper.c b/drivers/mtd/mtdsuper.c index af8b42e0a55..d2570523d70 100644 --- a/drivers/mtd/mtdsuper.c +++ b/drivers/mtd/mtdsuper.c @@ -150,18 +150,12 @@ int get_sb_mtd(struct file_system_type *fs_type, int flags, DEBUG(1, "MTDSB: mtd:%%s, name \"%s\"\n", dev_name + 4); - for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { - mtd = get_mtd_device(NULL, mtdnr); - if (!IS_ERR(mtd)) { - if (!strcmp(mtd->name, dev_name + 4)) - return get_sb_mtd_aux( - fs_type, flags, - dev_name, data, mtd, - fill_super, mnt); - - put_mtd_device(mtd); - } - } + mtd = get_mtd_device_nm(dev_name + 4); + if (!IS_ERR(mtd)) + return get_sb_mtd_aux( + fs_type, flags, + dev_name, data, mtd, + fill_super, mnt); printk(KERN_NOTICE "MTD:" " MTD device with name \"%s\" not found.\n", -- cgit v1.2.3-70-g09d2 From e99e90aef17517d99be8e049b2f5cc563cd6862a Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 29 Jan 2010 20:58:08 +0000 Subject: mtd: nandsim: Define CONFIG_NANDSIM_MAX_PARTS and use it instead of MAX_MTD_DEVICES MAX_MTD_DEVICES is about to be removed. Signed-off-by: Ben Hutchings Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nandsim.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 3cf91804867..8a0a5d16e0e 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -80,6 +80,9 @@ #ifndef CONFIG_NANDSIM_DBG #define CONFIG_NANDSIM_DBG 0 #endif +#ifndef CONFIG_NANDSIM_MAX_PARTS +#define CONFIG_NANDSIM_MAX_PARTS 32 +#endif static uint first_id_byte = CONFIG_NANDSIM_FIRST_ID_BYTE; static uint second_id_byte = CONFIG_NANDSIM_SECOND_ID_BYTE; @@ -94,7 +97,7 @@ static uint bus_width = CONFIG_NANDSIM_BUS_WIDTH; static uint do_delays = CONFIG_NANDSIM_DO_DELAYS; static uint log = CONFIG_NANDSIM_LOG; static uint dbg = CONFIG_NANDSIM_DBG; -static unsigned long parts[MAX_MTD_DEVICES]; +static unsigned long parts[CONFIG_NANDSIM_MAX_PARTS]; static unsigned int parts_num; static char *badblocks = NULL; static char *weakblocks = NULL; @@ -288,7 +291,7 @@ union ns_mem { * The structure which describes all the internal simulator data. */ struct nandsim { - struct mtd_partition partitions[MAX_MTD_DEVICES]; + struct mtd_partition partitions[CONFIG_NANDSIM_MAX_PARTS]; unsigned int nbparts; uint busw; /* flash chip bus width (8 or 16) */ -- cgit v1.2.3-70-g09d2 From 24c15496771ea1f3902dee23f746042ba34dc2b8 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 29 Jan 2010 20:58:23 +0000 Subject: mtd: Remove unnecessary comparisons with MAX_MTD_DEVICES MAX_MTD_DEVICES is about to be removed. Signed-off-by: Ben Hutchings Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/pmc551.c | 4 ++-- drivers/mtd/mtdchar.c | 3 --- drivers/mtd/mtdoops.c | 5 ----- 3 files changed, 2 insertions(+), 10 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index d2fd550f7e0..fc8ea0a57ac 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -668,7 +668,7 @@ static int __init init_pmc551(void) { struct pci_dev *PCI_Device = NULL; struct mypriv *priv; - int count, found = 0; + int found = 0; struct mtd_info *mtd; u32 length = 0; @@ -695,7 +695,7 @@ static int __init init_pmc551(void) /* * PCU-bus chipset probe. */ - for (count = 0; count < MAX_MTD_DEVICES; count++) { + for (;;) { if ((PCI_Device = pci_get_device(PCI_VENDOR_ID_V3_SEMI, PCI_DEVICE_ID_V3_SEMI_V370PDC, diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index bce0a07cbac..9f826cda274 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -67,9 +67,6 @@ static int mtd_open(struct inode *inode, struct file *file) DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); - if (devnum >= MAX_MTD_DEVICES) - return -ENODEV; - /* You can't open the RO devices RW */ if ((file->f_mode & FMODE_WRITE) && (minor & 1)) return -EACCES; diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index 92e12df0917..328313c3dcc 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -429,11 +429,6 @@ static int __init mtdoops_init(void) mtd_index = simple_strtoul(mtddev, &endp, 0); if (*endp == '\0') cxt->mtd_index = mtd_index; - if (cxt->mtd_index > MAX_MTD_DEVICES) { - printk(KERN_ERR "mtdoops: invalid mtd device number (%u) given\n", - mtd_index); - return -EINVAL; - } cxt->oops_buf = vmalloc(record_size); if (!cxt->oops_buf) { -- cgit v1.2.3-70-g09d2 From cbfe93e9cedfcd59689bad9e67f57ef67545e5a0 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 29 Jan 2010 20:58:37 +0000 Subject: mtd: mtdblock: Dynamically allocate cache info structures Since we allocate struct mtd_blktrans_dev for each block device, we can add our own structure members to the end. Therefore embed struct mtd_blktrans_dev in struct mtdblk_dev and remove the static array of struct mtdblk_dev. Also remove the redundant pointer to struct mtd_info. This is preparation for removing the static limit on the number of MTD devices. Signed-off-by: Ben Hutchings Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdblock.c | 74 +++++++++++++++++++++----------------------------- 1 file changed, 31 insertions(+), 43 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 9f41b1a853c..69f6bf2e0a8 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -19,15 +19,15 @@ #include -static struct mtdblk_dev { - struct mtd_info *mtd; +struct mtdblk_dev { + struct mtd_blktrans_dev mbd; int count; struct mutex cache_mutex; unsigned char *cache_data; unsigned long cache_offset; unsigned int cache_size; enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; -} *mtdblks[MAX_MTD_DEVICES]; +}; static struct mutex mtdblks_lock; @@ -98,7 +98,7 @@ static int erase_write (struct mtd_info *mtd, unsigned long pos, static int write_cached_data (struct mtdblk_dev *mtdblk) { - struct mtd_info *mtd = mtdblk->mtd; + struct mtd_info *mtd = mtdblk->mbd.mtd; int ret; if (mtdblk->cache_state != STATE_DIRTY) @@ -128,7 +128,7 @@ static int write_cached_data (struct mtdblk_dev *mtdblk) static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, int len, const char *buf) { - struct mtd_info *mtd = mtdblk->mtd; + struct mtd_info *mtd = mtdblk->mbd.mtd; unsigned int sect_size = mtdblk->cache_size; size_t retlen; int ret; @@ -198,7 +198,7 @@ static int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, int len, char *buf) { - struct mtd_info *mtd = mtdblk->mtd; + struct mtd_info *mtd = mtdblk->mbd.mtd; unsigned int sect_size = mtdblk->cache_size; size_t retlen; int ret; @@ -244,16 +244,16 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, static int mtdblock_readsect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) { - struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); return do_cached_read(mtdblk, block<<9, 512, buf); } static int mtdblock_writesect(struct mtd_blktrans_dev *dev, unsigned long block, char *buf) { - struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); if (unlikely(!mtdblk->cache_data && mtdblk->cache_size)) { - mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); + mtdblk->cache_data = vmalloc(mtdblk->mbd.mtd->erasesize); if (!mtdblk->cache_data) return -EINTR; /* -EINTR is not really correct, but it is the best match @@ -266,37 +266,26 @@ static int mtdblock_writesect(struct mtd_blktrans_dev *dev, static int mtdblock_open(struct mtd_blktrans_dev *mbd) { - struct mtdblk_dev *mtdblk; - struct mtd_info *mtd = mbd->mtd; - int dev = mbd->devnum; + struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd); DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); mutex_lock(&mtdblks_lock); - if (mtdblks[dev]) { - mtdblks[dev]->count++; + if (mtdblk->count) { + mtdblk->count++; mutex_unlock(&mtdblks_lock); return 0; } /* OK, it's not open. Create cache info for it */ - mtdblk = kzalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); - if (!mtdblk) { - mutex_unlock(&mtdblks_lock); - return -ENOMEM; - } - mtdblk->count = 1; - mtdblk->mtd = mtd; - mutex_init(&mtdblk->cache_mutex); mtdblk->cache_state = STATE_EMPTY; - if ( !(mtdblk->mtd->flags & MTD_NO_ERASE) && mtdblk->mtd->erasesize) { - mtdblk->cache_size = mtdblk->mtd->erasesize; + if (!(mbd->mtd->flags & MTD_NO_ERASE) && mbd->mtd->erasesize) { + mtdblk->cache_size = mbd->mtd->erasesize; mtdblk->cache_data = NULL; } - mtdblks[dev] = mtdblk; mutex_unlock(&mtdblks_lock); DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); @@ -306,8 +295,7 @@ static int mtdblock_open(struct mtd_blktrans_dev *mbd) static int mtdblock_release(struct mtd_blktrans_dev *mbd) { - int dev = mbd->devnum; - struct mtdblk_dev *mtdblk = mtdblks[dev]; + struct mtdblk_dev *mtdblk = container_of(mbd, struct mtdblk_dev, mbd); DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); @@ -318,12 +306,10 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd) mutex_unlock(&mtdblk->cache_mutex); if (!--mtdblk->count) { - /* It was the last usage. Free the device */ - mtdblks[dev] = NULL; - if (mtdblk->mtd->sync) - mtdblk->mtd->sync(mtdblk->mtd); + /* It was the last usage. Free the cache */ + if (mbd->mtd->sync) + mbd->mtd->sync(mbd->mtd); vfree(mtdblk->cache_data); - kfree(mtdblk); } mutex_unlock(&mtdblks_lock); @@ -335,40 +321,42 @@ static int mtdblock_release(struct mtd_blktrans_dev *mbd) static int mtdblock_flush(struct mtd_blktrans_dev *dev) { - struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); mutex_lock(&mtdblk->cache_mutex); write_cached_data(mtdblk); mutex_unlock(&mtdblk->cache_mutex); - if (mtdblk->mtd->sync) - mtdblk->mtd->sync(mtdblk->mtd); + if (dev->mtd->sync) + dev->mtd->sync(dev->mtd); return 0; } static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - struct mtd_blktrans_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); + struct mtdblk_dev *dev = kzalloc(sizeof(*dev), GFP_KERNEL); if (!dev) return; - dev->mtd = mtd; - dev->devnum = mtd->index; + dev->mbd.mtd = mtd; + dev->mbd.devnum = mtd->index; - dev->size = mtd->size >> 9; - dev->tr = tr; + dev->mbd.size = mtd->size >> 9; + dev->mbd.tr = tr; if (!(mtd->flags & MTD_WRITEABLE)) - dev->readonly = 1; + dev->mbd.readonly = 1; - add_mtd_blktrans_dev(dev); + add_mtd_blktrans_dev(&dev->mbd); } static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { + struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); + del_mtd_blktrans_dev(dev); - kfree(dev); + kfree(mtdblk); } static struct mtd_blktrans_ops mtdblock_tr = { -- cgit v1.2.3-70-g09d2 From b520e412faaaad35641aeedd6059179f9f1b393c Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 29 Jan 2010 20:59:42 +0000 Subject: mtd: Replace static array of devices with an idr structure Signed-off-by: Ben Hutchings Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdcore.c | 151 +++++++++++++++++++++++++----------------------- drivers/mtd/mtdcore.h | 12 +--- include/linux/mtd/mtd.h | 1 - 3 files changed, 81 insertions(+), 83 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 402d41723c3..b3b98d1fffc 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -17,6 +17,7 @@ #include #include #include +#include #include #include "internal.h" @@ -33,13 +34,18 @@ static struct class mtd_class = { .resume = mtd_cls_resume, }; +static DEFINE_IDR(mtd_idr); + /* These are exported solely for the purpose of mtd_blkdevs.c. You should not use them for _anything_ else */ DEFINE_MUTEX(mtd_table_mutex); -struct mtd_info *mtd_table[MAX_MTD_DEVICES]; - EXPORT_SYMBOL_GPL(mtd_table_mutex); -EXPORT_SYMBOL_GPL(mtd_table); + +struct mtd_info *__mtd_next_device(int i) +{ + return idr_get_next(&mtd_idr, &i); +} +EXPORT_SYMBOL_GPL(__mtd_next_device); static LIST_HEAD(mtd_notifiers); @@ -235,13 +241,13 @@ static struct device_type mtd_devtype = { * Add a device to the list of MTD devices present in the system, and * notify each currently active MTD 'user' of its arrival. Returns * zero on success or 1 on failure, which currently will only happen - * if the number of present devices exceeds MAX_MTD_DEVICES (i.e. 16) - * or there's a sysfs error. + * if there is insufficient memory or a sysfs error. */ int add_mtd_device(struct mtd_info *mtd) { - int i; + struct mtd_notifier *not; + int i, error; if (!mtd->backing_dev_info) { switch (mtd->type) { @@ -260,70 +266,73 @@ int add_mtd_device(struct mtd_info *mtd) BUG_ON(mtd->writesize == 0); mutex_lock(&mtd_table_mutex); - for (i=0; i < MAX_MTD_DEVICES; i++) - if (!mtd_table[i]) { - struct mtd_notifier *not; - - mtd_table[i] = mtd; - mtd->index = i; - mtd->usecount = 0; - - if (is_power_of_2(mtd->erasesize)) - mtd->erasesize_shift = ffs(mtd->erasesize) - 1; - else - mtd->erasesize_shift = 0; - - if (is_power_of_2(mtd->writesize)) - mtd->writesize_shift = ffs(mtd->writesize) - 1; - else - mtd->writesize_shift = 0; - - mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; - mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; - - /* Some chips always power up locked. Unlock them now */ - if ((mtd->flags & MTD_WRITEABLE) - && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { - if (mtd->unlock(mtd, 0, mtd->size)) - printk(KERN_WARNING - "%s: unlock failed, " - "writes may not work\n", - mtd->name); - } + do { + if (!idr_pre_get(&mtd_idr, GFP_KERNEL)) + goto fail_locked; + error = idr_get_new(&mtd_idr, mtd, &i); + } while (error == -EAGAIN); - /* Caller should have set dev.parent to match the - * physical device. - */ - mtd->dev.type = &mtd_devtype; - mtd->dev.class = &mtd_class; - mtd->dev.devt = MTD_DEVT(i); - dev_set_name(&mtd->dev, "mtd%d", i); - dev_set_drvdata(&mtd->dev, mtd); - if (device_register(&mtd->dev) != 0) { - mtd_table[i] = NULL; - break; - } + if (error) + goto fail_locked; - if (MTD_DEVT(i)) - device_create(&mtd_class, mtd->dev.parent, - MTD_DEVT(i) + 1, - NULL, "mtd%dro", i); - - DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); - /* No need to get a refcount on the module containing - the notifier, since we hold the mtd_table_mutex */ - list_for_each_entry(not, &mtd_notifiers, list) - not->add(mtd); - - mutex_unlock(&mtd_table_mutex); - /* We _know_ we aren't being removed, because - our caller is still holding us here. So none - of this try_ nonsense, and no bitching about it - either. :) */ - __module_get(THIS_MODULE); - return 0; - } + mtd->index = i; + mtd->usecount = 0; + + if (is_power_of_2(mtd->erasesize)) + mtd->erasesize_shift = ffs(mtd->erasesize) - 1; + else + mtd->erasesize_shift = 0; + + if (is_power_of_2(mtd->writesize)) + mtd->writesize_shift = ffs(mtd->writesize) - 1; + else + mtd->writesize_shift = 0; + + mtd->erasesize_mask = (1 << mtd->erasesize_shift) - 1; + mtd->writesize_mask = (1 << mtd->writesize_shift) - 1; + + /* Some chips always power up locked. Unlock them now */ + if ((mtd->flags & MTD_WRITEABLE) + && (mtd->flags & MTD_POWERUP_LOCK) && mtd->unlock) { + if (mtd->unlock(mtd, 0, mtd->size)) + printk(KERN_WARNING + "%s: unlock failed, writes may not work\n", + mtd->name); + } + + /* Caller should have set dev.parent to match the + * physical device. + */ + mtd->dev.type = &mtd_devtype; + mtd->dev.class = &mtd_class; + mtd->dev.devt = MTD_DEVT(i); + dev_set_name(&mtd->dev, "mtd%d", i); + dev_set_drvdata(&mtd->dev, mtd); + if (device_register(&mtd->dev) != 0) + goto fail_added; + + if (MTD_DEVT(i)) + device_create(&mtd_class, mtd->dev.parent, + MTD_DEVT(i) + 1, + NULL, "mtd%dro", i); + + DEBUG(0, "mtd: Giving out device %d to %s\n", i, mtd->name); + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->add(mtd); + + mutex_unlock(&mtd_table_mutex); + /* We _know_ we aren't being removed, because + our caller is still holding us here. So none + of this try_ nonsense, and no bitching about it + either. :) */ + __module_get(THIS_MODULE); + return 0; +fail_added: + idr_remove(&mtd_idr, i); +fail_locked: mutex_unlock(&mtd_table_mutex); return 1; } @@ -344,7 +353,7 @@ int del_mtd_device (struct mtd_info *mtd) mutex_lock(&mtd_table_mutex); - if (mtd_table[mtd->index] != mtd) { + if (idr_find(&mtd_idr, mtd->index) != mtd) { ret = -ENODEV; } else if (mtd->usecount) { printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", @@ -360,7 +369,7 @@ int del_mtd_device (struct mtd_info *mtd) list_for_each_entry(not, &mtd_notifiers, list) not->remove(mtd); - mtd_table[mtd->index] = NULL; + idr_remove(&mtd_idr, mtd->index); module_put(THIS_MODULE); ret = 0; @@ -448,8 +457,8 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) break; } } - } else if (num >= 0 && num < MAX_MTD_DEVICES) { - ret = mtd_table[num]; + } else if (num >= 0) { + ret = idr_find(&mtd_idr, num); if (mtd && mtd != ret) ret = NULL; } diff --git a/drivers/mtd/mtdcore.h b/drivers/mtd/mtdcore.h index e2f93a30073..6a64fdebc89 100644 --- a/drivers/mtd/mtdcore.h +++ b/drivers/mtd/mtdcore.h @@ -8,17 +8,7 @@ should not use them for _anything_ else */ extern struct mutex mtd_table_mutex; -extern struct mtd_info *mtd_table[MAX_MTD_DEVICES]; - -static inline struct mtd_info *__mtd_next_device(int i) -{ - while (i < MAX_MTD_DEVICES) { - if (mtd_table[i]) - return mtd_table[i]; - i++; - } - return NULL; -} +extern struct mtd_info *__mtd_next_device(int i); #define mtd_for_each_device(mtd) \ for ((mtd) = __mtd_next_device(0); \ diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 0f32a9b6ff5..ba53ecca107 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -20,7 +20,6 @@ #define MTD_CHAR_MAJOR 90 #define MTD_BLOCK_MAJOR 31 -#define MAX_MTD_DEVICES 32 #define MTD_ERASE_PENDING 0x01 #define MTD_ERASING 0x02 -- cgit v1.2.3-70-g09d2 From 4d3a8534bdbcf4843fc8ad05c9a81a964fc65237 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 29 Jan 2010 20:59:53 +0000 Subject: mtd: Raise limit on block device minor numbers add_mtd_blktrans_dev() imposes a maximum of 257 devices per block translator. This was presumably meant to prevent overflow back in the days of 8-bit minor numbers. Instead, check against MINORMASK and the limits of the partition naming scheme. Signed-off-by: Ben Hutchings Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 85a52b3c769..2f8c202dbd8 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -242,9 +242,12 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) if (new->devnum == -1) new->devnum = last_devnum+1; - if ((new->devnum << tr->part_bits) > 256) { + /* Check that the device and any partitions will get valid + * minor numbers and that the disk naming code below can cope + * with this number. */ + if (new->devnum > (MINORMASK >> tr->part_bits) || + (tr->part_bits && new->devnum >= 27 * 26)) return -EBUSY; - } list_add_tail(&new->list, &tr->devs); added: -- cgit v1.2.3-70-g09d2 From dad0db318b391ddb9845ac5e52044f921219bf69 Mon Sep 17 00:00:00 2001 From: Ben Hutchings Date: Fri, 29 Jan 2010 21:00:04 +0000 Subject: mtdchar: Register the full range of minor numbers register_chrdev() registers minor numbers up to 255, but we can now potentially have much larger numbers. Signed-off-by: Ben Hutchings Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index 9f826cda274..c355491d132 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -955,7 +955,8 @@ static int __init init_mtdchar(void) { int status; - status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops); + status = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, + "mtd", &mtd_fops); if (status < 0) { printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", MTD_CHAR_MAJOR); @@ -966,7 +967,7 @@ static int __init init_mtdchar(void) static void __exit cleanup_mtdchar(void) { - unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); } module_init(init_mtdchar); -- cgit v1.2.3-70-g09d2 From 9a5dea7b1046510fdcc81c523405494fd07ec303 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 9 Feb 2010 13:42:39 +0200 Subject: mtd: maps: ceiva: do not return random numbers When machine_is_ceiva() returns zero, 'clps_setup_flash()' returns a value of an unitialized variable. Fix this. Spotted by David Binderman. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/ceiva.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index d41f34766e5..c09f4f57093 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -253,7 +253,7 @@ static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd static int __init clps_setup_flash(void) { - int nr; + int nr = 0; #ifdef CONFIG_ARCH_CEIVA if (machine_is_ceiva()) { -- cgit v1.2.3-70-g09d2 From 6fe5a6acdc126107e54a6c584536e09ab7dde949 Mon Sep 17 00:00:00 2001 From: Vimal Singh Date: Wed, 3 Feb 2010 14:12:24 +0530 Subject: mtd: nand: create a helper verification function ... verification for 'nand_erase_nand' These checks are expected to be used by 'nand_lock' and 'nand_unlock' routines too. As all these three are block aligned operations. So, creating a helper function for this makes sense. Signed-off-by: Vimal Singh Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 48 +++++++++++++++++++++++++++----------------- 1 file changed, 30 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 8f2958fe214..2dfeb4bea83 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -108,6 +108,35 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, */ DEFINE_LED_TRIGGER(nand_led_trigger); +static int check_offs_len(struct mtd_info *mtd, + loff_t ofs, uint64_t len) +{ + struct nand_chip *chip = mtd->priv; + int ret = 0; + + /* Start address must align on block boundary */ + if (ofs & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); + ret = -EINVAL; + } + + /* Length must align on block boundary */ + if (len & ((1 << chip->phys_erase_shift) - 1)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", + __func__); + ret = -EINVAL; + } + + /* Do not allow past end of device */ + if (ofs + len > mtd->size) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Past end of device\n", + __func__); + ret = -EINVAL; + } + + return ret; +} + /** * nand_release_device - [GENERIC] release chip * @mtd: MTD device structure @@ -2293,25 +2322,8 @@ int nand_erase_nand(struct mtd_info *mtd, struct erase_info *instr, __func__, (unsigned long long)instr->addr, (unsigned long long)instr->len); - /* Start address must align on block boundary */ - if (instr->addr & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Unaligned address\n", __func__); - return -EINVAL; - } - - /* Length must align on block boundary */ - if (instr->len & ((1 << chip->phys_erase_shift) - 1)) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Length not block aligned\n", - __func__); - return -EINVAL; - } - - /* Do not allow erase past end of device */ - if ((instr->len + instr->addr) > mtd->size) { - DEBUG(MTD_DEBUG_LEVEL0, "%s: Erase past end of device\n", - __func__); + if (check_offs_len(mtd, instr->addr, instr->len)) return -EINVAL; - } instr->fail_addr = MTD_FAIL_ADDR_UNKNOWN; -- cgit v1.2.3-70-g09d2 From 7d70f334ad2bf1b3aaa1f0699c0f442e14bcc9e0 Mon Sep 17 00:00:00 2001 From: Vimal Singh Date: Mon, 8 Feb 2010 15:50:49 +0530 Subject: mtd: nand: add lock/unlock routines Add nand lock / unlock routines. At least 'micron' parts support this. Signed-off-by: Vimal Singh Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 164 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mtd/nand.h | 10 +++ 2 files changed, 174 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 2dfeb4bea83..ed62e1ee0f8 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -863,6 +863,168 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip) return status; } +/** + * __nand_unlock - [REPLACABLE] unlocks specified locked blockes + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * @invert - when = 0, unlock the range of blocks within the lower and + * upper boundary address + * whne = 1, unlock the range of blocks outside the boundaries + * of the lower and upper boundary address + * + * @return - unlock status + */ +static int __nand_unlock(struct mtd_info *mtd, loff_t ofs, + uint64_t len, int invert) +{ + int ret = 0; + int status, page; + struct nand_chip *chip = mtd->priv; + + /* Submit address of first page to unlock */ + page = ofs >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_UNLOCK1, -1, page & chip->pagemask); + + /* Submit address of last page to unlock */ + page = (ofs + len) >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_UNLOCK2, -1, + (page | invert) & chip->pagemask); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + udelay(1000); + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + __func__, status); + ret = -EIO; + } + + return ret; +} + +/** + * nand_unlock - [REPLACABLE] unlocks specified locked blockes + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * + * @return - unlock status + */ +int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr; + struct nand_chip *chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + if (check_offs_len(mtd, ofs, len)) + ret = -EINVAL; + + /* Align to last block address if size addresses end of the device */ + if (ofs + len == mtd->size) + len -= mtd->erasesize; + + nand_get_device(chip, mtd, FL_UNLOCKING); + + /* Shift to get chip number */ + chipnr = ofs >> chip->chip_shift; + + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); + ret = -EIO; + goto out; + } + + ret = __nand_unlock(mtd, ofs, len, 0); + +out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + + nand_release_device(mtd); + + return ret; +} + +/** + * nand_lock - [REPLACABLE] locks all blockes present in the device + * + * @param mtd - mtd info + * @param ofs - offset to start unlock from + * @param len - length to unlock + * + * @return - lock status + * + * This feature is not support in many NAND parts. 'Micron' NAND parts + * do have this feature, but it allows only to lock all blocks not for + * specified range for block. + * + * Implementing 'lock' feature by making use of 'unlock', for now. + */ +int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) +{ + int ret = 0; + int chipnr, status, page; + struct nand_chip *chip = mtd->priv; + + DEBUG(MTD_DEBUG_LEVEL3, "%s: start = 0x%012llx, len = %llu\n", + __func__, (unsigned long long)ofs, len); + + if (check_offs_len(mtd, ofs, len)) + ret = -EINVAL; + + nand_get_device(chip, mtd, FL_LOCKING); + + /* Shift to get chip number */ + chipnr = ofs >> chip->chip_shift; + + chip->select_chip(mtd, chipnr); + + /* Check, if it is write protected */ + if (nand_check_wp(mtd)) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Device is write protected!!!\n", + __func__); + status = MTD_ERASE_FAILED; + ret = -EIO; + goto out; + } + + /* Submit address of first page to lock */ + page = ofs >> chip->page_shift; + chip->cmdfunc(mtd, NAND_CMD_LOCK, -1, page & chip->pagemask); + + /* Call wait ready function */ + status = chip->waitfunc(mtd, chip); + udelay(1000); + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG(MTD_DEBUG_LEVEL0, "%s: Error status = 0x%08x\n", + __func__, status); + ret = -EIO; + goto out; + } + + ret = __nand_unlock(mtd, ofs, len, 0x1); + +out: + /* de-select the NAND device */ + chip->select_chip(mtd, -1); + + nand_release_device(mtd); + + return ret; +} + /** * nand_read_page_raw - [Intern] read raw page data without ecc * @mtd: mtd info structure @@ -3089,6 +3251,8 @@ void nand_release(struct mtd_info *mtd) kfree(chip->buffers); } +EXPORT_SYMBOL_GPL(nand_lock); +EXPORT_SYMBOL_GPL(nand_unlock); EXPORT_SYMBOL_GPL(nand_scan); EXPORT_SYMBOL_GPL(nand_scan_ident); EXPORT_SYMBOL_GPL(nand_scan_tail); diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index ccab9dfc521..48bc2c54302 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -38,6 +38,12 @@ extern void nand_release (struct mtd_info *mtd); /* Internal helper for board drivers which need to override command function */ extern void nand_wait_ready(struct mtd_info *mtd); +/* locks all blockes present in the device */ +extern int nand_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len); + +/* unlocks specified locked blockes */ +extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); + /* The maximum number of NAND chips in an array */ #define NAND_MAX_CHIPS 8 @@ -82,6 +88,10 @@ extern void nand_wait_ready(struct mtd_info *mtd); #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff +#define NAND_CMD_LOCK 0x2a +#define NAND_CMD_UNLOCK1 0x23 +#define NAND_CMD_UNLOCK2 0x24 + /* Extended commands for large page devices */ #define NAND_CMD_READSTART 0x30 #define NAND_CMD_RNDOUTSTART 0xE0 -- cgit v1.2.3-70-g09d2 From 932f5d21ccd2705f1fb22e8a9e0da42013dcee17 Mon Sep 17 00:00:00 2001 From: Aaro Koskinen Date: Wed, 10 Feb 2010 19:03:19 +0200 Subject: mtd: OneNAND: do not use DMA if oops in progress Otherwise we may hang if we are called from panic() through mtdoops. Signed-off-by: Aaro Koskinen Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/onenand/omap2.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index 75f38b95811..dfbab6c72b7 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -308,7 +308,7 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area, goto out_copy; /* panic_write() may be in an interrupt context */ - if (in_interrupt()) + if (in_interrupt() || oops_in_progress) goto out_copy; if (buf >= high_memory) { @@ -385,7 +385,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, goto out_copy; /* panic_write() may be in an interrupt context */ - if (in_interrupt()) + if (in_interrupt() || oops_in_progress) goto out_copy; if (buf >= high_memory) { -- cgit v1.2.3-70-g09d2 From 35109451f18280000ce5e7b76ac8d29eb222823b Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Mon, 15 Feb 2010 22:57:24 +0100 Subject: mtd: inftl: misplaced parenthesis in find_boot_record The parenthesis was misplaced, upon error a one was shown. [dwmw2: Fix the code not to do the assignment within the if() statement] Signed-off-by: Roel Kluin Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/inftlmount.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c index 32e82aef3e5..8f988d7d3c5 100644 --- a/drivers/mtd/inftlmount.c +++ b/drivers/mtd/inftlmount.c @@ -100,9 +100,10 @@ static int find_boot_record(struct INFTLrecord *inftl) } /* To be safer with BIOS, also use erase mark as discriminant */ - if ((ret = inftl_read_oob(mtd, block * inftl->EraseSize + - SECTORSIZE + 8, 8, &retlen, - (char *)&h1) < 0)) { + ret = inftl_read_oob(mtd, + block * inftl->EraseSize + SECTORSIZE + 8, + 8, &retlen,(char *)&h1); + if (ret < 0) { printk(KERN_WARNING "INFTL: ANAND header found at " "0x%x in mtd%d, but OOB data read failed " "(err %d)\n", block * inftl->EraseSize, -- cgit v1.2.3-70-g09d2 From 66803762c19f2e45ff4cc13cf63194589eb698c2 Mon Sep 17 00:00:00 2001 From: Eric Benard Date: Wed, 9 Dec 2009 12:12:43 +0100 Subject: mtd: mxc_nand: add RESET command support mxc_nand driver must support the RESET Command in order to support Micron NAND which need a reset before any other command. Signed-off-by: Eric Benard Acked-by: Sascha Hauer Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 970ce6bd06a..06cc378196b 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -638,6 +638,7 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: + case NAND_CMD_RESET: send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); -- cgit v1.2.3-70-g09d2 From f3e69c6584be2db1ccd5292d6a1d7c566d265701 Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Tue, 15 Dec 2009 23:01:06 +0100 Subject: mtd: move more manufacturers to the common cfi.h header file Move MANUFACTURER_MACRONIX and MANUFACTURER_SST definitions to the include/linux/mtd/cfi.h header file and rename them to CFI_MFR_MACRONIX and CFI_MFR_SST. All references in drivers/mtd/chips/cfi_cmdset_0002.c are updated to reflect this. Signed-off-by: Guillaume LECERF Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 14 +++++--------- include/linux/mtd/cfi.h | 12 +++++++----- 2 files changed, 12 insertions(+), 14 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 1ebdcdd72d8..ea2a7f66ddf 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -43,10 +43,6 @@ #define MAX_WORD_RETRIES 3 -#define MANUFACTURER_AMD 0x0001 -#define MANUFACTURER_ATMEL 0x001F -#define MANUFACTURER_MACRONIX 0x00C2 -#define MANUFACTURER_SST 0x00BF #define SST49LF004B 0x0060 #define SST49LF040B 0x0050 #define SST49LF008A 0x005a @@ -168,7 +164,7 @@ static void fixup_amd_bootblock(struct mtd_info *mtd, void* param) * This reduces the risk of false detection due to * the 8-bit device ID. */ - (cfi->mfr == MANUFACTURER_MACRONIX)) { + (cfi->mfr == CFI_MFR_MACRONIX)) { DEBUG(MTD_DEBUG_LEVEL1, "%s: Macronix MX29LV400C with bottom boot block" " detected\n", map->name); @@ -286,7 +282,7 @@ static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef AMD_BOOTLOC_BUG { CFI_MFR_AMD, CFI_ID_ANY, fixup_amd_bootblock, NULL }, - { MANUFACTURER_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL }, + { CFI_MFR_MACRONIX, CFI_ID_ANY, fixup_amd_bootblock, NULL }, #endif { CFI_MFR_AMD, 0x0050, fixup_use_secsi, NULL, }, { CFI_MFR_AMD, 0x0053, fixup_use_secsi, NULL, }, @@ -304,9 +300,9 @@ static struct cfi_fixup cfi_fixup_table[] = { { 0, 0, NULL, NULL } }; static struct cfi_fixup jedec_fixup_table[] = { - { MANUFACTURER_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, - { MANUFACTURER_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_SST, SST49LF004B, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_SST, SST49LF040B, fixup_use_fwh_lock, NULL, }, + { CFI_MFR_SST, SST49LF008A, fixup_use_fwh_lock, NULL, }, { 0, 0, NULL, NULL } }; diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index a4eefc5810d..cee05b1e62b 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -518,11 +518,13 @@ struct cfi_fixup { #define CFI_MFR_ANY 0xffff #define CFI_ID_ANY 0xffff -#define CFI_MFR_AMD 0x0001 -#define CFI_MFR_INTEL 0x0089 -#define CFI_MFR_ATMEL 0x001F -#define CFI_MFR_SAMSUNG 0x00EC -#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ +#define CFI_MFR_AMD 0x0001 +#define CFI_MFR_ATMEL 0x001F +#define CFI_MFR_INTEL 0x0089 +#define CFI_MFR_MACRONIX 0x00C2 +#define CFI_MFR_SAMSUNG 0x00EC +#define CFI_MFR_SST 0x00BF +#define CFI_MFR_ST 0x0020 /* STMicroelectronics */ void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups); -- cgit v1.2.3-70-g09d2 From bdaefc41627b6f2815ef7aa476dfa4ebb3ad499f Mon Sep 17 00:00:00 2001 From: Vimal Singh Date: Tue, 5 Jan 2010 12:49:24 +0530 Subject: mtd: omap2: fixing compilation warning Fixing below warning in compilation: drivers/mtd/nand/omap2.c: In function 'omap_write_buf_dma_pref': drivers/mtd/nand/omap2.c:508: warning: passing argument 2 of 'omap_nand_dma_transfer' discards qualifiers from pointer target type Signed-off-by: Vimal Singh Signed-off-by: David Woodhouse --- drivers/mtd/nand/omap2.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 1bb799f0125..4eea97c03b2 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -505,7 +505,7 @@ static void omap_write_buf_dma_pref(struct mtd_info *mtd, omap_write_buf_pref(mtd, buf, len); else /* start transfer in DMA mode */ - omap_nand_dma_transfer(mtd, buf, len, 0x1); + omap_nand_dma_transfer(mtd, (u_char *) buf, len, 0x1); } /** -- cgit v1.2.3-70-g09d2 From f35b6eda5184e46bf2393d8970b4b9498daf7bcf Mon Sep 17 00:00:00 2001 From: Vimal Singh Date: Tue, 5 Jan 2010 16:01:08 +0530 Subject: mtd: omap2: correct 'info' pointer in 'omap_nand_remove' Removing OMAP NAND driver, when loaded as a module, gives error and does not get success. This fixes this and makes driver loadable and removable run time. Signed-off-by: Vimal Singh Signed-off-by: David Woodhouse --- drivers/mtd/nand/omap2.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 4eea97c03b2..16120e2dd4a 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -1054,7 +1054,8 @@ out_free_info: static int omap_nand_remove(struct platform_device *pdev) { struct mtd_info *mtd = platform_get_drvdata(pdev); - struct omap_nand_info *info = mtd->priv; + struct omap_nand_info *info = container_of(mtd, struct omap_nand_info, + mtd); platform_set_drvdata(pdev, NULL); if (use_dma) -- cgit v1.2.3-70-g09d2 From c3341d0ceb4de1680572024f50233403c6a8b10d Mon Sep 17 00:00:00 2001 From: Vimal Singh Date: Thu, 7 Jan 2010 12:16:26 +0530 Subject: mtd: omap2 fix prefetch mode read issue There is a bug in nand prefetch read routine, which comes into effect only if nand device is a 16-bit device (as we have in zoom boards). This bug is effective only with below combination of conditions: 1. nand deivce, in use, is a 16 bit device 2. nand driver supports 'subpage' read 3. SW ECC is in use This was not seen old kernel (ex: .23), because when, in early days, we tested this (nand prefetch read in LDP boards) there was no 'subpage read' support. Later when we had subpage read in (.27) kernel, we had hw ecc enabled always in our internal tree. So, we missed this bug. This patch fixes the issue. Signed-off-by: Vimal Singh Signed-off-by: David Woodhouse --- drivers/mtd/nand/omap2.c | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 16120e2dd4a..7df303aed8a 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -295,11 +295,14 @@ static void omap_read_buf_pref(struct mtd_info *mtd, u_char *buf, int len) u32 *p = (u32 *)buf; /* take care of subpage reads */ - for (; len % 4 != 0; ) { - *buf++ = __raw_readb(info->nand.IO_ADDR_R); - len--; + if (len % 4) { + if (info->nand.options & NAND_BUSWIDTH_16) + omap_read_buf16(mtd, buf, len % 4); + else + omap_read_buf8(mtd, buf, len % 4); + p = (u32 *) (buf + len % 4); + len -= len % 4; } - p = (u32 *) buf; /* configure and start prefetch transfer */ ret = gpmc_prefetch_enable(info->gpmc_cs, 0x0, len, 0x0); -- cgit v1.2.3-70-g09d2 From 1385858ee07cbfd68c503a10e4a526d24223d465 Mon Sep 17 00:00:00 2001 From: Roel Kluin Date: Fri, 22 Jan 2010 22:22:52 +0100 Subject: mtd: nand_bcm: fix hot spin and code duplication In the branch where pagesize equalled NAND_DATA_ACCESS_SIZE, NumToRead wasn't decremented in the `while (numToRead > 11)' loop. Also the first and last while loops were duplicated in both branches. Signed-off-by: Roel Kluin Acked-by: Leo Chen Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_bcm_umi.h | 71 +++++++++++++++-------------------------- 1 file changed, 25 insertions(+), 46 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_bcm_umi.h b/drivers/mtd/nand/nand_bcm_umi.h index 7cec2cd9785..198b304d6f7 100644 --- a/drivers/mtd/nand/nand_bcm_umi.h +++ b/drivers/mtd/nand/nand_bcm_umi.h @@ -167,18 +167,27 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, int numToRead = 16; /* There are 16 bytes per sector in the OOB */ /* ECC is already paused when this function is called */ + if (pageSize != NAND_DATA_ACCESS_SIZE) { + /* skip BI */ +#if defined(__KERNEL__) && !defined(STANDALONE) + *oobp++ = REG_NAND_DATA8; +#else + REG_NAND_DATA8; +#endif + numToRead--; + } - if (pageSize == NAND_DATA_ACCESS_SIZE) { - while (numToRead > numEccBytes) { - /* skip free oob region */ + while (numToRead > numEccBytes) { + /* skip free oob region */ #if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; + *oobp++ = REG_NAND_DATA8; #else - REG_NAND_DATA8; + REG_NAND_DATA8; #endif - numToRead--; - } + numToRead--; + } + if (pageSize == NAND_DATA_ACCESS_SIZE) { /* read ECC bytes before BI */ nand_bcm_umi_bch_resume_read_ecc_calc(); @@ -190,6 +199,7 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, #else eccCalc[eccPos++] = REG_NAND_DATA8; #endif + numToRead--; } nand_bcm_umi_bch_pause_read_ecc_calc(); @@ -204,49 +214,18 @@ static inline void nand_bcm_umi_bch_read_oobEcc(uint32_t pageSize, numToRead--; } - /* read ECC bytes */ - nand_bcm_umi_bch_resume_read_ecc_calc(); - while (numToRead) { -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp = REG_NAND_DATA8; - eccCalc[eccPos++] = *oobp; - oobp++; -#else - eccCalc[eccPos++] = REG_NAND_DATA8; -#endif - numToRead--; - } - } else { - /* skip BI */ + } + /* read ECC bytes */ + nand_bcm_umi_bch_resume_read_ecc_calc(); + while (numToRead) { #if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; + *oobp = REG_NAND_DATA8; + eccCalc[eccPos++] = *oobp; + oobp++; #else - REG_NAND_DATA8; + eccCalc[eccPos++] = REG_NAND_DATA8; #endif numToRead--; - - while (numToRead > numEccBytes) { - /* skip free oob region */ -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp++ = REG_NAND_DATA8; -#else - REG_NAND_DATA8; -#endif - numToRead--; - } - - /* read ECC bytes */ - nand_bcm_umi_bch_resume_read_ecc_calc(); - while (numToRead) { -#if defined(__KERNEL__) && !defined(STANDALONE) - *oobp = REG_NAND_DATA8; - eccCalc[eccPos++] = *oobp; - oobp++; -#else - eccCalc[eccPos++] = REG_NAND_DATA8; -#endif - numToRead--; - } } } -- cgit v1.2.3-70-g09d2 From bb315f749f8c800cb8bf8d7dabc4b5fbab97b328 Mon Sep 17 00:00:00 2001 From: Anatolij Gustschin Date: Mon, 15 Feb 2010 18:35:05 +0100 Subject: mtd: nand: Add MPC5121 NAND Flash Controller driver Adds NAND Flash Controller driver for MPC5121 Revision 2. All device features, except hardware ECC and power management, are supported. Signed-off-by: Piotr Ziecik Signed-off-by: Wolfgang Denk Signed-off-by: Anatolij Gustschin Acked-by: Grant Likely Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 7 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/mpc5121_nfc.c | 916 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 924 insertions(+) create mode 100644 drivers/mtd/nand/mpc5121_nfc.c (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 318ef2f2194..8a7ecfab4fe 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -444,6 +444,13 @@ config MTD_NAND_FSL_UPM Enables support for NAND Flash chips wired onto Freescale PowerPC processor localbus with User-Programmable Machine support. +config MTD_NAND_MPC5121_NFC + tristate "MPC5121 built-in NAND Flash Controller support" + depends on PPC_MPC512x + help + This enables the driver for the NAND flash controller on the + MPC5121 SoC. + config MTD_NAND_MXC tristate "MXC NAND support" depends on ARCH_MX2 || ARCH_MX3 diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 355786846bc..1d19b7ab903 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -42,5 +42,6 @@ obj-$(CONFIG_MTD_NAND_TXX9NDFMC) += txx9ndfmc.o obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o +obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c new file mode 100644 index 00000000000..d7333f4dae8 --- /dev/null +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -0,0 +1,916 @@ +/* + * Copyright 2004-2008 Freescale Semiconductor, Inc. + * Copyright 2009 Semihalf. + * + * Approved as OSADL project by a majority of OSADL members and funded + * by OSADL membership fees in 2009; for details see www.osadl.org. + * + * Based on original driver from Freescale Semiconductor + * written by John Rigby on basis + * of drivers/mtd/nand/mxc_nand.c. Reworked and extended + * Piotr Ziecik . + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + * MA 02110-1301, USA. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +/* Addresses for NFC MAIN RAM BUFFER areas */ +#define NFC_MAIN_AREA(n) ((n) * 0x200) + +/* Addresses for NFC SPARE BUFFER areas */ +#define NFC_SPARE_BUFFERS 8 +#define NFC_SPARE_LEN 0x40 +#define NFC_SPARE_AREA(n) (0x1000 + ((n) * NFC_SPARE_LEN)) + +/* MPC5121 NFC registers */ +#define NFC_BUF_ADDR 0x1E04 +#define NFC_FLASH_ADDR 0x1E06 +#define NFC_FLASH_CMD 0x1E08 +#define NFC_CONFIG 0x1E0A +#define NFC_ECC_STATUS1 0x1E0C +#define NFC_ECC_STATUS2 0x1E0E +#define NFC_SPAS 0x1E10 +#define NFC_WRPROT 0x1E12 +#define NFC_NF_WRPRST 0x1E18 +#define NFC_CONFIG1 0x1E1A +#define NFC_CONFIG2 0x1E1C +#define NFC_UNLOCKSTART_BLK0 0x1E20 +#define NFC_UNLOCKEND_BLK0 0x1E22 +#define NFC_UNLOCKSTART_BLK1 0x1E24 +#define NFC_UNLOCKEND_BLK1 0x1E26 +#define NFC_UNLOCKSTART_BLK2 0x1E28 +#define NFC_UNLOCKEND_BLK2 0x1E2A +#define NFC_UNLOCKSTART_BLK3 0x1E2C +#define NFC_UNLOCKEND_BLK3 0x1E2E + +/* Bit Definitions: NFC_BUF_ADDR */ +#define NFC_RBA_MASK (7 << 0) +#define NFC_ACTIVE_CS_SHIFT 5 +#define NFC_ACTIVE_CS_MASK (3 << NFC_ACTIVE_CS_SHIFT) + +/* Bit Definitions: NFC_CONFIG */ +#define NFC_BLS_UNLOCKED (1 << 1) + +/* Bit Definitions: NFC_CONFIG1 */ +#define NFC_ECC_4BIT (1 << 0) +#define NFC_FULL_PAGE_DMA (1 << 1) +#define NFC_SPARE_ONLY (1 << 2) +#define NFC_ECC_ENABLE (1 << 3) +#define NFC_INT_MASK (1 << 4) +#define NFC_BIG_ENDIAN (1 << 5) +#define NFC_RESET (1 << 6) +#define NFC_CE (1 << 7) +#define NFC_ONE_CYCLE (1 << 8) +#define NFC_PPB_32 (0 << 9) +#define NFC_PPB_64 (1 << 9) +#define NFC_PPB_128 (2 << 9) +#define NFC_PPB_256 (3 << 9) +#define NFC_PPB_MASK (3 << 9) +#define NFC_FULL_PAGE_INT (1 << 11) + +/* Bit Definitions: NFC_CONFIG2 */ +#define NFC_COMMAND (1 << 0) +#define NFC_ADDRESS (1 << 1) +#define NFC_INPUT (1 << 2) +#define NFC_OUTPUT (1 << 3) +#define NFC_ID (1 << 4) +#define NFC_STATUS (1 << 5) +#define NFC_CMD_FAIL (1 << 15) +#define NFC_INT (1 << 15) + +/* Bit Definitions: NFC_WRPROT */ +#define NFC_WPC_LOCK_TIGHT (1 << 0) +#define NFC_WPC_LOCK (1 << 1) +#define NFC_WPC_UNLOCK (1 << 2) + +#define DRV_NAME "mpc5121_nfc" + +/* Timeouts */ +#define NFC_RESET_TIMEOUT 1000 /* 1 ms */ +#define NFC_TIMEOUT (HZ / 10) /* 1/10 s */ + +struct mpc5121_nfc_prv { + struct mtd_info mtd; + struct nand_chip chip; + int irq; + void __iomem *regs; + struct clk *clk; + wait_queue_head_t irq_waitq; + uint column; + int spareonly; + void __iomem *csreg; + struct device *dev; +}; + +static void mpc5121_nfc_done(struct mtd_info *mtd); + +#ifdef CONFIG_MTD_PARTITIONS +static const char *mpc5121_nfc_pprobes[] = { "cmdlinepart", NULL }; +#endif + +/* Read NFC register */ +static inline u16 nfc_read(struct mtd_info *mtd, uint reg) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + return in_be16(prv->regs + reg); +} + +/* Write NFC register */ +static inline void nfc_write(struct mtd_info *mtd, uint reg, u16 val) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + out_be16(prv->regs + reg, val); +} + +/* Set bits in NFC register */ +static inline void nfc_set(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) | bits); +} + +/* Clear bits in NFC register */ +static inline void nfc_clear(struct mtd_info *mtd, uint reg, u16 bits) +{ + nfc_write(mtd, reg, nfc_read(mtd, reg) & ~bits); +} + +/* Invoke address cycle */ +static inline void mpc5121_nfc_send_addr(struct mtd_info *mtd, u16 addr) +{ + nfc_write(mtd, NFC_FLASH_ADDR, addr); + nfc_write(mtd, NFC_CONFIG2, NFC_ADDRESS); + mpc5121_nfc_done(mtd); +} + +/* Invoke command cycle */ +static inline void mpc5121_nfc_send_cmd(struct mtd_info *mtd, u16 cmd) +{ + nfc_write(mtd, NFC_FLASH_CMD, cmd); + nfc_write(mtd, NFC_CONFIG2, NFC_COMMAND); + mpc5121_nfc_done(mtd); +} + +/* Send data from NFC buffers to NAND flash */ +static inline void mpc5121_nfc_send_prog_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_INPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive data from NAND flash */ +static inline void mpc5121_nfc_send_read_page(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_OUTPUT); + mpc5121_nfc_done(mtd); +} + +/* Receive ID from NAND flash */ +static inline void mpc5121_nfc_send_read_id(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_ID); + mpc5121_nfc_done(mtd); +} + +/* Receive status from NAND flash */ +static inline void mpc5121_nfc_send_read_status(struct mtd_info *mtd) +{ + nfc_clear(mtd, NFC_BUF_ADDR, NFC_RBA_MASK); + nfc_write(mtd, NFC_CONFIG2, NFC_STATUS); + mpc5121_nfc_done(mtd); +} + +/* NFC interrupt handler */ +static irqreturn_t mpc5121_nfc_irq(int irq, void *data) +{ + struct mtd_info *mtd = data; + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + nfc_set(mtd, NFC_CONFIG1, NFC_INT_MASK); + wake_up(&prv->irq_waitq); + + return IRQ_HANDLED; +} + +/* Wait for operation complete */ +static void mpc5121_nfc_done(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + int rv; + + if ((nfc_read(mtd, NFC_CONFIG2) & NFC_INT) == 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_INT_MASK); + rv = wait_event_timeout(prv->irq_waitq, + (nfc_read(mtd, NFC_CONFIG2) & NFC_INT), NFC_TIMEOUT); + + if (!rv) + dev_warn(prv->dev, + "Timeout while waiting for interrupt.\n"); + } + + nfc_clear(mtd, NFC_CONFIG2, NFC_INT); +} + +/* Do address cycle(s) */ +static void mpc5121_nfc_addr_cycle(struct mtd_info *mtd, int column, int page) +{ + struct nand_chip *chip = mtd->priv; + u32 pagemask = chip->pagemask; + + if (column != -1) { + mpc5121_nfc_send_addr(mtd, column); + if (mtd->writesize > 512) + mpc5121_nfc_send_addr(mtd, column >> 8); + } + + if (page != -1) { + do { + mpc5121_nfc_send_addr(mtd, page & 0xFF); + page >>= 8; + pagemask >>= 8; + } while (pagemask); + } +} + +/* Control chip select signals */ +static void mpc5121_nfc_select_chip(struct mtd_info *mtd, int chip) +{ + if (chip < 0) { + nfc_clear(mtd, NFC_CONFIG1, NFC_CE); + return; + } + + nfc_clear(mtd, NFC_BUF_ADDR, NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_BUF_ADDR, (chip << NFC_ACTIVE_CS_SHIFT) & + NFC_ACTIVE_CS_MASK); + nfc_set(mtd, NFC_CONFIG1, NFC_CE); +} + +/* Init external chip select logic on ADS5121 board */ +static int ads5121_chipselect_init(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + struct device_node *dn; + + dn = of_find_compatible_node(NULL, NULL, "fsl,mpc5121ads-cpld"); + if (dn) { + prv->csreg = of_iomap(dn, 0); + of_node_put(dn); + if (!prv->csreg) + return -ENOMEM; + + /* CPLD Register 9 controls NAND /CE Lines */ + prv->csreg += 9; + return 0; + } + + return -EINVAL; +} + +/* Control chips select signal on ADS5121 board */ +static void ads5121_select_chip(struct mtd_info *mtd, int chip) +{ + struct nand_chip *nand = mtd->priv; + struct mpc5121_nfc_prv *prv = nand->priv; + u8 v; + + v = in_8(prv->csreg); + v |= 0x0F; + + if (chip >= 0) { + mpc5121_nfc_select_chip(mtd, 0); + v &= ~(1 << chip); + } else + mpc5121_nfc_select_chip(mtd, -1); + + out_8(prv->csreg, v); +} + +/* Read NAND Ready/Busy signal */ +static int mpc5121_nfc_dev_ready(struct mtd_info *mtd) +{ + /* + * NFC handles ready/busy signal internally. Therefore, this function + * always returns status as ready. + */ + return 1; +} + +/* Write command to NAND flash */ +static void mpc5121_nfc_command(struct mtd_info *mtd, unsigned command, + int column, int page) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + prv->column = (column >= 0) ? column : 0; + prv->spareonly = 0; + + switch (command) { + case NAND_CMD_PAGEPROG: + mpc5121_nfc_send_prog_page(mtd); + break; + /* + * NFC does not support sub-page reads and writes, + * so emulate them using full page transfers. + */ + case NAND_CMD_READ0: + column = 0; + break; + + case NAND_CMD_READ1: + prv->column += 256; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_READOOB: + prv->spareonly = 1; + command = NAND_CMD_READ0; + column = 0; + break; + + case NAND_CMD_SEQIN: + mpc5121_nfc_command(mtd, NAND_CMD_READ0, column, page); + column = 0; + break; + + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_READID: + case NAND_CMD_STATUS: + break; + + default: + return; + } + + mpc5121_nfc_send_cmd(mtd, command); + mpc5121_nfc_addr_cycle(mtd, column, page); + + switch (command) { + case NAND_CMD_READ0: + if (mtd->writesize > 512) + mpc5121_nfc_send_cmd(mtd, NAND_CMD_READSTART); + mpc5121_nfc_send_read_page(mtd); + break; + + case NAND_CMD_READID: + mpc5121_nfc_send_read_id(mtd); + break; + + case NAND_CMD_STATUS: + mpc5121_nfc_send_read_status(mtd); + if (chip->options & NAND_BUSWIDTH_16) + prv->column = 1; + else + prv->column = 0; + break; + } +} + +/* Copy data from/to NFC spare buffers. */ +static void mpc5121_nfc_copy_spare(struct mtd_info *mtd, uint offset, + u8 *buffer, uint size, int wr) +{ + struct nand_chip *nand = mtd->priv; + struct mpc5121_nfc_prv *prv = nand->priv; + uint o, s, sbsize, blksize; + + /* + * NAND spare area is available through NFC spare buffers. + * The NFC divides spare area into (page_size / 512) chunks. + * Each chunk is placed into separate spare memory area, using + * first (spare_size / num_of_chunks) bytes of the buffer. + * + * For NAND device in which the spare area is not divided fully + * by the number of chunks, number of used bytes in each spare + * buffer is rounded down to the nearest even number of bytes, + * and all remaining bytes are added to the last used spare area. + * + * For more information read section 26.6.10 of MPC5121e + * Microcontroller Reference Manual, Rev. 3. + */ + + /* Calculate number of valid bytes in each spare buffer */ + sbsize = (mtd->oobsize / (mtd->writesize / 512)) & ~1; + + while (size) { + /* Calculate spare buffer number */ + s = offset / sbsize; + if (s > NFC_SPARE_BUFFERS - 1) + s = NFC_SPARE_BUFFERS - 1; + + /* + * Calculate offset to requested data block in selected spare + * buffer and its size. + */ + o = offset - (s * sbsize); + blksize = min(sbsize - o, size); + + if (wr) + memcpy_toio(prv->regs + NFC_SPARE_AREA(s) + o, + buffer, blksize); + else + memcpy_fromio(buffer, + prv->regs + NFC_SPARE_AREA(s) + o, blksize); + + buffer += blksize; + offset += blksize; + size -= blksize; + }; +} + +/* Copy data from/to NFC main and spare buffers */ +static void mpc5121_nfc_buf_copy(struct mtd_info *mtd, u_char *buf, int len, + int wr) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + uint c = prv->column; + uint l; + + /* Handle spare area access */ + if (prv->spareonly || c >= mtd->writesize) { + /* Calculate offset from beginning of spare area */ + if (c >= mtd->writesize) + c -= mtd->writesize; + + prv->column += len; + mpc5121_nfc_copy_spare(mtd, c, buf, len, wr); + return; + } + + /* + * Handle main area access - limit copy length to prevent + * crossing main/spare boundary. + */ + l = min((uint)len, mtd->writesize - c); + prv->column += l; + + if (wr) + memcpy_toio(prv->regs + NFC_MAIN_AREA(0) + c, buf, l); + else + memcpy_fromio(buf, prv->regs + NFC_MAIN_AREA(0) + c, l); + + /* Handle crossing main/spare boundary */ + if (l != len) { + buf += l; + len -= l; + mpc5121_nfc_buf_copy(mtd, buf, len, wr); + } +} + +/* Read data from NFC buffers */ +static void mpc5121_nfc_read_buf(struct mtd_info *mtd, u_char *buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, buf, len, 0); +} + +/* Write data to NFC buffers */ +static void mpc5121_nfc_write_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + mpc5121_nfc_buf_copy(mtd, (u_char *)buf, len, 1); +} + +/* Compare buffer with NAND flash */ +static int mpc5121_nfc_verify_buf(struct mtd_info *mtd, + const u_char *buf, int len) +{ + u_char tmp[256]; + uint bsize; + + while (len) { + bsize = min(len, 256); + mpc5121_nfc_read_buf(mtd, tmp, bsize); + + if (memcmp(buf, tmp, bsize)) + return 1; + + buf += bsize; + len -= bsize; + } + + return 0; +} + +/* Read byte from NFC buffers */ +static u8 mpc5121_nfc_read_byte(struct mtd_info *mtd) +{ + u8 tmp; + + mpc5121_nfc_read_buf(mtd, &tmp, sizeof(tmp)); + + return tmp; +} + +/* Read word from NFC buffers */ +static u16 mpc5121_nfc_read_word(struct mtd_info *mtd) +{ + u16 tmp; + + mpc5121_nfc_read_buf(mtd, (u_char *)&tmp, sizeof(tmp)); + + return tmp; +} + +/* + * Read NFC configuration from Reset Config Word + * + * NFC is configured during reset in basis of information stored + * in Reset Config Word. There is no other way to set NAND block + * size, spare size and bus width. + */ +static int mpc5121_nfc_read_hw_config(struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + struct mpc512x_reset_module *rm; + struct device_node *rmnode; + uint rcw_pagesize = 0; + uint rcw_sparesize = 0; + uint rcw_width; + uint rcwh; + uint romloc, ps; + + rmnode = of_find_compatible_node(NULL, NULL, "fsl,mpc5121-reset"); + if (!rmnode) { + dev_err(prv->dev, "Missing 'fsl,mpc5121-reset' " + "node in device tree!\n"); + return -ENODEV; + } + + rm = of_iomap(rmnode, 0); + if (!rm) { + dev_err(prv->dev, "Error mapping reset module node!\n"); + return -EBUSY; + } + + rcwh = in_be32(&rm->rcwhr); + + /* Bit 6: NFC bus width */ + rcw_width = ((rcwh >> 6) & 0x1) ? 2 : 1; + + /* Bit 7: NFC Page/Spare size */ + ps = (rcwh >> 7) & 0x1; + + /* Bits [22:21]: ROM Location */ + romloc = (rcwh >> 21) & 0x3; + + /* Decode RCW bits */ + switch ((ps << 2) | romloc) { + case 0x00: + case 0x01: + rcw_pagesize = 512; + rcw_sparesize = 16; + break; + case 0x02: + case 0x03: + rcw_pagesize = 4096; + rcw_sparesize = 128; + break; + case 0x04: + case 0x05: + rcw_pagesize = 2048; + rcw_sparesize = 64; + break; + case 0x06: + case 0x07: + rcw_pagesize = 4096; + rcw_sparesize = 218; + break; + } + + mtd->writesize = rcw_pagesize; + mtd->oobsize = rcw_sparesize; + if (rcw_width == 2) + chip->options |= NAND_BUSWIDTH_16; + + dev_notice(prv->dev, "Configured for " + "%u-bit NAND, page size %u " + "with %u spare.\n", + rcw_width * 8, rcw_pagesize, + rcw_sparesize); + iounmap(rm); + of_node_put(rmnode); + return 0; +} + +/* Free driver resources */ +static void mpc5121_nfc_free(struct device *dev, struct mtd_info *mtd) +{ + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + if (prv->clk) { + clk_disable(prv->clk); + clk_put(prv->clk); + } + + if (prv->csreg) + iounmap(prv->csreg); +} + +static int __devinit mpc5121_nfc_probe(struct of_device *op, + const struct of_device_id *match) +{ + struct device_node *rootnode, *dn = op->node; + struct device *dev = &op->dev; + struct mpc5121_nfc_prv *prv; + struct resource res; + struct mtd_info *mtd; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif + struct nand_chip *chip; + unsigned long regs_paddr, regs_size; + const uint *chips_no; + int resettime = 0; + int retval = 0; + int rev, len; + + /* + * Check SoC revision. This driver supports only NFC + * in MPC5121 revision 2. + */ + rev = (mfspr(SPRN_SVR) >> 4) & 0xF; + if (rev != 2) { + dev_err(dev, "SoC revision %u is not supported!\n", rev); + return -ENXIO; + } + + prv = devm_kzalloc(dev, sizeof(*prv), GFP_KERNEL); + if (!prv) { + dev_err(dev, "Memory exhausted!\n"); + return -ENOMEM; + } + + mtd = &prv->mtd; + chip = &prv->chip; + + mtd->priv = chip; + chip->priv = prv; + prv->dev = dev; + + /* Read NFC configuration from Reset Config Word */ + retval = mpc5121_nfc_read_hw_config(mtd); + if (retval) { + dev_err(dev, "Unable to read NFC config!\n"); + return retval; + } + + prv->irq = irq_of_parse_and_map(dn, 0); + if (prv->irq == NO_IRQ) { + dev_err(dev, "Error mapping IRQ!\n"); + return -EINVAL; + } + + retval = of_address_to_resource(dn, 0, &res); + if (retval) { + dev_err(dev, "Error parsing memory region!\n"); + return retval; + } + + chips_no = of_get_property(dn, "chips", &len); + if (!chips_no || len != sizeof(*chips_no)) { + dev_err(dev, "Invalid/missing 'chips' property!\n"); + return -EINVAL; + } + + regs_paddr = res.start; + regs_size = res.end - res.start + 1; + + if (!devm_request_mem_region(dev, regs_paddr, regs_size, DRV_NAME)) { + dev_err(dev, "Error requesting memory region!\n"); + return -EBUSY; + } + + prv->regs = devm_ioremap(dev, regs_paddr, regs_size); + if (!prv->regs) { + dev_err(dev, "Error mapping memory region!\n"); + return -ENOMEM; + } + + mtd->name = "MPC5121 NAND"; + chip->dev_ready = mpc5121_nfc_dev_ready; + chip->cmdfunc = mpc5121_nfc_command; + chip->read_byte = mpc5121_nfc_read_byte; + chip->read_word = mpc5121_nfc_read_word; + chip->read_buf = mpc5121_nfc_read_buf; + chip->write_buf = mpc5121_nfc_write_buf; + chip->verify_buf = mpc5121_nfc_verify_buf; + chip->select_chip = mpc5121_nfc_select_chip; + chip->options = NAND_NO_AUTOINCR | NAND_USE_FLASH_BBT; + chip->ecc.mode = NAND_ECC_SOFT; + + /* Support external chip-select logic on ADS5121 board */ + rootnode = of_find_node_by_path("/"); + if (of_device_is_compatible(rootnode, "fsl,mpc5121ads")) { + retval = ads5121_chipselect_init(mtd); + if (retval) { + dev_err(dev, "Chipselect init error!\n"); + of_node_put(rootnode); + return retval; + } + + chip->select_chip = ads5121_select_chip; + } + of_node_put(rootnode); + + /* Enable NFC clock */ + prv->clk = clk_get(dev, "nfc_clk"); + if (!prv->clk) { + dev_err(dev, "Unable to acquire NFC clock!\n"); + retval = -ENODEV; + goto error; + } + + clk_enable(prv->clk); + + /* Reset NAND Flash controller */ + nfc_set(mtd, NFC_CONFIG1, NFC_RESET); + while (nfc_read(mtd, NFC_CONFIG1) & NFC_RESET) { + if (resettime++ >= NFC_RESET_TIMEOUT) { + dev_err(dev, "Timeout while resetting NFC!\n"); + retval = -EINVAL; + goto error; + } + + udelay(1); + } + + /* Enable write to NFC memory */ + nfc_write(mtd, NFC_CONFIG, NFC_BLS_UNLOCKED); + + /* Enable write to all NAND pages */ + nfc_write(mtd, NFC_UNLOCKSTART_BLK0, 0x0000); + nfc_write(mtd, NFC_UNLOCKEND_BLK0, 0xFFFF); + nfc_write(mtd, NFC_WRPROT, NFC_WPC_UNLOCK); + + /* + * Setup NFC: + * - Big Endian transfers, + * - Interrupt after full page read/write. + */ + nfc_write(mtd, NFC_CONFIG1, NFC_BIG_ENDIAN | NFC_INT_MASK | + NFC_FULL_PAGE_INT); + + /* Set spare area size */ + nfc_write(mtd, NFC_SPAS, mtd->oobsize >> 1); + + init_waitqueue_head(&prv->irq_waitq); + retval = devm_request_irq(dev, prv->irq, &mpc5121_nfc_irq, 0, DRV_NAME, + mtd); + if (retval) { + dev_err(dev, "Error requesting IRQ!\n"); + goto error; + } + + /* Detect NAND chips */ + if (nand_scan(mtd, *chips_no)) { + dev_err(dev, "NAND Flash not found !\n"); + devm_free_irq(dev, prv->irq, mtd); + retval = -ENXIO; + goto error; + } + + /* Set erase block size */ + switch (mtd->erasesize / mtd->writesize) { + case 32: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_32); + break; + + case 64: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_64); + break; + + case 128: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_128); + break; + + case 256: + nfc_set(mtd, NFC_CONFIG1, NFC_PPB_256); + break; + + default: + dev_err(dev, "Unsupported NAND flash!\n"); + devm_free_irq(dev, prv->irq, mtd); + retval = -ENXIO; + goto error; + } + + dev_set_drvdata(dev, mtd); + + /* Register device in MTD */ +#ifdef CONFIG_MTD_PARTITIONS + retval = parse_mtd_partitions(mtd, mpc5121_nfc_pprobes, &parts, 0); +#ifdef CONFIG_MTD_OF_PARTS + if (retval == 0) + retval = of_mtd_parse_partitions(dev, dn, &parts); +#endif + if (retval < 0) { + dev_err(dev, "Error parsing MTD partitions!\n"); + devm_free_irq(dev, prv->irq, mtd); + retval = -EINVAL; + goto error; + } + + if (retval > 0) + retval = add_mtd_partitions(mtd, parts, retval); + else +#endif + retval = add_mtd_device(mtd); + + if (retval) { + dev_err(dev, "Error adding MTD device!\n"); + devm_free_irq(dev, prv->irq, mtd); + goto error; + } + + return 0; +error: + mpc5121_nfc_free(dev, mtd); + return retval; +} + +static int __devexit mpc5121_nfc_remove(struct of_device *op) +{ + struct device *dev = &op->dev; + struct mtd_info *mtd = dev_get_drvdata(dev); + struct nand_chip *chip = mtd->priv; + struct mpc5121_nfc_prv *prv = chip->priv; + + nand_release(mtd); + devm_free_irq(dev, prv->irq, mtd); + mpc5121_nfc_free(dev, mtd); + + return 0; +} + +static struct of_device_id mpc5121_nfc_match[] __devinitdata = { + { .compatible = "fsl,mpc5121-nfc", }, + {}, +}; + +static struct of_platform_driver mpc5121_nfc_driver = { + .match_table = mpc5121_nfc_match, + .probe = mpc5121_nfc_probe, + .remove = __devexit_p(mpc5121_nfc_remove), + .driver = { + .name = DRV_NAME, + .owner = THIS_MODULE, + }, +}; + +static int __init mpc5121_nfc_init(void) +{ + return of_register_platform_driver(&mpc5121_nfc_driver); +} + +module_init(mpc5121_nfc_init); + +static void __exit mpc5121_nfc_cleanup(void) +{ + of_unregister_platform_driver(&mpc5121_nfc_driver); +} + +module_exit(mpc5121_nfc_cleanup); + +MODULE_AUTHOR("Freescale Semiconductor, Inc."); +MODULE_DESCRIPTION("MPC5121 NAND MTD driver"); +MODULE_LICENSE("GPL"); -- cgit v1.2.3-70-g09d2 From 3bd456576f22acd55fb6c3d3d4261131821f5a3b Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:28 +0200 Subject: mtd: create unlocked versions of {get,put}_mtd_device Use these only if you know that you already hold mtd_table_mutex Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/mtdcore.c | 60 ++++++++++++++++++++++++++++++++----------------- include/linux/mtd/mtd.h | 3 ++- 2 files changed, 41 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index b3b98d1fffc..67669a76eaf 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -463,27 +463,38 @@ struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) ret = NULL; } - if (!ret) - goto out_unlock; - - if (!try_module_get(ret->owner)) - goto out_unlock; - - if (ret->get_device) { - err = ret->get_device(ret); - if (err) - goto out_put; + if (!ret) { + ret = ERR_PTR(err); + goto out; } - ret->usecount++; + err = __get_mtd_device(ret); + if (err) + ret = ERR_PTR(err); +out: mutex_unlock(&mtd_table_mutex); return ret; +} -out_put: - module_put(ret->owner); -out_unlock: - mutex_unlock(&mtd_table_mutex); - return ERR_PTR(err); + +int __get_mtd_device(struct mtd_info *mtd) +{ + int err; + + if (!try_module_get(mtd->owner)) + return -ENODEV; + + if (mtd->get_device) { + + err = mtd->get_device(mtd); + + if (err) { + module_put(mtd->owner); + return err; + } + } + mtd->usecount++; + return 0; } /** @@ -534,14 +545,19 @@ out_unlock: void put_mtd_device(struct mtd_info *mtd) { - int c; - mutex_lock(&mtd_table_mutex); - c = --mtd->usecount; + __put_mtd_device(mtd); + mutex_unlock(&mtd_table_mutex); + +} + +void __put_mtd_device(struct mtd_info *mtd) +{ + --mtd->usecount; + BUG_ON(mtd->usecount < 0); + if (mtd->put_device) mtd->put_device(mtd); - mutex_unlock(&mtd_table_mutex); - BUG_ON(c < 0); module_put(mtd->owner); } @@ -579,7 +595,9 @@ EXPORT_SYMBOL_GPL(add_mtd_device); EXPORT_SYMBOL_GPL(del_mtd_device); EXPORT_SYMBOL_GPL(get_mtd_device); EXPORT_SYMBOL_GPL(get_mtd_device_nm); +EXPORT_SYMBOL_GPL(__get_mtd_device); EXPORT_SYMBOL_GPL(put_mtd_device); +EXPORT_SYMBOL_GPL(__put_mtd_device); EXPORT_SYMBOL_GPL(register_mtd_user); EXPORT_SYMBOL_GPL(unregister_mtd_user); EXPORT_SYMBOL_GPL(default_mtd_writev); diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index ba53ecca107..11d8e68d17c 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -289,8 +289,9 @@ extern int add_mtd_device(struct mtd_info *mtd); extern int del_mtd_device (struct mtd_info *mtd); extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); +extern int __get_mtd_device(struct mtd_info *mtd); +extern void __put_mtd_device(struct mtd_info *mtd); extern struct mtd_info *get_mtd_device_nm(const char *name); - extern void put_mtd_device(struct mtd_info *mtd); -- cgit v1.2.3-70-g09d2 From a863862257b7dd08d855bafcb0aedd9ad848ed91 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:29 +0200 Subject: mtd: blktrans: remove mtd_blkcore_priv, switch to per device queue and thread This is the biggest change. To make hotplug possible, and this layer clean, the mtd_blktrans_dev now contains everything for a single mtd block translation device. Also removed some very old leftovers. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 124 ++++++++++++++++++++----------------------- include/linux/mtd/blktrans.h | 10 ++-- 2 files changed, 63 insertions(+), 71 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 2f8c202dbd8..6a572625bfc 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include @@ -26,11 +25,6 @@ static LIST_HEAD(blktrans_majors); -struct mtd_blkcore_priv { - struct task_struct *thread; - struct request_queue *rq; - spinlock_t queue_lock; -}; static int do_blktrans_request(struct mtd_blktrans_ops *tr, struct mtd_blktrans_dev *dev, @@ -61,7 +55,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, return -EIO; rq_flush_dcache_pages(req); return 0; - case WRITE: if (!tr->writesect) return -EIO; @@ -71,7 +64,6 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, if (tr->writesect(dev, block, buf)) return -EIO; return 0; - default: printk(KERN_NOTICE "Unknown request %u\n", rq_data_dir(req)); return -EIO; @@ -80,14 +72,13 @@ static int do_blktrans_request(struct mtd_blktrans_ops *tr, static int mtd_blktrans_thread(void *arg) { - struct mtd_blktrans_ops *tr = arg; - struct request_queue *rq = tr->blkcore_priv->rq; + struct mtd_blktrans_dev *dev = arg; + struct request_queue *rq = dev->rq; struct request *req = NULL; spin_lock_irq(rq->queue_lock); while (!kthread_should_stop()) { - struct mtd_blktrans_dev *dev; int res; if (!req && !(req = blk_fetch_request(rq))) { @@ -98,13 +89,10 @@ static int mtd_blktrans_thread(void *arg) continue; } - dev = req->rq_disk->private_data; - tr = dev->tr; - spin_unlock_irq(rq->queue_lock); mutex_lock(&dev->lock); - res = do_blktrans_request(tr, dev, req); + res = do_blktrans_request(dev->tr, dev, req); mutex_unlock(&dev->lock); spin_lock_irq(rq->queue_lock); @@ -123,8 +111,8 @@ static int mtd_blktrans_thread(void *arg) static void mtd_blktrans_request(struct request_queue *rq) { - struct mtd_blktrans_ops *tr = rq->queuedata; - wake_up_process(tr->blkcore_priv->thread); + struct mtd_blktrans_dev *dev = rq->queuedata; + wake_up_process(dev->thread); } @@ -214,6 +202,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) struct mtd_blktrans_dev *d; int last_devnum = -1; struct gendisk *gd; + int ret; if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); @@ -239,6 +228,8 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) } last_devnum = d->devnum; } + + ret = -EBUSY; if (new->devnum == -1) new->devnum = last_devnum+1; @@ -247,7 +238,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) * with this number. */ if (new->devnum > (MINORMASK >> tr->part_bits) || (tr->part_bits && new->devnum >= 27 * 26)) - return -EBUSY; + goto error1; list_add_tail(&new->list, &tr->devs); added: @@ -255,11 +246,16 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) if (!tr->writesect) new->readonly = 1; + + /* Create gendisk */ + ret = -ENOMEM; gd = alloc_disk(1 << tr->part_bits); - if (!gd) { - list_del(&new->list); - return -ENOMEM; - } + + if (!gd) + goto error2; + + new->disk = gd; + gd->private_data = new; gd->major = tr->major; gd->first_minor = (new->devnum) << tr->part_bits; gd->fops = &mtd_blktrans_ops; @@ -277,21 +273,49 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) snprintf(gd->disk_name, sizeof(gd->disk_name), "%s%d", tr->name, new->devnum); - /* 2.5 has capacity in units of 512 bytes while still - having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ set_capacity(gd, (new->size * tr->blksize) >> 9); - gd->private_data = new; - new->blkcore_priv = gd; - gd->queue = tr->blkcore_priv->rq; + + /* Create the request queue */ + spin_lock_init(&new->queue_lock); + new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock); + + if (!new->rq) + goto error3; + + new->rq->queuedata = new; + blk_queue_logical_block_size(new->rq, tr->blksize); + + if (tr->discard) + queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, + new->rq); + + gd->queue = new->rq; + + /* Create processing thread */ + /* TODO: workqueue ? */ + new->thread = kthread_run(mtd_blktrans_thread, new, + "%s%d", tr->name, new->mtd->index); + if (IS_ERR(new->thread)) { + ret = PTR_ERR(new->thread); + goto error4; + } gd->driverfs_dev = &new->mtd->dev; if (new->readonly) set_disk_ro(gd, 1); add_disk(gd); - return 0; +error4: + blk_cleanup_queue(new->rq); +error3: + put_disk(new->disk); +error2: + list_del(&new->list); +error1: + kfree(new); + return ret; } int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) @@ -303,9 +327,13 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) list_del(&old->list); - del_gendisk(old->blkcore_priv); - put_disk(old->blkcore_priv); + /* stop new requests to arrive */ + del_gendisk(old->disk); + /* Stop the thread */ + kthread_stop(old->thread); + + blk_cleanup_queue(old->rq); return 0; } @@ -347,9 +375,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) if (!blktrans_notifier.list.next) register_mtd_user(&blktrans_notifier); - tr->blkcore_priv = kzalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); - if (!tr->blkcore_priv) - return -ENOMEM; mutex_lock(&mtd_table_mutex); @@ -357,39 +382,12 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) if (ret) { printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", tr->name, tr->major, ret); - kfree(tr->blkcore_priv); mutex_unlock(&mtd_table_mutex); return ret; } - spin_lock_init(&tr->blkcore_priv->queue_lock); - - tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); - if (!tr->blkcore_priv->rq) { - unregister_blkdev(tr->major, tr->name); - kfree(tr->blkcore_priv); - mutex_unlock(&mtd_table_mutex); - return -ENOMEM; - } - - tr->blkcore_priv->rq->queuedata = tr; - blk_queue_logical_block_size(tr->blkcore_priv->rq, tr->blksize); - if (tr->discard) - queue_flag_set_unlocked(QUEUE_FLAG_DISCARD, - tr->blkcore_priv->rq); tr->blkshift = ffs(tr->blksize) - 1; - tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr, - "%sd", tr->name); - if (IS_ERR(tr->blkcore_priv->thread)) { - ret = PTR_ERR(tr->blkcore_priv->thread); - blk_cleanup_queue(tr->blkcore_priv->rq); - unregister_blkdev(tr->major, tr->name); - kfree(tr->blkcore_priv); - mutex_unlock(&mtd_table_mutex); - return ret; - } - INIT_LIST_HEAD(&tr->devs); list_add(&tr->list, &blktrans_majors); @@ -408,8 +406,6 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) mutex_lock(&mtd_table_mutex); - /* Clean up the kernel thread */ - kthread_stop(tr->blkcore_priv->thread); /* Remove it from the list of active majors */ list_del(&tr->list); @@ -417,13 +413,9 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) list_for_each_entry_safe(dev, next, &tr->devs, list) tr->remove_dev(dev); - blk_cleanup_queue(tr->blkcore_priv->rq); unregister_blkdev(tr->major, tr->name); - mutex_unlock(&mtd_table_mutex); - kfree(tr->blkcore_priv); - BUG_ON(!list_empty(&tr->devs)); return 0; } diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h index 8b4aa0523db..a4b392868b5 100644 --- a/include/linux/mtd/blktrans.h +++ b/include/linux/mtd/blktrans.h @@ -24,11 +24,13 @@ struct mtd_blktrans_dev { int devnum; unsigned long size; int readonly; - void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */ + struct gendisk *disk; + struct task_struct *thread; + struct request_queue *rq; + spinlock_t queue_lock; + void *priv; }; -struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */ - struct mtd_blktrans_ops { char *name; int major; @@ -60,8 +62,6 @@ struct mtd_blktrans_ops { struct list_head devs; struct list_head list; struct module *owner; - - struct mtd_blkcore_priv *blkcore_priv; }; extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr); -- cgit v1.2.3-70-g09d2 From 048d87199566663e4edc4880df3703c04bcf41d9 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:30 +0200 Subject: mtd: blktrans: Hotplug fixes * Add locking where it was missing. * Don't do a get_mtd_device in blktrans_open because it would lead to a deadlock; instead do that in add_mtd_blktrans_dev. * Only free the mtd_blktrans_dev structure when the last user exits. * Flush request queue on device removal. * Track users, and call tr->release in del_mtd_blktrans_dev Due to that ->open and release aren't called more that once. Now it is safe to call del_mtd_blktrans_dev while the device is still in use. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/ftl.c | 1 - drivers/mtd/inftlcore.c | 1 - drivers/mtd/mtd_blkdevs.c | 202 +++++++++++++++++++++++++++++++------------ drivers/mtd/mtdblock.c | 2 - drivers/mtd/mtdblock_ro.c | 1 - drivers/mtd/nftlcore.c | 1 - drivers/mtd/rfd_ftl.c | 1 - drivers/mtd/ssfdc.c | 1 - include/linux/mtd/blktrans.h | 3 + 9 files changed, 151 insertions(+), 62 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index e56d6b42f02..62da9eb7032 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1082,7 +1082,6 @@ static void ftl_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); ftl_freepart((partition_t *)dev); - kfree(dev); } static struct mtd_blktrans_ops ftl_tr = { diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c index 8aca5523a33..015a7fe1b6e 100755 --- a/drivers/mtd/inftlcore.c +++ b/drivers/mtd/inftlcore.c @@ -139,7 +139,6 @@ static void inftl_remove_dev(struct mtd_blktrans_dev *dev) kfree(inftl->PUtable); kfree(inftl->VUtable); - kfree(inftl); } /* diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 6a572625bfc..646cc84ae69 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -24,6 +24,40 @@ #include "mtdcore.h" static LIST_HEAD(blktrans_majors); +static DEFINE_MUTEX(blktrans_ref_mutex); + +void blktrans_dev_release(struct kref *kref) +{ + struct mtd_blktrans_dev *dev = + container_of(kref, struct mtd_blktrans_dev, ref); + + dev->disk->private_data = NULL; + put_disk(dev->disk); + list_del(&dev->list); + kfree(dev); +} + +static struct mtd_blktrans_dev *blktrans_dev_get(struct gendisk *disk) +{ + struct mtd_blktrans_dev *dev; + + mutex_lock(&blktrans_ref_mutex); + dev = disk->private_data; + + if (!dev) + goto unlock; + kref_get(&dev->ref); +unlock: + mutex_unlock(&blktrans_ref_mutex); + return dev; +} + +void blktrans_dev_put(struct mtd_blktrans_dev *dev) +{ + mutex_lock(&blktrans_ref_mutex); + kref_put(&dev->ref, blktrans_dev_release); + mutex_unlock(&blktrans_ref_mutex); +} static int do_blktrans_request(struct mtd_blktrans_ops *tr, @@ -111,81 +145,112 @@ static int mtd_blktrans_thread(void *arg) static void mtd_blktrans_request(struct request_queue *rq) { - struct mtd_blktrans_dev *dev = rq->queuedata; - wake_up_process(dev->thread); -} + struct mtd_blktrans_dev *dev; + struct request *req = NULL; + + dev = rq->queuedata; + if (!dev) + while ((req = blk_fetch_request(rq)) != NULL) + __blk_end_request_all(req, -ENODEV); + else + wake_up_process(dev->thread); +} static int blktrans_open(struct block_device *bdev, fmode_t mode) { - struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; - struct mtd_blktrans_ops *tr = dev->tr; - int ret = -ENODEV; - - if (!get_mtd_device(NULL, dev->mtd->index)) - goto out; - - if (!try_module_get(tr->owner)) - goto out_tr; - - /* FIXME: Locking. A hot pluggable device can go away - (del_mtd_device can be called for it) without its module - being unloaded. */ - dev->mtd->usecount++; - - ret = 0; - if (tr->open && (ret = tr->open(dev))) { - dev->mtd->usecount--; - put_mtd_device(dev->mtd); - out_tr: - module_put(tr->owner); + struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); + int ret; + + if (!dev) + return -ERESTARTSYS; + + mutex_lock(&dev->lock); + + if (!dev->mtd) { + ret = -ENXIO; + goto unlock; } - out: + + ret = !dev->open++ && dev->tr->open ? dev->tr->open(dev) : 0; + + /* Take another reference on the device so it won't go away till + last release */ + if (!ret) + kref_get(&dev->ref); +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); return ret; } static int blktrans_release(struct gendisk *disk, fmode_t mode) { - struct mtd_blktrans_dev *dev = disk->private_data; - struct mtd_blktrans_ops *tr = dev->tr; - int ret = 0; + struct mtd_blktrans_dev *dev = blktrans_dev_get(disk); + int ret = -ENXIO; - if (tr->release) - ret = tr->release(dev); + if (!dev) + return ret; - if (!ret) { - dev->mtd->usecount--; - put_mtd_device(dev->mtd); - module_put(tr->owner); - } + mutex_lock(&dev->lock); + + /* Release one reference, we sure its not the last one here*/ + kref_put(&dev->ref, blktrans_dev_release); + if (!dev->mtd) + goto unlock; + + ret = !--dev->open && dev->tr->release ? dev->tr->release(dev) : 0; +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); return ret; } static int blktrans_getgeo(struct block_device *bdev, struct hd_geometry *geo) { - struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; + struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); + int ret = -ENXIO; + + if (!dev) + return ret; + + mutex_lock(&dev->lock); - if (dev->tr->getgeo) - return dev->tr->getgeo(dev, geo); - return -ENOTTY; + if (!dev->mtd) + goto unlock; + + ret = dev->tr->getgeo ? dev->tr->getgeo(dev, geo) : 0; +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); + return ret; } static int blktrans_ioctl(struct block_device *bdev, fmode_t mode, unsigned int cmd, unsigned long arg) { - struct mtd_blktrans_dev *dev = bdev->bd_disk->private_data; - struct mtd_blktrans_ops *tr = dev->tr; + struct mtd_blktrans_dev *dev = blktrans_dev_get(bdev->bd_disk); + int ret = -ENXIO; + + if (!dev) + return ret; + + mutex_lock(&dev->lock); + + if (!dev->mtd) + goto unlock; switch (cmd) { case BLKFLSBUF: - if (tr->flush) - return tr->flush(dev); - /* The core code did the work, we had nothing to do. */ - return 0; + ret = dev->tr->flush ? dev->tr->flush(dev) : 0; default: - return -ENOTTY; + ret = -ENOTTY; } +unlock: + mutex_unlock(&dev->lock); + blktrans_dev_put(dev); + return ret; } static const struct block_device_operations mtd_blktrans_ops = { @@ -209,6 +274,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) BUG(); } + mutex_lock(&blktrans_ref_mutex); list_for_each_entry(d, &tr->devs, list) { if (new->devnum == -1) { /* Use first free number */ @@ -220,6 +286,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) } } else if (d->devnum == new->devnum) { /* Required number taken */ + mutex_unlock(&blktrans_ref_mutex); return -EBUSY; } else if (d->devnum > new->devnum) { /* Required number was free */ @@ -237,16 +304,20 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) * minor numbers and that the disk naming code below can cope * with this number. */ if (new->devnum > (MINORMASK >> tr->part_bits) || - (tr->part_bits && new->devnum >= 27 * 26)) + (tr->part_bits && new->devnum >= 27 * 26)) { + mutex_unlock(&blktrans_ref_mutex); goto error1; + } list_add_tail(&new->list, &tr->devs); added: + mutex_unlock(&blktrans_ref_mutex); + mutex_init(&new->lock); + kref_init(&new->ref); if (!tr->writesect) new->readonly = 1; - /* Create gendisk */ ret = -ENOMEM; gd = alloc_disk(1 << tr->part_bits); @@ -275,7 +346,6 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) set_capacity(gd, (new->size * tr->blksize) >> 9); - /* Create the request queue */ spin_lock_init(&new->queue_lock); new->rq = blk_init_queue(mtd_blktrans_request, &new->queue_lock); @@ -292,6 +362,9 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) gd->queue = new->rq; + __get_mtd_device(new->mtd); + __module_get(tr->owner); + /* Create processing thread */ /* TODO: workqueue ? */ new->thread = kthread_run(mtd_blktrans_thread, new, @@ -308,6 +381,8 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) add_disk(gd); return 0; error4: + module_put(tr->owner); + __put_mtd_device(new->mtd); blk_cleanup_queue(new->rq); error3: put_disk(new->disk); @@ -320,20 +395,41 @@ error1: int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) { + unsigned long flags; + if (mutex_trylock(&mtd_table_mutex)) { mutex_unlock(&mtd_table_mutex); BUG(); } - list_del(&old->list); - - /* stop new requests to arrive */ + /* Stop new requests to arrive */ del_gendisk(old->disk); /* Stop the thread */ kthread_stop(old->thread); + /* Kill current requests */ + spin_lock_irqsave(&old->queue_lock, flags); + old->rq->queuedata = NULL; + blk_start_queue(old->rq); + spin_unlock_irqrestore(&old->queue_lock, flags); blk_cleanup_queue(old->rq); + + /* Ask trans driver for release to the mtd device */ + mutex_lock(&old->lock); + if (old->open && old->tr->release) { + old->tr->release(old); + old->open = 0; + } + + __put_mtd_device(old->mtd); + module_put(old->tr->owner); + + /* At that point, we don't touch the mtd anymore */ + old->mtd = NULL; + + mutex_unlock(&old->lock); + blktrans_dev_put(old); return 0; } @@ -396,7 +492,6 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr) tr->add_mtd(tr, mtd); mutex_unlock(&mtd_table_mutex); - return 0; } @@ -406,7 +501,6 @@ int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) mutex_lock(&mtd_table_mutex); - /* Remove it from the list of active majors */ list_del(&tr->list); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 69f6bf2e0a8..8e5da1e4607 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -354,9 +354,7 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); - del_mtd_blktrans_dev(dev); - kfree(mtdblk); } static struct mtd_blktrans_ops mtdblock_tr = { diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 852165f8b1c..54ff2880cf6 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -49,7 +49,6 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { del_mtd_blktrans_dev(dev); - kfree(dev); } static struct mtd_blktrans_ops mtdblock_tr = { diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 1002e188299..a4578bf903a 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -126,7 +126,6 @@ static void nftl_remove_dev(struct mtd_blktrans_dev *dev) del_mtd_blktrans_dev(dev); kfree(nftl->ReplUnitTable); kfree(nftl->EUNtable); - kfree(nftl); } /* diff --git a/drivers/mtd/rfd_ftl.c b/drivers/mtd/rfd_ftl.c index d2aa9c46530..63b83c0d9a1 100644 --- a/drivers/mtd/rfd_ftl.c +++ b/drivers/mtd/rfd_ftl.c @@ -817,7 +817,6 @@ static void rfd_ftl_remove_dev(struct mtd_blktrans_dev *dev) vfree(part->sector_map); kfree(part->header_cache); kfree(part->blocks); - kfree(part); } static struct mtd_blktrans_ops rfd_ftl_tr = { diff --git a/drivers/mtd/ssfdc.c b/drivers/mtd/ssfdc.c index 3f67e00d98e..81c4ecdc11f 100644 --- a/drivers/mtd/ssfdc.c +++ b/drivers/mtd/ssfdc.c @@ -375,7 +375,6 @@ static void ssfdcr_remove_dev(struct mtd_blktrans_dev *dev) del_mtd_blktrans_dev(dev); kfree(ssfdc->logic_block_map); - kfree(ssfdc); } static int ssfdcr_readsect(struct mtd_blktrans_dev *dev, diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h index a4b392868b5..d89b8fbba4c 100644 --- a/include/linux/mtd/blktrans.h +++ b/include/linux/mtd/blktrans.h @@ -9,6 +9,7 @@ #define __MTD_TRANS_H__ #include +#include struct hd_geometry; struct mtd_info; @@ -24,6 +25,8 @@ struct mtd_blktrans_dev { int devnum; unsigned long size; int readonly; + int open; + struct kref ref; struct gendisk *disk; struct task_struct *thread; struct request_queue *rq; -- cgit v1.2.3-70-g09d2 From 298304f1a554d44cf13391e531ced3cde69a8ce4 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:31 +0200 Subject: mtd: mtdblock: test return value of add_mtd_blktrans_dev, because if can fail This prevents a memory leak Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/mtdblock.c | 3 ++- drivers/mtd/mtdblock_ro.c | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 8e5da1e4607..7ce30a239ad 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -348,7 +348,8 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) if (!(mtd->flags & MTD_WRITEABLE)) dev->mbd.readonly = 1; - add_mtd_blktrans_dev(&dev->mbd); + if (add_mtd_blktrans_dev(&dev->mbd)) + kfree(dev); } static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 54ff2880cf6..d0d3f79f9d0 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -43,7 +43,8 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) dev->tr = tr; dev->readonly = 1; - add_mtd_blktrans_dev(dev); + if (add_mtd_blktrans_dev(dev)) + kfree(dev); } static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) -- cgit v1.2.3-70-g09d2 From 75c0b84d41c6f08c0cb083464907005683ef2920 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:32 +0200 Subject: mtd: call the remove notifiers before assuming it is in use Now that mtd block common layer is prepared for proper hotplug support, enable it here Now all users of the mtd device have a chance to put the mtd device when they are notified to do so, and they have to do so to make hotplug work. [dwmw2: There's more work to be done to fix hotplug in the general case, but this is a reasonable start] Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/mtdcore.c | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 67669a76eaf..70a78587c3c 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -350,31 +350,34 @@ fail_locked: int del_mtd_device (struct mtd_info *mtd) { int ret; + struct mtd_notifier *not; mutex_lock(&mtd_table_mutex); if (idr_find(&mtd_idr, mtd->index) != mtd) { ret = -ENODEV; - } else if (mtd->usecount) { + goto out_error; + } + + /* No need to get a refcount on the module containing + the notifier, since we hold the mtd_table_mutex */ + list_for_each_entry(not, &mtd_notifiers, list) + not->remove(mtd); + + if (mtd->usecount) { printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", mtd->index, mtd->name, mtd->usecount); ret = -EBUSY; } else { - struct mtd_notifier *not; - device_unregister(&mtd->dev); - /* No need to get a refcount on the module containing - the notifier, since we hold the mtd_table_mutex */ - list_for_each_entry(not, &mtd_notifiers, list) - not->remove(mtd); - idr_remove(&mtd_idr, mtd->index); module_put(THIS_MODULE); ret = 0; } +out_error: mutex_unlock(&mtd_table_mutex); return ret; } -- cgit v1.2.3-70-g09d2 From 026ec57886b67c092bf7baecd029a7c1c4998c28 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:33 +0200 Subject: mtd: blktrans: allow FTL drivers to export sysfs attributes This patch adds an ability to export sysfs attributes below the block disk device. This can be used to pass the udev an information about the FTL and could include the vendor, serial, version, etc... Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 8 ++++++++ include/linux/mtd/blktrans.h | 2 ++ 2 files changed, 10 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 646cc84ae69..9dd23d6acbb 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -379,6 +379,10 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) set_disk_ro(gd, 1); add_disk(gd); + + if (new->disk_attributes) + sysfs_create_group(&disk_to_dev(gd)->kobj, + new->disk_attributes); return 0; error4: module_put(tr->owner); @@ -405,6 +409,10 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) /* Stop new requests to arrive */ del_gendisk(old->disk); + if (old->disk_attributes) + sysfs_remove_group(&disk_to_dev(old->disk)->kobj, + old->disk_attributes); + /* Stop the thread */ kthread_stop(old->thread); diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h index d89b8fbba4c..b481ccd7ff3 100644 --- a/include/linux/mtd/blktrans.h +++ b/include/linux/mtd/blktrans.h @@ -10,6 +10,7 @@ #include #include +#include struct hd_geometry; struct mtd_info; @@ -28,6 +29,7 @@ struct mtd_blktrans_dev { int open; struct kref ref; struct gendisk *disk; + struct attribute_group *disk_attributes; struct task_struct *thread; struct request_queue *rq; spinlock_t queue_lock; -- cgit v1.2.3-70-g09d2 From 49ef3c6ee11e221b26caf4ac55c2702a37cca103 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:34 +0200 Subject: mtd: nand: make suspend work if device is accessed by kernel threads. Since all userspace threads are frozen at the time the nand_suspend is called, they aren't inside any nand function. We don't call try_to_freeze in nand ether. Thus the only user that can be inside the nand functions is an non freezeable kernel thread. Thus we can safely wait for it to finish. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 3 --- 1 file changed, 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ed62e1ee0f8..7442b3a29b2 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -773,9 +773,6 @@ nand_get_device(struct nand_chip *chip, struct mtd_info *mtd, int new_state) chip->state = FL_PM_SUSPENDED; spin_unlock(lock); return 0; - } else { - spin_unlock(lock); - return -EAGAIN; } } set_current_state(TASK_UNINTERRUPTIBLE); -- cgit v1.2.3-70-g09d2 From 9aca334e854c319ccafea871006fda3814196e7b Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:35 +0200 Subject: mtd: nand: make MTD_OOB_PLACE work correctly. MTD_OOB_PLACE is supposed to read/write the raw oob data similiar to the MTD_OOB_RAW however due to a bug, currently it is not possible to read more data that is specified by the oob 'free' regions. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 7442b3a29b2..cada4cffacf 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1420,6 +1420,9 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, int ret = 0; uint32_t readlen = ops->len; uint32_t oobreadlen = ops->ooblen; + uint32_t max_oobsize = ops->mode == MTD_OOB_AUTO ? + mtd->oobavail : mtd->oobsize; + uint8_t *bufpoi, *oob, *buf; stats = mtd->ecc_stats; @@ -1470,10 +1473,11 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, buf += bytes; if (unlikely(oob)) { + /* Raw mode does data:oob:data:oob */ if (ops->mode != MTD_OOB_RAW) { int toread = min(oobreadlen, - chip->ecc.layout->oobavail); + max_oobsize); if (toread) { oob = nand_transfer_oob(chip, oob, ops, toread); -- cgit v1.2.3-70-g09d2 From 782ce79a45b3b850b108896fcf7da26754061c8f Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:36 +0200 Subject: mtd: nand: cleanup the nand_do_write_ops nand_do_write_ops was broken in regard to writing several pages, each with its own oob. Although nand_do_write_ops intends to allow such mode, it fails do do so Probably this was never tested. Also add missing checks for attempts to write at illegal offsets. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 24 +++++++++++++++++------- 1 file changed, 17 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index cada4cffacf..138674183c1 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2072,11 +2072,9 @@ static int nand_write_page(struct mtd_info *mtd, struct nand_chip *chip, * @oob: oob data buffer * @ops: oob ops structure */ -static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, - struct mtd_oob_ops *ops) +static uint8_t *nand_fill_oob(struct nand_chip *chip, uint8_t *oob, size_t len, + struct mtd_oob_ops *ops) { - size_t len = ops->ooblen; - switch(ops->mode) { case MTD_OOB_PLACE: @@ -2131,6 +2129,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, int chipnr, realpage, page, blockmask, column; struct nand_chip *chip = mtd->priv; uint32_t writelen = ops->len; + + uint32_t oobwritelen = ops->ooblen; + uint32_t oobmaxlen = ops->mode == MTD_OOB_AUTO ? + mtd->oobavail : mtd->oobsize; + uint8_t *oob = ops->oobbuf; uint8_t *buf = ops->datbuf; int ret, subpage; @@ -2172,6 +2175,10 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, if (likely(!oob)) memset(chip->oob_poi, 0xff, mtd->oobsize); + /* Don't allow multipage oob writes with offset */ + if (ops->ooboffs && (ops->ooboffs + ops->ooblen > oobmaxlen)) + return -EINVAL; + while(1) { int bytes = mtd->writesize; int cached = writelen > bytes && page != blockmask; @@ -2187,8 +2194,11 @@ static int nand_do_write_ops(struct mtd_info *mtd, loff_t to, wbuf = chip->buffers->databuf; } - if (unlikely(oob)) - oob = nand_fill_oob(chip, oob, ops); + if (unlikely(oob)) { + size_t len = min(oobwritelen, oobmaxlen); + oob = nand_fill_oob(chip, oob, len, ops); + oobwritelen -= len; + } ret = chip->write_page(mtd, chip, wbuf, page, cached, (ops->mode == MTD_OOB_RAW)); @@ -2362,7 +2372,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to, chip->pagebuf = -1; memset(chip->oob_poi, 0xff, mtd->oobsize); - nand_fill_oob(chip, ops->oobbuf, ops); + nand_fill_oob(chip, ops->oobbuf, ops->ooblen, ops); status = chip->ecc.write_oob(mtd, chip, page & chip->pagemask); memset(chip->oob_poi, 0xff, mtd->oobsize); -- cgit v1.2.3-70-g09d2 From b64d39d8b03fea88417d53715ccbebf71d4dcc9f Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:37 +0200 Subject: mtd: nand: make reads using MTD_OOB_RAW affect only ECC validation This changes the behavier of MTD_OOB_RAW. It used to read both OOB and data to the data buffer, however you would still need to specify the dummy oob buffer. This is only used in one place, but makes it hard to read data+oob without ECC test, thus I removed that behavier, and fixed the user. Now MTD_OOB_RAW behaves just like MTD_OOB_PLACE, but doesn't do ECC validation Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 19 +++++++------------ drivers/mtd/nand/nand_bbt.c | 26 ++++++++++++++++++++++---- include/linux/mtd/mtd.h | 4 +--- 3 files changed, 30 insertions(+), 19 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 138674183c1..51dfea1b3ce 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -1474,18 +1474,13 @@ static int nand_do_read_ops(struct mtd_info *mtd, loff_t from, if (unlikely(oob)) { - /* Raw mode does data:oob:data:oob */ - if (ops->mode != MTD_OOB_RAW) { - int toread = min(oobreadlen, - max_oobsize); - if (toread) { - oob = nand_transfer_oob(chip, - oob, ops, toread); - oobreadlen -= toread; - } - } else - buf = nand_transfer_oob(chip, - buf, ops, mtd->oobsize); + int toread = min(oobreadlen, max_oobsize); + + if (toread) { + oob = nand_transfer_oob(chip, + oob, ops, toread); + oobreadlen -= toread; + } } if (!(chip->options & NAND_NO_READRDY)) { diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 55c23e5cd21..387c45c366f 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -237,15 +237,33 @@ static int scan_read_raw(struct mtd_info *mtd, uint8_t *buf, loff_t offs, size_t len) { struct mtd_oob_ops ops; + int res; ops.mode = MTD_OOB_RAW; ops.ooboffs = 0; ops.ooblen = mtd->oobsize; - ops.oobbuf = buf; - ops.datbuf = buf; - ops.len = len; - return mtd->read_oob(mtd, offs, &ops); + + while (len > 0) { + if (len <= mtd->writesize) { + ops.oobbuf = buf + len; + ops.datbuf = buf; + ops.len = len; + return mtd->read_oob(mtd, offs, &ops); + } else { + ops.oobbuf = buf + mtd->writesize; + ops.datbuf = buf; + ops.len = mtd->writesize; + res = mtd->read_oob(mtd, offs, &ops); + + if (res) + return res; + } + + buf += mtd->oobsize + mtd->writesize; + len -= mtd->writesize; + } + return 0; } /* diff --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 11d8e68d17c..5326435a757 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -60,9 +60,7 @@ struct mtd_erase_region_info { * MTD_OOB_PLACE: oob data are placed at the given offset * MTD_OOB_AUTO: oob data are automatically placed at the free areas * which are defined by the ecclayout - * MTD_OOB_RAW: mode to read raw data+oob in one chunk. The oob data - * is inserted into the data. Thats a raw image of the - * flash contents. + * MTD_OOB_RAW: mode to read oob and data without doing ECC checking */ typedef enum { MTD_OOB_PLACE, -- cgit v1.2.3-70-g09d2 From e0b58d0a7005cd4b9c7fa4694a437a2d86719c13 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:38 +0200 Subject: mtd: nand: add ->badblockbits for minimum number of set bits in bad block byte This can be used to protect against bitflips in that field, but now mostly for smartmedia. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 13 +++++++++---- include/linux/mtd/nand.h | 1 + 2 files changed, 10 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 51dfea1b3ce..ba29a29bd74 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -364,14 +364,18 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) bad = cpu_to_le16(chip->read_word(mtd)); if (chip->badblockpos & 0x1) bad >>= 8; - if ((bad & 0xFF) != 0xff) - res = 1; + else + bad &= 0xFF; } else { chip->cmdfunc(mtd, NAND_CMD_READOOB, chip->badblockpos, page); - if (chip->read_byte(mtd) != 0xff) - res = 1; + bad = chip->read_byte(mtd); } + if (likely(chip->badblockbits == 8)) + res = bad != 0xFF; + else + res = hweight8(bad) < chip->badblockbits; + if (getchip) nand_release_device(mtd); @@ -2884,6 +2888,7 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, /* Set the bad block position */ chip->badblockpos = mtd->writesize > 512 ? NAND_LARGE_BADBLOCK_POS : NAND_SMALL_BADBLOCK_POS; + chip->badblockbits = 8; /* Get chip options, preserve non chip based options */ chip->options &= ~NAND_CHIPOPTIONS_MSK; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 48bc2c54302..f2d4a1ac14b 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -401,6 +401,7 @@ struct nand_chip { int subpagesize; uint8_t cellinfo; int badblockpos; + int badblockbits; flstate_t state; -- cgit v1.2.3-70-g09d2 From 9fc51a37a8da84618df7584cad67c078317f6720 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:39 +0200 Subject: mtd: common module for smartmedia/xD support This small module implements few helpers that are usefull for nand drivers for SmartMedia/xD card readers. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 9 ++++ drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/sm_common.c | 107 +++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/sm_common.h | 61 ++++++++++++++++++++++++ 4 files changed, 178 insertions(+) create mode 100644 drivers/mtd/nand/sm_common.c create mode 100644 drivers/mtd/nand/sm_common.h (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 8a7ecfab4fe..5010344f4bb 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -18,6 +18,10 @@ config MTD_NAND_VERIFY_WRITE device thinks the write was successful, a bit could have been flipped accidentally due to device wear or something else. +config MTD_NAND_SMARTMEDIA + boolean + default n + config MTD_NAND_ECC_SMC bool "NAND ECC Smart Media byte order" default n @@ -25,6 +29,11 @@ config MTD_NAND_ECC_SMC Software ECC according to the Smart Media Specification. The original Linux implementation had byte 0 and 1 swapped. +config MTD_SM_COMMON + select MTD_NAND_SMARTMEDIA + tristate + default n + config MTD_NAND_MUSEUM_IDS bool "Enable chip ids for obsolete ancient NAND devices" depends on MTD_NAND diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 1d19b7ab903..d9257961a6e 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -4,6 +4,7 @@ obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o +obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c new file mode 100644 index 00000000000..07b6f725723 --- /dev/null +++ b/drivers/mtd/nand/sm_common.c @@ -0,0 +1,107 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * Common routines & support for xD format + * + * 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 "sm_common.h" + +static struct nand_ecclayout nand_oob_sm = { + .eccbytes = 6, + .eccpos = {8, 9, 10, 13, 14, 15}, + .oobfree = { + {.offset = 0 , .length = 4}, /* reserved */ + {.offset = 6 , .length = 2}, /* LBA1 */ + {.offset = 11, .length = 2} /* LBA2 */ + } +}; + +/* NOTE: This layout is is not compatabable with SmartMedia, */ +/* because the 256 byte devices have page depenent oob layout */ +/* However it does preserve the bad block markers */ +/* If you use smftl, it will bypass this and work correctly */ +/* If you not, then you break SmartMedia compliance anyway */ + +static struct nand_ecclayout nand_oob_sm_small = { + .eccbytes = 3, + .eccpos = {0, 1, 2}, + .oobfree = { + {.offset = 3 , .length = 2}, /* reserved */ + {.offset = 6 , .length = 2}, /* LBA1 */ + } +}; + + +static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) +{ + struct mtd_oob_ops ops; + struct sm_oob oob; + int ret, error = 0; + + memset(&oob, -1, SM_OOB_SIZE); + oob.block_status = 0x0F; + + /* As long as this function is called on erase block boundaries + it will work correctly for 256 byte nand */ + ops.mode = MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = mtd->oobsize; + ops.oobbuf = (void *)&oob; + ops.datbuf = NULL; + + + ret = mtd->write_oob(mtd, ofs, &ops); + if (ret < 0 || ops.oobretlen != SM_OOB_SIZE) { + printk(KERN_NOTICE + "sm_common: can't mark sector at %i as bad\n", + (int)ofs); + error = -EIO; + } else + mtd->ecc_stats.badblocks++; + + return error; +} + + +int sm_register_device(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + int ret; + + chip->options |= NAND_SKIP_BBTSCAN | NAND_SMARTMEDIA; + + /* Scan for card properties */ + ret = nand_scan_ident(mtd, 1); + + if (ret) + return ret; + + /* Bad block marker postion */ + chip->badblockpos = 0x05; + chip->badblockbits = 7; + chip->block_markbad = sm_block_markbad; + + /* ECC layout */ + if (mtd->writesize == SM_SECTOR_SIZE) + chip->ecc.layout = &nand_oob_sm; + else if (mtd->writesize == SM_SMALL_PAGE) + chip->ecc.layout = &nand_oob_sm_small; + else + return -ENODEV; + + ret = nand_scan_tail(mtd); + + if (ret) + return ret; + + return add_mtd_device(mtd); +} +EXPORT_SYMBOL_GPL(sm_register_device); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky "); +MODULE_DESCRIPTION("Common SmartMedia/xD functions"); diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h new file mode 100644 index 00000000000..7c03314bdac --- /dev/null +++ b/drivers/mtd/nand/sm_common.h @@ -0,0 +1,61 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * Common routines & support for SmartMedia/xD format + * + * 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 + +/* Full oob structure as written on the flash */ +struct sm_oob { + uint32_t reserved; + uint8_t data_status; + uint8_t block_status; + uint8_t lba_copy1[2]; + uint8_t ecc2[3]; + uint8_t lba_copy2[2]; + uint8_t ecc1[3]; +} __attribute__((packed)); + + +/* one sector is always 512 bytes, but it can consist of two nand pages */ +#define SM_SECTOR_SIZE 512 + +/* oob area is also 16 bytes, but might be from two pages */ +#define SM_OOB_SIZE 16 + +/* This is maximum zone size, and all devices that have more that one zone + have this size */ +#define SM_MAX_ZONE_SIZE 1024 + +/* support for small page nand */ +#define SM_SMALL_PAGE 256 +#define SM_SMALL_OOB_SIZE 8 + + +extern int sm_register_device(struct mtd_info *mtd); + + +inline int sm_sector_valid(struct sm_oob *oob) +{ + return hweight16(oob->data_status) >= 5; +} + +inline int sm_block_valid(struct sm_oob *oob) +{ + return hweight16(oob->block_status) >= 7; +} + +inline int sm_block_erased(struct sm_oob *oob) +{ + static const uint32_t erased_pattern[4] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + + /* First test for erased block */ + if (!memcmp(oob, erased_pattern, sizeof(*oob))) + return 1; + return 0; +} -- cgit v1.2.3-70-g09d2 From 5e81e88a4c140586d9212999cea683bcd66a15c6 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 26 Feb 2010 18:32:56 +0000 Subject: mtd: nand: Allow caller to pass alternative ID table to nand_scan_ident() Signed-off-by: David Woodhouse --- drivers/mtd/nand/atmel_nand.c | 2 +- drivers/mtd/nand/bcm_umi_nand.c | 2 +- drivers/mtd/nand/cafe_nand.c | 2 +- drivers/mtd/nand/davinci_nand.c | 2 +- drivers/mtd/nand/fsl_elbc_nand.c | 2 +- drivers/mtd/nand/mxc_nand.c | 2 +- drivers/mtd/nand/nand_base.c | 29 +++++++++++++++-------------- drivers/mtd/nand/s3c2410.c | 3 ++- drivers/mtd/nand/sh_flctl.c | 2 +- drivers/mtd/nand/sm_common.c | 2 +- drivers/mtd/nand/socrates_nand.c | 2 +- drivers/mtd/nand/txx9ndfmc.c | 2 +- include/linux/mtd/nand.h | 4 +++- 13 files changed, 30 insertions(+), 26 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/atmel_nand.c b/drivers/mtd/nand/atmel_nand.c index 524e6c9e067..04d30887ca7 100644 --- a/drivers/mtd/nand/atmel_nand.c +++ b/drivers/mtd/nand/atmel_nand.c @@ -474,7 +474,7 @@ static int __init atmel_nand_probe(struct platform_device *pdev) } /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 1, NULL)) { res = -ENXIO; goto err_scan_ident; } diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index 087bcd745bb..5ff90b7e565 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -446,7 +446,7 @@ static int __devinit bcm_umi_nand_probe(struct platform_device *pdev) * layout we'll be using. */ - err = nand_scan_ident(board_mtd, 1); + err = nand_scan_ident(board_mtd, 1, NULL); if (err) { printk(KERN_ERR "nand_scan failed: %d\n", err); iounmap(bcm_umi_io_base); diff --git a/drivers/mtd/nand/cafe_nand.c b/drivers/mtd/nand/cafe_nand.c index 67e2b33f7ef..01a6fe1c780 100644 --- a/drivers/mtd/nand/cafe_nand.c +++ b/drivers/mtd/nand/cafe_nand.c @@ -761,7 +761,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev, cafe_readl(cafe, GLOBAL_CTRL), cafe_readl(cafe, GLOBAL_IRQ_MASK)); /* Scan to find existence of the device */ - if (nand_scan_ident(mtd, 2)) { + if (nand_scan_ident(mtd, 2, NULL)) { err = -ENXIO; goto out_irq; } diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c index e2eeaf1e51a..45bb931c084 100644 --- a/drivers/mtd/nand/davinci_nand.c +++ b/drivers/mtd/nand/davinci_nand.c @@ -690,7 +690,7 @@ 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_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1); + ret = nand_scan_ident(&info->mtd, pdata->mask_chipsel ? 2 : 1, NULL); if (ret < 0) { dev_dbg(&pdev->dev, "no NAND chip(s) found\n"); goto err_scan; diff --git a/drivers/mtd/nand/fsl_elbc_nand.c b/drivers/mtd/nand/fsl_elbc_nand.c index 1b8328fbb9d..3f38fb8e666 100644 --- a/drivers/mtd/nand/fsl_elbc_nand.c +++ b/drivers/mtd/nand/fsl_elbc_nand.c @@ -891,7 +891,7 @@ static int __devinit fsl_elbc_chip_probe(struct fsl_elbc_ctrl *ctrl, if (ret) goto err; - ret = nand_scan_ident(&priv->mtd, 1); + ret = nand_scan_ident(&priv->mtd, 1, NULL); if (ret) goto err; diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 06cc378196b..474a09e5313 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -819,7 +819,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) } /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 1, NULL)) { err = -ENXIO; goto escan; } diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index ba29a29bd74..1c4823696be 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2766,10 +2766,10 @@ static void nand_set_defaults(struct nand_chip *chip, int busw) */ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, struct nand_chip *chip, - int busw, int *maf_id) + int busw, int *maf_id, + struct nand_flash_dev *type) { - struct nand_flash_dev *type = NULL; - int i, dev_id, maf_idx; + int dev_id, maf_idx; int tmp_id, tmp_manf; /* Select the device */ @@ -2808,15 +2808,14 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, return ERR_PTR(-ENODEV); } - /* Lookup the flash id */ - for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (dev_id == nand_flash_ids[i].id) { - type = &nand_flash_ids[i]; - break; - } - } - if (!type) + type = nand_flash_ids; + + for (; type->name != NULL; type++) + if (dev_id == type->id) + break; + + if (!type->name) return ERR_PTR(-ENODEV); if (!mtd->name) @@ -2926,13 +2925,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, * nand_scan_ident - [NAND Interface] Scan for the NAND device * @mtd: MTD device structure * @maxchips: Number of chips to scan for + * @table: Alternative NAND ID table * * This is the first phase of the normal nand_scan() function. It * reads the flash ID and sets up MTD fields accordingly. * * The mtd->owner field must be set to the module of the caller. */ -int nand_scan_ident(struct mtd_info *mtd, int maxchips) +int nand_scan_ident(struct mtd_info *mtd, int maxchips, + struct nand_flash_dev *table) { int i, busw, nand_maf_id; struct nand_chip *chip = mtd->priv; @@ -2944,7 +2945,7 @@ int nand_scan_ident(struct mtd_info *mtd, int maxchips) nand_set_defaults(chip, busw); /* Read the flash type */ - type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id); + type = nand_get_flash_type(mtd, chip, busw, &nand_maf_id, table); if (IS_ERR(type)) { if (!(chip->options & NAND_SCAN_SILENT_NODEV)) @@ -3235,7 +3236,7 @@ int nand_scan(struct mtd_info *mtd, int maxchips) BUG(); } - ret = nand_scan_ident(mtd, maxchips); + ret = nand_scan_ident(mtd, maxchips, NULL); if (!ret) ret = nand_scan_tail(mtd); return ret; diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index c41ad2285c6..dc02dcd0c08 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -1013,7 +1013,8 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) s3c2410_nand_init_chip(info, nmtd, sets); nmtd->scan_res = nand_scan_ident(&nmtd->mtd, - (sets) ? sets->nr_chips : 1); + (sets) ? sets->nr_chips : 1, + NULL); if (nmtd->scan_res == 0) { s3c2410_nand_update_chip(info, nmtd); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 4260ab78f95..dbc09a81866 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -825,7 +825,7 @@ static int __init flctl_probe(struct platform_device *pdev) nand->select_chip = flctl_select_chip; nand->cmdfunc = flctl_cmdfunc; - ret = nand_scan_ident(flctl_mtd, 1); + ret = nand_scan_ident(flctl_mtd, 1, NULL); if (ret) goto err; diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index 07b6f725723..f52bb394927 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -75,7 +75,7 @@ int sm_register_device(struct mtd_info *mtd) chip->options |= NAND_SKIP_BBTSCAN | NAND_SMARTMEDIA; /* Scan for card properties */ - ret = nand_scan_ident(mtd, 1); + ret = nand_scan_ident(mtd, 1, NULL); if (ret) return ret; diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c index 65748ea2b34..b37cbde6e7d 100644 --- a/drivers/mtd/nand/socrates_nand.c +++ b/drivers/mtd/nand/socrates_nand.c @@ -220,7 +220,7 @@ static int __devinit socrates_nand_probe(struct of_device *ofdev, dev_set_drvdata(&ofdev->dev, host); /* first scan to find the device and get the page size */ - if (nand_scan_ident(mtd, 1)) { + if (nand_scan_ident(mtd, 1, NULL)) { res = -ENXIO; goto out; } diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c index 863513c3b69..054a41c0ef4 100644 --- a/drivers/mtd/nand/txx9ndfmc.c +++ b/drivers/mtd/nand/txx9ndfmc.c @@ -274,7 +274,7 @@ static int txx9ndfmc_nand_scan(struct mtd_info *mtd) struct nand_chip *chip = mtd->priv; int ret; - ret = nand_scan_ident(mtd, 1); + ret = nand_scan_ident(mtd, 1, NULL); if (!ret) { if (mtd->writesize >= 512) { chip->ecc.size = mtd->writesize; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index f2d4a1ac14b..d152bdf9161 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -25,11 +25,13 @@ #include struct mtd_info; +struct nand_flash_dev; /* Scan and identify a NAND device */ extern int nand_scan (struct mtd_info *mtd, int max_chips); /* Separate phases of nand_scan(), allowing board driver to intervene * and override command or ECC setup according to flash type */ -extern int nand_scan_ident(struct mtd_info *mtd, int max_chips); +extern int nand_scan_ident(struct mtd_info *mtd, int max_chips, + struct nand_flash_dev *table); extern int nand_scan_tail(struct mtd_info *mtd); /* Free resources held by the NAND device */ -- cgit v1.2.3-70-g09d2 From 93edbad69b0491d794c2ec86bcc65c69eac676e3 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:40 +0200 Subject: mtd: Workaround wrong write protect status on some xD cards Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 8 +++++++- include/linux/mtd/nand.h | 6 ++++++ 2 files changed, 13 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 1c4823696be..b9dc65c7253 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -434,6 +434,11 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) static int nand_check_wp(struct mtd_info *mtd) { struct nand_chip *chip = mtd->priv; + + /* broken xD cards report WP despite being writable */ + if (chip->options & NAND_BROKEN_XD) + return 0; + /* Check the WP bit */ chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); return (chip->read_byte(mtd) & NAND_STATUS_WP) ? 0 : 1; @@ -3175,7 +3180,8 @@ int nand_scan_tail(struct mtd_info *mtd) /* Fill in remaining MTD driver data */ mtd->type = MTD_NANDFLASH; - mtd->flags = MTD_CAP_NANDFLASH; + mtd->flags = (chip->options & NAND_ROM) ? MTD_CAP_ROM : + MTD_CAP_NANDFLASH; mtd->erase = nand_erase; mtd->point = NULL; mtd->unpoint = NULL; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index d152bdf9161..8bdacb885f9 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -182,6 +182,12 @@ typedef enum { /* Chip does not allow subpage writes */ #define NAND_NO_SUBPAGE_WRITE 0x00000200 +/* Device is one of 'new' xD cards that expose fake nand command set */ +#define NAND_BROKEN_XD 0x00000400 + +/* Device behaves just like nand, but is readonly */ +#define NAND_ROM 0x00000800 + /* Options valid for Samsung large page devices */ #define NAND_SAMSUNG_LP_OPTIONS \ (NAND_NO_PADDING | NAND_CACHEPRG | NAND_COPYBACK) -- cgit v1.2.3-70-g09d2 From 2764fb4244cc1bc08df3667924ca4a972e90ac70 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 26 Feb 2010 18:45:37 +0000 Subject: mtd: nand: Add SmartMedia device table to sm_common module (and remove the CONFIG_MTD_NAND_SMARTMEDIA option which isn't going to be used now that we're doing it this way) Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 5 ----- drivers/mtd/nand/sm_common.c | 40 ++++++++++++++++++++++++++++++++++++++-- 2 files changed, 38 insertions(+), 7 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 5010344f4bb..c89aaab1571 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -18,10 +18,6 @@ config MTD_NAND_VERIFY_WRITE device thinks the write was successful, a bit could have been flipped accidentally due to device wear or something else. -config MTD_NAND_SMARTMEDIA - boolean - default n - config MTD_NAND_ECC_SMC bool "NAND ECC Smart Media byte order" default n @@ -30,7 +26,6 @@ config MTD_NAND_ECC_SMC The original Linux implementation had byte 0 and 1 swapped. config MTD_SM_COMMON - select MTD_NAND_SMARTMEDIA tristate default n diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index f52bb394927..aae0b9acd7a 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -67,15 +67,51 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) } +static struct nand_flash_dev nand_smartmedia_flash_ids[] = { + + /* SmartMedia */ + {"SmartMedia 1MiB 5V", 0x6e, 256, 1, 0x1000, 0}, + {"SmartMedia 1MiB 3,3V", 0xe8, 256, 1, 0x1000, 0}, + {"SmartMedia 1MiB 3,3V", 0xec, 256, 1, 0x1000, 0}, + {"SmartMedia 2MiB 3,3V", 0xea, 256, 2, 0x1000, 0}, + {"SmartMedia 2MiB 5V", 0x64, 256, 2, 0x1000, 0}, + {"SmartMedia 2MiB 3,3V ROM", 0x5d, 512, 2, 0x2000, NAND_ROM}, + {"SmartMedia 4MiB 3,3V", 0xe3, 512, 4, 0x2000, 0}, + {"SmartMedia 4MiB 3,3/5V", 0xe5, 512, 4, 0x2000, 0}, + {"SmartMedia 4MiB 5V", 0x6b, 512, 4, 0x2000, 0}, + {"SmartMedia 4MiB 3,3V ROM", 0xd5, 512, 4, 0x2000, NAND_ROM}, + {"SmartMedia 8MiB 3,3V", 0xe6, 512, 8, 0x2000, 0}, + {"SmartMedia 8MiB 3,3V ROM", 0xd6, 512, 8, 0x2000, NAND_ROM}, + +#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD) + /* xD / SmartMedia */ + {"SmartMedia/xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, + {"SmartMedia 16MiB 3,3V ROM", 0x57, 512, 16, 0x4000, NAND_ROM}, + {"SmartMedia/xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, + {"SmartMedia 32MiB 3,3V ROM", 0x58, 512, 32, 0x4000, NAND_ROM}, + {"SmartMedia/xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, + {"SmartMedia 64MiB 3,3V ROM", 0xd9, 512, 64, 0x4000, NAND_ROM}, + {"SmartMedia/xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, + {"SmartMedia 128MiB 3,3V ROM", 0xda, 512, 128, 0x4000, NAND_ROM}, + {"SmartMedia/xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM}, + {"SmartMedia 256MiB 3,3V ROM", 0x5b, 512, 256, 0x4000, NAND_ROM}, + + /* xD only */ + {"xD 512MiB 3,3V", 0xDC, 512, 512, 0x4000, XD_TYPEM}, + {"xD 1GiB 3,3V", 0xD3, 512, 1024, 0x4000, XD_TYPEM}, + {"xD 2GiB 3,3V", 0xD5, 512, 2048, 0x4000, XD_TYPEM}, + {NULL,} +}; + int sm_register_device(struct mtd_info *mtd) { struct nand_chip *chip = (struct nand_chip *)mtd->priv; int ret; - chip->options |= NAND_SKIP_BBTSCAN | NAND_SMARTMEDIA; + chip->options |= NAND_SKIP_BBTSCAN; /* Scan for card properties */ - ret = nand_scan_ident(mtd, 1, NULL); + ret = nand_scan_ident(mtd, 1, nand_smartmedia_flash_ids); if (ret) return ret; -- cgit v1.2.3-70-g09d2 From 7d17c02a01a111f40986859f044c8c4cce8a4aa6 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:41 +0200 Subject: mtd: Add new SmartMedia/xD FTL This implements new readwrite SmartMedia/xd FTL. mtd driver must have support proper ECC and badblock verification based on oob parts for 512 bytes nand. Also mtd driver must define read_oob and write_oob, which are used to read and write both data and oob together. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 21 + drivers/mtd/Makefile | 1 + drivers/mtd/sm_ftl.c | 1284 ++++++++++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/sm_ftl.h | 94 ++++ 4 files changed, 1400 insertions(+) create mode 100644 drivers/mtd/sm_ftl.c create mode 100644 drivers/mtd/sm_ftl.h (limited to 'drivers') diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index ecf90f5c97c..8a912406433 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -304,6 +304,27 @@ config SSFDC This enables read only access to SmartMedia formatted NAND flash. You can mount it with FAT file system. + +config SM_FTL + tristate "SmartMedia/xD new translation layer" + depends on EXPERIMENTAL && BLOCK + select MTD_BLKDEVS + help + This enables new and very EXPERMENTAL support for SmartMedia/xD + FTL (Flash tanslation layer) + Write support isn't yet well tested, therefore this code IS likely to + eat your card, so please don't use it together with valuable data. + Use readonly driver (CONFIG_SSFDC) instead. + +config SM_FTL_MUSEUM + boolean "Additional Support for 1MiB and 2MiB SmartMedia cards" + depends on SM_FTL && MTD_NAND + select MTD_NAND_ECC_SMC + help + Very old SmartMedia cards need ECC to be calculated in the FTL + Such cards are very rare, thus enabling this option is mostly useless + Also this support is completely UNTESTED. + config MTD_OOPS tristate "Log panic/oops to an MTD buffer" depends on MTD diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 82d1e4de475..d53357bd75a 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_NFTL) += nftl.o obj-$(CONFIG_INFTL) += inftl.o obj-$(CONFIG_RFD_FTL) += rfd_ftl.o obj-$(CONFIG_SSFDC) += ssfdc.o +obj-$(CONFIG_SM_FTL) += sm_ftl.o obj-$(CONFIG_MTD_OOPS) += mtdoops.o nftl-objs := nftlcore.o nftlmount.o diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c new file mode 100644 index 00000000000..a59ebb48cae --- /dev/null +++ b/drivers/mtd/sm_ftl.c @@ -0,0 +1,1284 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * SmartMedia/xD translation layer + * + * 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 "nand/sm_common.h" +#include "sm_ftl.h" + +#ifdef CONFIG_SM_FTL_MUSEUM +#include +#endif + + +struct workqueue_struct *cache_flush_workqueue; + +static int cache_timeout = 1000; +module_param(cache_timeout, bool, S_IRUGO); +MODULE_PARM_DESC(cache_timeout, + "Timeout (in ms) for cache flush (1000 ms default"); + +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + + +/* ------------------- sysfs attributtes ---------------------------------- */ +struct sm_sysfs_attribute { + struct device_attribute dev_attr; + char *data; + int len; +}; + +ssize_t sm_attr_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct sm_sysfs_attribute *sm_attr = + container_of(attr, struct sm_sysfs_attribute, dev_attr); + + strncpy(buf, sm_attr->data, sm_attr->len); + return sm_attr->len; +} + + +#define NUM_ATTRIBUTES 1 +#define SM_CIS_VENDOR_OFFSET 0x59 +struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) +{ + struct attribute_group *attr_group; + struct attribute **attributes; + struct sm_sysfs_attribute *vendor_attribute; + + int vendor_len = strnlen(ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, + SM_SMALL_PAGE - SM_CIS_VENDOR_OFFSET); + + char *vendor = kmalloc(vendor_len, GFP_KERNEL); + memcpy(vendor, ftl->cis_buffer + SM_CIS_VENDOR_OFFSET, vendor_len); + vendor[vendor_len] = 0; + + /* Initialize sysfs attributes */ + vendor_attribute = + kzalloc(sizeof(struct sm_sysfs_attribute), GFP_KERNEL); + + vendor_attribute->data = vendor; + vendor_attribute->len = vendor_len; + vendor_attribute->dev_attr.attr.name = "vendor"; + vendor_attribute->dev_attr.attr.mode = S_IRUGO; + vendor_attribute->dev_attr.show = sm_attr_show; + + + /* Create array of pointers to the attributes */ + attributes = kzalloc(sizeof(struct attribute *) * (NUM_ATTRIBUTES + 1), + GFP_KERNEL); + attributes[0] = &vendor_attribute->dev_attr.attr; + + /* Finally create the attribute group */ + attr_group = kzalloc(sizeof(struct attribute_group), GFP_KERNEL); + attr_group->attrs = attributes; + return attr_group; +} + +void sm_delete_sysfs_attributes(struct sm_ftl *ftl) +{ + struct attribute **attributes = ftl->disk_attributes->attrs; + int i; + + for (i = 0; attributes[i] ; i++) { + + struct device_attribute *dev_attr = container_of(attributes[i], + struct device_attribute, attr); + + struct sm_sysfs_attribute *sm_attr = + container_of(dev_attr, + struct sm_sysfs_attribute, dev_attr); + + kfree(sm_attr->data); + kfree(sm_attr); + } + + kfree(ftl->disk_attributes->attrs); + kfree(ftl->disk_attributes); +} + + +/* ----------------------- oob helpers -------------------------------------- */ + +static int sm_get_lba(uint8_t *lba) +{ + /* check fixed bits */ + if ((lba[0] & 0xF8) != 0x10) + return -2; + + /* check parity - endianess doesn't matter */ + if (hweight16(*(uint16_t *)lba) & 1) + return -2; + + return (lba[1] >> 1) | ((lba[0] & 0x07) << 7); +} + + +/* + * Read LBA asscociated with block + * returns -1, if block is erased + * returns -2 if error happens + */ +static int sm_read_lba(struct sm_oob *oob) +{ + static const uint32_t erased_pattern[4] = { + 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; + + uint16_t lba_test; + int lba; + + /* First test for erased block */ + if (!memcmp(oob, erased_pattern, SM_OOB_SIZE)) + return -1; + + /* Now check is both copies of the LBA differ too much */ + lba_test = *(uint16_t *)oob->lba_copy1 ^ *(uint16_t*)oob->lba_copy2; + if (lba_test && !is_power_of_2(lba_test)) + return -2; + + /* And read it */ + lba = sm_get_lba(oob->lba_copy1); + + if (lba == -2) + lba = sm_get_lba(oob->lba_copy2); + + return lba; +} + +static void sm_write_lba(struct sm_oob *oob, uint16_t lba) +{ + uint8_t tmp[2]; + + WARN_ON(lba >= 1000); + + tmp[0] = 0x10 | ((lba >> 7) & 0x07); + tmp[1] = (lba << 1) & 0xFF; + + if (hweight16(*(uint16_t *)tmp) & 0x01) + tmp[1] |= 1; + + oob->lba_copy1[0] = oob->lba_copy2[0] = tmp[0]; + oob->lba_copy1[1] = oob->lba_copy2[1] = tmp[1]; +} + + +/* Make offset from parts */ +static loff_t sm_mkoffset(struct sm_ftl *ftl, int zone, int block, int boffset) +{ + WARN_ON(boffset & (SM_SECTOR_SIZE - 1)); + WARN_ON(zone < 0 || zone >= ftl->zone_count); + WARN_ON(block >= ftl->zone_size); + WARN_ON(boffset >= ftl->block_size); + + if (block == -1) + return -1; + + return (zone * SM_MAX_ZONE_SIZE + block) * ftl->block_size + boffset; +} + +/* Breaks offset into parts */ +static void sm_break_offset(struct sm_ftl *ftl, loff_t offset, + int *zone, int *block, int *boffset) +{ + *boffset = do_div(offset, ftl->block_size); + *block = do_div(offset, ftl->max_lba); + *zone = offset >= ftl->zone_count ? -1 : offset; +} + +/* ---------------------- low level IO ------------------------------------- */ + +static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob) +{ +#ifdef CONFIG_SM_FTL_MUSEUM + uint8_t ecc[3]; + + __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc); + if (__nand_correct_data(buffer, ecc, oob->ecc1, SM_SMALL_PAGE) < 0) + return -EIO; + + buffer += SM_SMALL_PAGE; + + __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc); + if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE) < 0) + return -EIO; +#endif + return 0; +} + +/* Reads a sector + oob*/ +static int sm_read_sector(struct sm_ftl *ftl, + int zone, int block, int boffset, + uint8_t *buffer, struct sm_oob *oob) +{ + struct mtd_info *mtd = ftl->trans->mtd; + struct mtd_oob_ops ops; + struct sm_oob tmp_oob; + int ret; + int try = 0; + + /* FTL can contain -1 entries that are by default filled with bits */ + if (block == -1) { + memset(buffer, 0xFF, SM_SECTOR_SIZE); + return 0; + } + + /* User might not need the oob, but we do for data vertification */ + if (!oob) + oob = &tmp_oob; + + ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE; + ops.ooboffs = 0; + ops.ooblen = SM_OOB_SIZE; + ops.oobbuf = (void *)oob; + ops.len = SM_SECTOR_SIZE; + ops.datbuf = buffer; + +again: + if (try++) { + /* Avoid infinite recursion on CIS reads, sm_recheck_media + won't help anyway */ + if (zone == 0 && block == ftl->cis_block && boffset == + ftl->cis_boffset) + return ret; + + /* Test if media is stable */ + if (try == 3 || sm_recheck_media(ftl)) + return ret; + } + + /* Unfortunelly, oob read will _always_ succeed, + despite card removal..... */ + ret = mtd->read_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); + + /* Test for unknown errors */ + if (ret != 0 && ret != -EUCLEAN && ret != -EBADMSG) { + dbg("read of block %d at zone %d, failed due to error (%d)", + block, zone, ret); + goto again; + } + + /* Do a basic test on the oob, to guard against returned garbage */ + if (oob->reserved != 0xFFFFFFFF && !is_power_of_2(~oob->reserved)) + goto again; + + /* This should never happen, unless there is a bug in the mtd driver */ + WARN_ON(ops.oobretlen != SM_OOB_SIZE); + WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); + + if (!buffer) + return 0; + + /* Test if sector marked as bad */ + if (!sm_sector_valid(oob)) { + dbg("read of block %d at zone %d, failed because it is marked" + " as bad" , block, zone); + goto again; + } + + /* Test ECC*/ + if (ret == -EBADMSG || + (ftl->smallpagenand && sm_correct_sector(buffer, oob))) { + + dbg("read of block %d at zone %d, failed due to ECC error", + block, zone); + goto again; + } + + return 0; +} + +/* Writes a sector to media */ +static int sm_write_sector(struct sm_ftl *ftl, + int zone, int block, int boffset, + uint8_t *buffer, struct sm_oob *oob) +{ + struct mtd_oob_ops ops; + struct mtd_info *mtd = ftl->trans->mtd; + int ret; + + BUG_ON(ftl->readonly); + + if (zone == 0 && (block == ftl->cis_block || block == 0)) { + dbg("attempted to write the CIS!"); + return -EIO; + } + + if (ftl->unstable) + return -EIO; + + ops.mode = ftl->smallpagenand ? MTD_OOB_RAW : MTD_OOB_PLACE; + ops.len = SM_SECTOR_SIZE; + ops.datbuf = buffer; + ops.ooboffs = 0; + ops.ooblen = SM_OOB_SIZE; + ops.oobbuf = (void *)oob; + + ret = mtd->write_oob(mtd, sm_mkoffset(ftl, zone, block, boffset), &ops); + + /* Now we assume that hardware will catch write bitflip errors */ + /* If you are paranoid, use CONFIG_MTD_NAND_VERIFY_WRITE */ + + if (ret) { + dbg("write to block %d at zone %d, failed with error %d", + block, zone, ret); + + sm_recheck_media(ftl); + return ret; + } + + /* This should never happen, unless there is a bug in the driver */ + WARN_ON(ops.oobretlen != SM_OOB_SIZE); + WARN_ON(buffer && ops.retlen != SM_SECTOR_SIZE); + + return 0; +} + +/* ------------------------ block IO ------------------------------------- */ + +/* Write a block using data and lba, and invalid sector bitmap */ +static int sm_write_block(struct sm_ftl *ftl, uint8_t *buf, + int zone, int block, int lba, + unsigned long invalid_bitmap) +{ + struct sm_oob oob; + int boffset; + int retry = 0; + + /* Initialize the oob with requested values */ + memset(&oob, 0xFF, SM_OOB_SIZE); + sm_write_lba(&oob, lba); +restart: + if (ftl->unstable) + return -EIO; + + for (boffset = 0; boffset < ftl->block_size; + boffset += SM_SECTOR_SIZE) { + + oob.data_status = 0xFF; + + if (test_bit(boffset / SM_SECTOR_SIZE, &invalid_bitmap)) { + + sm_printk("sector %d of block at LBA %d of zone %d" + " coudn't be read, marking it as invalid", + boffset / SM_SECTOR_SIZE, lba, zone); + + oob.data_status = 0; + } + +#ifdef CONFIG_SM_FTL_MUSEUM + if (ftl->smallpagenand) { + __nand_calculate_ecc(buf + boffset, + SM_SMALL_PAGE, oob.ecc1); + + __nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE, + SM_SMALL_PAGE, oob.ecc2); + } +#endif + if (!sm_write_sector(ftl, zone, block, boffset, + buf + boffset, &oob)) + continue; + + if (!retry) { + + /* If write fails. try to erase the block */ + /* This is safe, because we never write in blocks + that contain valuable data. + This is intended to repair block that are marked + as erased, but that isn't fully erased*/ + + if (sm_erase_block(ftl, zone, block, 0)) + return -EIO; + + retry = 1; + goto restart; + } else { + sm_mark_block_bad(ftl, zone, block); + return -EIO; + } + } + return 0; +} + + +/* Mark whole block at offset 'offs' as bad. */ +static void sm_mark_block_bad(struct sm_ftl *ftl, int zone, int block) +{ + struct sm_oob oob; + int boffset; + + memset(&oob, 0xFF, SM_OOB_SIZE); + oob.block_status = 0xF0; + + if (ftl->unstable) + return; + + if (sm_recheck_media(ftl)) + return; + + sm_printk("marking block %d of zone %d as bad", block, zone); + + /* We aren't checking the return value, because we don't care */ + /* This also fails on fake xD cards, but I guess these won't expose + any bad blocks till fail completly */ + for (boffset = 0; boffset < ftl->block_size; boffset += SM_SECTOR_SIZE) + sm_write_sector(ftl, zone, block, boffset, NULL, &oob); +} + +/* + * Erase a block within a zone + * If erase succedes, it updates free block fifo, otherwise marks block as bad + */ +static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, + int put_free) +{ + struct ftl_zone *zone = &ftl->zones[zone_num]; + struct mtd_info *mtd = ftl->trans->mtd; + struct erase_info erase; + + erase.mtd = mtd; + erase.callback = sm_erase_callback; + erase.addr = sm_mkoffset(ftl, zone_num, block, 0); + erase.len = ftl->block_size; + erase.priv = (u_long)ftl; + + if (ftl->unstable) + return -EIO; + + BUG_ON(ftl->readonly); + + if (zone_num == 0 && (block == ftl->cis_block || block == 0)) { + sm_printk("attempted to erase the CIS!"); + return -EIO; + } + + if (mtd->erase(mtd, &erase)) { + sm_printk("erase of block %d in zone %d failed", + block, zone_num); + goto error; + } + + if (erase.state == MTD_ERASE_PENDING) + wait_for_completion(&ftl->erase_completion); + + if (erase.state != MTD_ERASE_DONE) { + sm_printk("erase of block %d in zone %d failed after wait", + block, zone_num); + goto error; + } + + if (put_free) + kfifo_in(&zone->free_sectors, + (const unsigned char *)&block, sizeof(block)); + + return 0; +error: + sm_mark_block_bad(ftl, zone_num, block); + return -EIO; +} + +static void sm_erase_callback(struct erase_info *self) +{ + struct sm_ftl *ftl = (struct sm_ftl *)self->priv; + complete(&ftl->erase_completion); +} + +/* Throughtly test that block is valid. */ +static int sm_check_block(struct sm_ftl *ftl, int zone, int block) +{ + int boffset; + struct sm_oob oob; + int lbas[] = { -3, 0, 0, 0 }; + int i = 0; + int test_lba; + + + /* First just check that block doesn't look fishy */ + /* Only blocks that are valid or are sliced in two parts, are + accepted */ + for (boffset = 0; boffset < ftl->block_size; + boffset += SM_SECTOR_SIZE) { + + /* This shoudn't happen anyway */ + if (sm_read_sector(ftl, zone, block, boffset, NULL, &oob)) + return -2; + + test_lba = sm_read_lba(&oob); + + if (lbas[i] != test_lba) + lbas[++i] = test_lba; + + /* If we found three different LBAs, something is fishy */ + if (i == 3) + return -EIO; + } + + /* If the block is sliced (partialy erased usually) erase it */ + if (i == 2) { + sm_erase_block(ftl, zone, block, 1); + return 1; + } + + return 0; +} + +/* ----------------- media scanning --------------------------------- */ +static const struct chs_entry chs_table[] = { + { 1, 125, 4, 4 }, + { 2, 125, 4, 8 }, + { 4, 250, 4, 8 }, + { 8, 250, 4, 16 }, + { 16, 500, 4, 16 }, + { 32, 500, 8, 16 }, + { 64, 500, 8, 32 }, + { 128, 500, 16, 32 }, + { 256, 1000, 16, 32 }, + { 512, 1015, 32, 63 }, + { 1024, 985, 33, 63 }, + { 2048, 985, 33, 63 }, + { 0 }, +}; + + +static const uint8_t cis_signature[] = { + 0x01, 0x03, 0xD9, 0x01, 0xFF, 0x18, 0x02, 0xDF, 0x01, 0x20 +}; +/* Find out media parameters. + * This ideally has to be based on nand id, but for now device size is enough */ +int sm_get_media_info(struct sm_ftl *ftl, struct mtd_info *mtd) +{ + int i; + int size_in_megs = mtd->size / (1024 * 1024); + + ftl->readonly = mtd->type == MTD_ROM; + + /* Manual settings for very old devices */ + ftl->zone_count = 1; + ftl->smallpagenand = 0; + + switch (size_in_megs) { + case 1: + /* 1 MiB flash/rom SmartMedia card (256 byte pages)*/ + ftl->zone_size = 256; + ftl->max_lba = 250; + ftl->block_size = 8 * SM_SECTOR_SIZE; + ftl->smallpagenand = 1; + + break; + case 2: + /* 2 MiB flash SmartMedia (256 byte pages)*/ + if (mtd->writesize == SM_SMALL_PAGE) { + ftl->zone_size = 512; + ftl->max_lba = 500; + ftl->block_size = 8 * SM_SECTOR_SIZE; + ftl->smallpagenand = 1; + /* 2 MiB rom SmartMedia */ + } else { + + if (!ftl->readonly) + return -ENODEV; + + ftl->zone_size = 256; + ftl->max_lba = 250; + ftl->block_size = 16 * SM_SECTOR_SIZE; + } + break; + case 4: + /* 4 MiB flash/rom SmartMedia device */ + ftl->zone_size = 512; + ftl->max_lba = 500; + ftl->block_size = 16 * SM_SECTOR_SIZE; + break; + case 8: + /* 8 MiB flash/rom SmartMedia device */ + ftl->zone_size = 1024; + ftl->max_lba = 1000; + ftl->block_size = 16 * SM_SECTOR_SIZE; + } + + /* Minimum xD size is 16MiB. Also, all xD cards have standard zone + sizes. SmartMedia cards exist up to 128 MiB and have same layout*/ + if (size_in_megs >= 16) { + ftl->zone_count = size_in_megs / 16; + ftl->zone_size = 1024; + ftl->max_lba = 1000; + ftl->block_size = 32 * SM_SECTOR_SIZE; + } + + /* Test for proper write,erase and oob sizes */ + if (mtd->erasesize > ftl->block_size) + return -ENODEV; + + if (mtd->writesize > SM_SECTOR_SIZE) + return -ENODEV; + + if (ftl->smallpagenand && mtd->oobsize < SM_SMALL_OOB_SIZE) + return -ENODEV; + + if (!ftl->smallpagenand && mtd->oobsize < SM_OOB_SIZE) + return -ENODEV; + + /* We use these functions for IO */ + if (!mtd->read_oob || !mtd->write_oob) + return -ENODEV; + + /* Find geometry information */ + for (i = 0 ; i < ARRAY_SIZE(chs_table) ; i++) { + if (chs_table[i].size == size_in_megs) { + ftl->cylinders = chs_table[i].cyl; + ftl->heads = chs_table[i].head; + ftl->sectors = chs_table[i].sec; + return 0; + } + } + + sm_printk("media has unknown size : %dMiB", size_in_megs); + ftl->cylinders = 985; + ftl->heads = 33; + ftl->sectors = 63; + return 0; +} + +/* Validate the CIS */ +static int sm_read_cis(struct sm_ftl *ftl) +{ + struct sm_oob oob; + + if (sm_read_sector(ftl, + 0, ftl->cis_block, ftl->cis_boffset, ftl->cis_buffer, &oob)) + return -EIO; + + if (!sm_sector_valid(&oob) || !sm_block_valid(&oob)) + return -EIO; + + if (!memcmp(ftl->cis_buffer + ftl->cis_page_offset, + cis_signature, sizeof(cis_signature))) { + return 0; + } + + return -EIO; +} + +/* Scan the media for the CIS */ +static int sm_find_cis(struct sm_ftl *ftl) +{ + struct sm_oob oob; + int block, boffset; + int block_found = 0; + int cis_found = 0; + + /* Search for first valid block */ + for (block = 0 ; block < ftl->zone_size - ftl->max_lba ; block++) { + + if (sm_read_sector(ftl, 0, block, 0, NULL, &oob)) + continue; + + if (!sm_block_valid(&oob)) + continue; + block_found = 1; + break; + } + + if (!block_found) + return -EIO; + + /* Search for first valid sector in this block */ + for (boffset = 0 ; boffset < ftl->block_size; + boffset += SM_SECTOR_SIZE) { + + if (sm_read_sector(ftl, 0, block, boffset, NULL, &oob)) + continue; + + if (!sm_sector_valid(&oob)) + continue; + break; + } + + if (boffset == ftl->block_size) + return -EIO; + + ftl->cis_block = block; + ftl->cis_boffset = boffset; + ftl->cis_page_offset = 0; + + cis_found = !sm_read_cis(ftl); + + if (!cis_found) { + ftl->cis_page_offset = SM_SMALL_PAGE; + cis_found = !sm_read_cis(ftl); + } + + if (cis_found) { + dbg("CIS block found at offset %x", + block * ftl->block_size + + boffset + ftl->cis_page_offset); + return 0; + } + return -EIO; +} + +/* Basic test to determine if underlying mtd device if functional */ +static int sm_recheck_media(struct sm_ftl *ftl) +{ + if (sm_read_cis(ftl)) { + + if (!ftl->unstable) { + sm_printk("media unstable, not allowing writes"); + ftl->unstable = 1; + } + return -EIO; + } + return 0; +} + +/* Initialize a FTL zone */ +static int sm_init_zone(struct sm_ftl *ftl, int zone_num) +{ + struct ftl_zone *zone = &ftl->zones[zone_num]; + struct sm_oob oob; + uint16_t block; + int lba; + int i = 0; + + dbg("initializing zone %d", zone_num); + + /* Allocate memory for FTL table */ + zone->lba_to_phys_table = kmalloc(ftl->max_lba * 2, GFP_KERNEL); + + if (!zone->lba_to_phys_table) + return -ENOMEM; + memset(zone->lba_to_phys_table, -1, ftl->max_lba * 2); + + + /* Allocate memory for free sectors FIFO */ + if (kfifo_alloc(&zone->free_sectors, ftl->zone_size * 2, GFP_KERNEL)) { + kfree(zone->lba_to_phys_table); + return -ENOMEM; + } + + /* Now scan the zone */ + for (block = 0 ; block < ftl->zone_size ; block++) { + + /* Skip blocks till the CIS (including) */ + if (zone_num == 0 && block <= ftl->cis_block) + continue; + + /* Read the oob of first sector */ + if (sm_read_sector(ftl, zone_num, block, 0, NULL, &oob)) + return -EIO; + + /* Test to see if block is erased. It is enough to test + first sector, because erase happens in one shot */ + if (sm_block_erased(&oob)) { + kfifo_in(&zone->free_sectors, + (unsigned char *)&block, 2); + continue; + } + + /* If block is marked as bad, skip it */ + /* This assumes we can trust first sector*/ + /* However the way the block valid status is defined, ensures + very low probability of failure here */ + if (!sm_block_valid(&oob)) { + dbg("PH %04d <-> ", block); + continue; + } + + + lba = sm_read_lba(&oob); + + /* Invalid LBA means that block is damaged. */ + /* We can try to erase it, or mark it as bad, but + lets leave that to recovery application */ + if (lba == -2 || lba >= ftl->max_lba) { + dbg("PH %04d <-> LBA %04d(bad)", block, lba); + continue; + } + + + /* If there is no collision, + just put the sector in the FTL table */ + if (zone->lba_to_phys_table[lba] < 0) { + dbg_verbose("PH %04d <-> LBA %04d", block, lba); + zone->lba_to_phys_table[lba] = block; + continue; + } + + sm_printk("collision" + " of LBA %d between blocks %d and %d in zone %d", + lba, zone->lba_to_phys_table[lba], block, zone_num); + + /* Test that this block is valid*/ + if (sm_check_block(ftl, zone_num, block)) + continue; + + /* Test now the old block */ + if (sm_check_block(ftl, zone_num, + zone->lba_to_phys_table[lba])) { + zone->lba_to_phys_table[lba] = block; + continue; + } + + /* If both blocks are valid and share same LBA, it means that + they hold different versions of same data. It not + known which is more recent, thus just erase one of them + */ + sm_printk("both blocks are valid, erasing the later"); + sm_erase_block(ftl, zone_num, block, 1); + } + + dbg("zone initialized"); + zone->initialized = 1; + + /* No free sectors, means that the zone is heavily damaged, write won't + work, but it can still can be (partially) read */ + if (!kfifo_len(&zone->free_sectors)) { + sm_printk("no free blocks in zone %d", zone_num); + return 0; + } + + /* Randomize first block we write to */ + get_random_bytes(&i, 2); + i %= (kfifo_len(&zone->free_sectors) / 2); + + while (i--) { + kfifo_out(&zone->free_sectors, (unsigned char *)&block, 2); + kfifo_in(&zone->free_sectors, (const unsigned char *)&block, 2); + } + return 0; +} + +/* Get and automaticly initialize an FTL mapping for one zone */ +struct ftl_zone *sm_get_zone(struct sm_ftl *ftl, int zone_num) +{ + struct ftl_zone *zone; + int error; + + BUG_ON(zone_num >= ftl->zone_count); + zone = &ftl->zones[zone_num]; + + if (!zone->initialized) { + error = sm_init_zone(ftl, zone_num); + + if (error) + return ERR_PTR(error); + } + return zone; +} + + +/* ----------------- cache handling ------------------------------------------*/ + +/* Initialize the one block cache */ +void sm_cache_init(struct sm_ftl *ftl) +{ + ftl->cache_data_invalid_bitmap = 0xFFFFFFFF; + ftl->cache_clean = 1; + ftl->cache_zone = -1; + ftl->cache_block = -1; + /*memset(ftl->cache_data, 0xAA, ftl->block_size);*/ +} + +/* Put sector in one block cache */ +void sm_cache_put(struct sm_ftl *ftl, char *buffer, int boffset) +{ + memcpy(ftl->cache_data + boffset, buffer, SM_SECTOR_SIZE); + clear_bit(boffset / SM_SECTOR_SIZE, &ftl->cache_data_invalid_bitmap); + ftl->cache_clean = 0; +} + +/* Read a sector from the cache */ +int sm_cache_get(struct sm_ftl *ftl, char *buffer, int boffset) +{ + if (test_bit(boffset / SM_SECTOR_SIZE, + &ftl->cache_data_invalid_bitmap)) + return -1; + + memcpy(buffer, ftl->cache_data + boffset, SM_SECTOR_SIZE); + return 0; +} + +/* Write the cache to hardware */ +int sm_cache_flush(struct sm_ftl *ftl) +{ + struct ftl_zone *zone; + + int sector_num; + uint16_t write_sector; + int zone_num = ftl->cache_zone; + int block_num; + + if (ftl->cache_clean) + return 0; + + if (ftl->unstable) + return -EIO; + + BUG_ON(zone_num < 0); + zone = &ftl->zones[zone_num]; + block_num = zone->lba_to_phys_table[ftl->cache_block]; + + + /* Try to read all unread areas of the cache block*/ + for_each_bit(sector_num, &ftl->cache_data_invalid_bitmap, + ftl->block_size / SM_SECTOR_SIZE) { + + if (!sm_read_sector(ftl, + zone_num, block_num, sector_num * SM_SECTOR_SIZE, + ftl->cache_data + sector_num * SM_SECTOR_SIZE, NULL)) + clear_bit(sector_num, + &ftl->cache_data_invalid_bitmap); + } +restart: + + if (ftl->unstable) + return -EIO; + /* No spare blocks */ + /* We could still continue by erasing the current block, + but for such worn out media it doesn't worth the trouble, + and the dangers */ + + if (!kfifo_len(&zone->free_sectors)) { + dbg("no free sectors for write!"); + return -EIO; + } + + kfifo_out(&zone->free_sectors, (unsigned char *)&write_sector, 2); + + if (sm_write_block(ftl, ftl->cache_data, zone_num, write_sector, + ftl->cache_block, ftl->cache_data_invalid_bitmap)) + goto restart; + + /* Update the FTL table */ + zone->lba_to_phys_table[ftl->cache_block] = write_sector; + + /* Write succesfull, so erase and free the old block */ + if (block_num > 0) + sm_erase_block(ftl, zone_num, block_num, 1); + + sm_cache_init(ftl); + return 0; +} + + +/* flush timer, runs a second after last write */ +static void sm_cache_flush_timer(unsigned long data) +{ + struct sm_ftl *ftl = (struct sm_ftl *)data; + queue_work(cache_flush_workqueue, &ftl->flush_work); +} + +/* cache flush work, kicked by timer */ +static void sm_cache_flush_work(struct work_struct *work) +{ + struct sm_ftl *ftl = container_of(work, struct sm_ftl, flush_work); + mutex_lock(&ftl->mutex); + sm_cache_flush(ftl); + mutex_unlock(&ftl->mutex); + return; +} + +/* ---------------- outside interface -------------------------------------- */ + +/* outside interface: read a sector */ +static int sm_read(struct mtd_blktrans_dev *dev, + unsigned long sect_no, char *buf) +{ + struct sm_ftl *ftl = dev->priv; + struct ftl_zone *zone; + int error = 0, in_cache = 0; + int zone_num, block, boffset; + + sm_break_offset(ftl, sect_no << 9, &zone_num, &block, &boffset); + mutex_lock(&ftl->mutex); + + + zone = sm_get_zone(ftl, zone_num); + if (IS_ERR(zone)) { + error = PTR_ERR(zone); + goto unlock; + } + + /* Have to look at cache first */ + if (ftl->cache_zone == zone_num && ftl->cache_block == block) { + in_cache = 1; + if (!sm_cache_get(ftl, buf, boffset)) + goto unlock; + } + + /* Translate the block and return if doesn't exist in the table */ + block = zone->lba_to_phys_table[block]; + + if (block == -1) { + memset(buf, 0xFF, SM_SECTOR_SIZE); + goto unlock; + } + + if (sm_read_sector(ftl, zone_num, block, boffset, buf, NULL)) { + error = -EIO; + goto unlock; + } + + if (in_cache) + sm_cache_put(ftl, buf, boffset); +unlock: + mutex_unlock(&ftl->mutex); + return error; +} + +/* outside interface: write a sector */ +static int sm_write(struct mtd_blktrans_dev *dev, + unsigned long sec_no, char *buf) +{ + struct sm_ftl *ftl = dev->priv; + struct ftl_zone *zone; + int error, zone_num, block, boffset; + + BUG_ON(ftl->readonly); + sm_break_offset(ftl, sec_no << 9, &zone_num, &block, &boffset); + + /* No need in flush thread running now */ + del_timer(&ftl->timer); + mutex_lock(&ftl->mutex); + + zone = sm_get_zone(ftl, zone_num); + if (IS_ERR(zone)) { + error = PTR_ERR(zone); + goto unlock; + } + + /* If entry is not in cache, flush it */ + if (ftl->cache_block != block || ftl->cache_zone != zone_num) { + + error = sm_cache_flush(ftl); + if (error) + goto unlock; + + ftl->cache_block = block; + ftl->cache_zone = zone_num; + } + + sm_cache_put(ftl, buf, boffset); +unlock: + mod_timer(&ftl->timer, jiffies + msecs_to_jiffies(cache_timeout)); + mutex_unlock(&ftl->mutex); + return error; +} + +/* outside interface: flush everything */ +static int sm_flush(struct mtd_blktrans_dev *dev) +{ + struct sm_ftl *ftl = dev->priv; + int retval; + + mutex_lock(&ftl->mutex); + retval = sm_cache_flush(ftl); + mutex_unlock(&ftl->mutex); + return retval; +} + +/* outside interface: device is released */ +static int sm_release(struct mtd_blktrans_dev *dev) +{ + struct sm_ftl *ftl = dev->priv; + + mutex_lock(&ftl->mutex); + del_timer_sync(&ftl->timer); + cancel_work_sync(&ftl->flush_work); + sm_cache_flush(ftl); + mutex_unlock(&ftl->mutex); + return 0; +} + +/* outside interface: get geometry */ +static int sm_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) +{ + struct sm_ftl *ftl = dev->priv; + geo->heads = ftl->heads; + geo->sectors = ftl->sectors; + geo->cylinders = ftl->cylinders; + return 0; +} + +/* external interface: main initialization function */ +static void sm_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) +{ + struct mtd_blktrans_dev *trans; + struct sm_ftl *ftl; + + /* Allocate & initialize our private structure */ + ftl = kzalloc(sizeof(struct sm_ftl), GFP_KERNEL); + if (!ftl) + goto error1; + + + mutex_init(&ftl->mutex); + setup_timer(&ftl->timer, sm_cache_flush_timer, (unsigned long)ftl); + INIT_WORK(&ftl->flush_work, sm_cache_flush_work); + init_completion(&ftl->erase_completion); + + /* Read media information */ + if (sm_get_media_info(ftl, mtd)) { + dbg("found unsupported mtd device, aborting"); + goto error2; + } + + + /* Allocate temporary CIS buffer for read retry support */ + ftl->cis_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); + if (!ftl->cis_buffer) + goto error2; + + /* Allocate zone array, it will be initialized on demand */ + ftl->zones = kzalloc(sizeof(struct ftl_zone) * ftl->zone_count, + GFP_KERNEL); + if (!ftl->zones) + goto error3; + + /* Allocate the cache*/ + ftl->cache_data = kzalloc(ftl->block_size, GFP_KERNEL); + + if (!ftl->cache_data) + goto error4; + + sm_cache_init(ftl); + + + /* Allocate upper layer structure and initialize it */ + trans = kzalloc(sizeof(struct mtd_blktrans_dev), GFP_KERNEL); + if (!trans) + goto error5; + + ftl->trans = trans; + trans->priv = ftl; + + trans->tr = tr; + trans->mtd = mtd; + trans->devnum = -1; + trans->size = (ftl->block_size * ftl->max_lba * ftl->zone_count) >> 9; + trans->readonly = ftl->readonly; + + if (sm_find_cis(ftl)) { + dbg("CIS not found on mtd device, aborting"); + goto error6; + } + + ftl->disk_attributes = sm_create_sysfs_attributes(ftl); + trans->disk_attributes = ftl->disk_attributes; + + sm_printk("Found %d MiB xD/SmartMedia FTL on mtd%d", + (int)(mtd->size / (1024 * 1024)), mtd->index); + + dbg("FTL layout:"); + dbg("%d zone(s), each consists of %d blocks (+%d spares)", + ftl->zone_count, ftl->max_lba, + ftl->zone_size - ftl->max_lba); + dbg("each block consists of %d bytes", + ftl->block_size); + + + /* Register device*/ + if (add_mtd_blktrans_dev(trans)) { + dbg("error in mtdblktrans layer"); + goto error6; + } + return; +error6: + kfree(trans); +error5: + kfree(ftl->cache_data); +error4: + kfree(ftl->zones); +error3: + kfree(ftl->cis_buffer); +error2: + kfree(ftl); +error1: + return; +} + +/* main interface: device {surprise,} removal */ +static void sm_remove_dev(struct mtd_blktrans_dev *dev) +{ + struct sm_ftl *ftl = dev->priv; + int i; + + del_mtd_blktrans_dev(dev); + ftl->trans = NULL; + + for (i = 0 ; i < ftl->zone_count; i++) { + + if (!ftl->zones[i].initialized) + continue; + + kfree(ftl->zones[i].lba_to_phys_table); + kfifo_free(&ftl->zones[i].free_sectors); + } + + sm_delete_sysfs_attributes(ftl); + kfree(ftl->cis_buffer); + kfree(ftl->zones); + kfree(ftl->cache_data); + kfree(ftl); +} + +static struct mtd_blktrans_ops sm_ftl_ops = { + .name = "smblk", + .major = -1, + .part_bits = SM_FTL_PARTN_BITS, + .blksize = SM_SECTOR_SIZE, + .getgeo = sm_getgeo, + + .add_mtd = sm_add_mtd, + .remove_dev = sm_remove_dev, + + .readsect = sm_read, + .writesect = sm_write, + + .flush = sm_flush, + .release = sm_release, + + .owner = THIS_MODULE, +}; + +static __init int sm_module_init(void) +{ + int error = 0; + cache_flush_workqueue = create_freezeable_workqueue("smflush"); + + if (IS_ERR(cache_flush_workqueue)) + return PTR_ERR(cache_flush_workqueue); + + error = register_mtd_blktrans(&sm_ftl_ops); + if (error) + destroy_workqueue(cache_flush_workqueue); + return error; + +} + +static void __exit sm_module_exit(void) +{ + destroy_workqueue(cache_flush_workqueue); + deregister_mtd_blktrans(&sm_ftl_ops); +} + +module_init(sm_module_init); +module_exit(sm_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky "); +MODULE_DESCRIPTION("Smartmedia/xD mtd translation layer"); diff --git a/drivers/mtd/sm_ftl.h b/drivers/mtd/sm_ftl.h new file mode 100644 index 00000000000..e30e48e7f63 --- /dev/null +++ b/drivers/mtd/sm_ftl.h @@ -0,0 +1,94 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * SmartMedia/xD translation layer + * + * Based loosly on ssfdc.c which is + * © 2005 Eptar srl + * Author: Claudio Lanconelli + * + * 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 + + + +struct ftl_zone { + int initialized; + int16_t *lba_to_phys_table; /* LBA to physical table */ + struct kfifo free_sectors; /* queue of free sectors */ +}; + +struct sm_ftl { + struct mtd_blktrans_dev *trans; + + struct mutex mutex; /* protects the structure */ + struct ftl_zone *zones; /* FTL tables for each zone */ + + /* Media information */ + int block_size; /* block size in bytes */ + int zone_size; /* zone size in blocks */ + int zone_count; /* number of zones */ + int max_lba; /* maximum lba in a zone */ + int smallpagenand; /* 256 bytes/page nand */ + int readonly; /* is FS readonly */ + int unstable; + int cis_block; /* CIS block location */ + int cis_boffset; /* CIS offset in the block */ + int cis_page_offset; /* CIS offset in the page */ + void *cis_buffer; /* tmp buffer for cis reads */ + + /* Cache */ + int cache_block; /* block number of cached block */ + int cache_zone; /* zone of cached block */ + unsigned char *cache_data; /* cached block data */ + long unsigned int cache_data_invalid_bitmap; + int cache_clean; + struct work_struct flush_work; + struct timer_list timer; + + /* Async erase stuff */ + struct completion erase_completion; + + /* Geometry stuff */ + int heads; + int sectors; + int cylinders; + + struct attribute_group *disk_attributes; +}; + +struct chs_entry { + unsigned long size; + unsigned short cyl; + unsigned char head; + unsigned char sec; +}; + + +#define SM_FTL_PARTN_BITS 3 + +#define sm_printk(format, ...) \ + printk(KERN_WARNING "sm_ftl" ": " format "\n", ## __VA_ARGS__) + +#define dbg(format, ...) \ + if (debug) \ + printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__) + +#define dbg_verbose(format, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG "sm_ftl" ": " format "\n", ## __VA_ARGS__) + + +static void sm_erase_callback(struct erase_info *self); +static int sm_erase_block(struct sm_ftl *ftl, int zone_num, uint16_t block, + int put_free); +static void sm_mark_block_bad(struct sm_ftl *ftl, int zone_num, int block); + +static int sm_recheck_media(struct sm_ftl *ftl); -- cgit v1.2.3-70-g09d2 From 67e054e919248fa1db93de727fb9ad49eb700642 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 22 Feb 2010 20:39:42 +0200 Subject: mtd: nand: Add driver for Ricoh xD/SmartMedia reader This adds a driver for Ricoh R5C852 xD card reader. This reader is a part of larger mulifunction chip and found at least in R5C832 Driver is complete, but bewere of the fact that some (probably only type M) xD cards are 'fake' which means that they have an on board CPU and expose emulated nand command set These cards don't even store the oob area on the flash, but generate it on the fly from something else. Thus they demand to have proper values written in the oob area, and therefore only useful with SmartMedia FTL. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- MAINTAINERS | 6 + drivers/mtd/nand/Kconfig | 11 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/r852.c | 1117 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/r852.h | 163 +++++++ 5 files changed, 1298 insertions(+) create mode 100644 drivers/mtd/nand/r852.c create mode 100644 drivers/mtd/nand/r852.h (limited to 'drivers') diff --git a/MAINTAINERS b/MAINTAINERS index 2533fc45a44..ecf4148ec2b 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -4567,6 +4567,12 @@ S: Maintained F: Documentation/rfkill.txt F: net/rfkill/ +RICOH SMARTMEDIA/XD DRIVER +M: Maxim Levitsky +S: Maintained +F: drivers/mtd/nand/r822.c +F: drivers/mtd/nand/r822.h + RISCOM8 DRIVER S: Orphan F: Documentation/serial/riscom8.txt diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 7a67218e86f..6701a00b7a9 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -102,6 +102,17 @@ config MTD_NAND_OMAP_PREFETCH_DMA config MTD_NAND_IDS tristate +config MTD_NAND_RICOH + tristate "Ricoh xD card reader" + default n + select MTD_SM_COMMON + help + Enable support for Ricoh R5C852 xD card reader + You also need to enable ether + NAND SSFDC (SmartMedia) read only translation layer' or new + expermental, readwrite + 'SmartMedia/xD new translation layer' + config MTD_NAND_AU1550 tristate "Au1550/1200 NAND support" depends on SOC_AU1200 || SOC_AU1550 diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index f39d0b6ed42..5fbd1f83afb 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -43,5 +43,6 @@ obj-$(CONFIG_MTD_NAND_NUC900) += nuc900_nand.o obj-$(CONFIG_MTD_NAND_NOMADIK) += nomadik_nand.o obj-$(CONFIG_MTD_NAND_BCM_UMI) += bcm_umi_nand.o nand_bcm_umi.o obj-$(CONFIG_MTD_NAND_MPC5121_NFC) += mpc5121_nfc.o +obj-$(CONFIG_MTD_NAND_RICOH) += r852.o nand-objs := nand_base.o nand_bbt.o diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c new file mode 100644 index 00000000000..9307a88e522 --- /dev/null +++ b/drivers/mtd/nand/r852.c @@ -0,0 +1,1117 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * driver for Ricoh xD readers + * + * 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 "sm_common.h" +#include "r852.h" + + +static int enable_dma = 1; +module_param(enable_dma, bool, S_IRUGO); +MODULE_PARM_DESC(enable_dma, "Enable usage of the DMA (default)"); + +static int debug; +module_param(debug, int, S_IRUGO | S_IWUSR); +MODULE_PARM_DESC(debug, "Debug level (0-2)"); + +/* read register */ +static inline uint8_t r852_read_reg(struct r852_device *dev, int address) +{ + uint8_t reg = readb(dev->mmio + address); + return reg; +} + +/* write register */ +static inline void r852_write_reg(struct r852_device *dev, + int address, uint8_t value) +{ + writeb(value, dev->mmio + address); + mmiowb(); +} + + +/* read dword sized register */ +static inline uint32_t r852_read_reg_dword(struct r852_device *dev, int address) +{ + uint32_t reg = le32_to_cpu(readl(dev->mmio + address)); + return reg; +} + +/* write dword sized register */ +static inline void r852_write_reg_dword(struct r852_device *dev, + int address, uint32_t value) +{ + writel(cpu_to_le32(value), dev->mmio + address); + mmiowb(); +} + +/* returns pointer to our private structure */ +static inline struct r852_device *r852_get_dev(struct mtd_info *mtd) +{ + struct nand_chip *chip = (struct nand_chip *)mtd->priv; + return (struct r852_device *)chip->priv; +} + + +/* check if controller supports dma */ +static void r852_dma_test(struct r852_device *dev) +{ + dev->dma_usable = (r852_read_reg(dev, R852_DMA_CAP) & + (R852_DMA1 | R852_DMA2)) == (R852_DMA1 | R852_DMA2); + + if (!dev->dma_usable) + message("Non dma capable device detected, dma disabled"); + + if (!enable_dma) { + message("disabling dma on user request"); + dev->dma_usable = 0; + } +} + +/* + * Enable dma. Enables ether first or second stage of the DMA, + * Expects dev->dma_dir and dev->dma_state be set + */ +static void r852_dma_enable(struct r852_device *dev) +{ + uint8_t dma_reg, dma_irq_reg; + + /* Set up dma settings */ + dma_reg = r852_read_reg_dword(dev, R852_DMA_SETTINGS); + dma_reg &= ~(R852_DMA_READ | R852_DMA_INTERNAL | R852_DMA_MEMORY); + + if (dev->dma_dir) + dma_reg |= R852_DMA_READ; + + if (dev->dma_state == DMA_INTERNAL) + dma_reg |= R852_DMA_INTERNAL; + else { + dma_reg |= R852_DMA_MEMORY; + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_dma_addr)); + } + + r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg); + + /* Set dma irq */ + dma_irq_reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, + dma_irq_reg | + R852_DMA_IRQ_INTERNAL | + R852_DMA_IRQ_ERROR | + R852_DMA_IRQ_MEMORY); +} + +/* + * Disable dma, called from the interrupt handler, which specifies + * success of the operation via 'error' argument + */ +static void r852_dma_done(struct r852_device *dev, int error) +{ + WARN_ON(dev->dma_stage == 0); + + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, + r852_read_reg_dword(dev, R852_DMA_IRQ_STA)); + + r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0); + + dev->dma_error = error; + dev->dma_stage = 0; + + if (dev->phys_dma_addr && dev->phys_dma_addr != dev->phys_bounce_buffer) + pci_unmap_single(dev->pci_dev, dev->phys_dma_addr, R852_DMA_LEN, + dev->dma_dir ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE); + complete(&dev->dma_done); +} + +/* + * Wait, till dma is done, which includes both phases of it + */ +static int r852_dma_wait(struct r852_device *dev) +{ + long timeout = wait_for_completion_timeout(&dev->dma_done, + msecs_to_jiffies(1000)); + if (!timeout) { + dbg("timeout waiting for DMA interrupt"); + return -ETIMEDOUT; + } + + return 0; +} + +/* + * Read/Write one page using dma. Only pages can be read (512 bytes) +*/ +static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) +{ + int bounce = 0; + unsigned long flags; + int error; + + dev->dma_error = 0; + + /* Set dma direction */ + dev->dma_dir = do_read; + dev->dma_stage = 1; + + dbg_verbose("doing dma %s ", do_read ? "read" : "write"); + + /* Set intial dma state: for reading first fill on board buffer, + from device, for writes first fill the buffer from memory*/ + dev->dma_state = do_read ? DMA_INTERNAL : DMA_MEMORY; + + /* if incoming buffer is not page aligned, we should do bounce */ + if ((unsigned long)buf & (R852_DMA_LEN-1)) + bounce = 1; + + if (!bounce) { + dev->phys_dma_addr = pci_map_single(dev->pci_dev, (void *)buf, + R852_DMA_LEN, + (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE)); + + if (dev->phys_dma_addr == DMA_ERROR_CODE) + bounce = 1; + } + + if (bounce) { + dbg_verbose("dma: using bounce buffer"); + dev->phys_dma_addr = dev->phys_bounce_buffer; + if (!do_read) + memcpy(dev->bounce_buffer, buf, R852_DMA_LEN); + } + + /* Enable DMA */ + spin_lock_irqsave(&dev->irqlock, flags); + r852_dma_enable(dev); + spin_unlock_irqrestore(&dev->irqlock, flags); + + /* Wait till complete */ + error = r852_dma_wait(dev); + + if (error) { + r852_dma_done(dev, error); + return; + } + + if (do_read && bounce) + memcpy((void *)buf, dev->bounce_buffer, R852_DMA_LEN); +} + +/* + * Program data lines of the nand chip to send data to it + */ +void r852_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + uint32_t reg; + + /* Don't allow any access to hardware if we suspect card removal */ + if (dev->card_unstable) + return; + + /* Special case for whole sector read */ + if (len == R852_DMA_LEN && dev->dma_usable) { + r852_do_dma(dev, (uint8_t *)buf, 0); + return; + } + + /* write DWORD chinks - faster */ + while (len) { + reg = buf[0] | buf[1] << 8 | buf[2] << 16 | buf[3] << 24; + r852_write_reg_dword(dev, R852_DATALINE, reg); + buf += 4; + len -= 4; + + } + + /* write rest */ + while (len) + r852_write_reg(dev, R852_DATALINE, *buf++); +} + +/* + * Read data lines of the nand chip to retrieve data + */ +void r852_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + uint32_t reg; + + if (dev->card_unstable) { + /* since we can't signal error here, at least, return + predictable buffer */ + memset(buf, 0, len); + return; + } + + /* special case for whole sector read */ + if (len == R852_DMA_LEN && dev->dma_usable) { + r852_do_dma(dev, buf, 1); + return; + } + + /* read in dword sized chunks */ + while (len >= 4) { + + reg = r852_read_reg_dword(dev, R852_DATALINE); + *buf++ = reg & 0xFF; + *buf++ = (reg >> 8) & 0xFF; + *buf++ = (reg >> 16) & 0xFF; + *buf++ = (reg >> 24) & 0xFF; + len -= 4; + } + + /* read the reset by bytes */ + while (len--) + *buf++ = r852_read_reg(dev, R852_DATALINE); +} + +/* + * Read one byte from nand chip + */ +static uint8_t r852_read_byte(struct mtd_info *mtd) +{ + struct r852_device *dev = r852_get_dev(mtd); + + /* Same problem as in r852_read_buf.... */ + if (dev->card_unstable) + return 0; + + return r852_read_reg(dev, R852_DATALINE); +} + + +/* + * Readback the buffer to verify it + */ +int r852_verify_buf(struct mtd_info *mtd, const uint8_t *buf, int len) +{ + struct r852_device *dev = r852_get_dev(mtd); + + /* We can't be sure about anything here... */ + if (dev->card_unstable) + return -1; + + /* This will never happen, unless you wired up a nand chip + with > 512 bytes page size to the reader */ + if (len > SM_SECTOR_SIZE) + return 0; + + r852_read_buf(mtd, dev->tmp_buffer, len); + return memcmp(buf, dev->tmp_buffer, len); +} + +/* + * Control several chip lines & send commands + */ +void r852_cmdctl(struct mtd_info *mtd, int dat, unsigned int ctrl) +{ + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return; + + if (ctrl & NAND_CTRL_CHANGE) { + + dev->ctlreg &= ~(R852_CTL_DATA | R852_CTL_COMMAND | + R852_CTL_ON | R852_CTL_CARDENABLE); + + if (ctrl & NAND_ALE) + dev->ctlreg |= R852_CTL_DATA; + + if (ctrl & NAND_CLE) + dev->ctlreg |= R852_CTL_COMMAND; + + if (ctrl & NAND_NCE) + dev->ctlreg |= (R852_CTL_CARDENABLE | R852_CTL_ON); + else + dev->ctlreg &= ~R852_CTL_WRITE; + + /* when write is stareted, enable write access */ + if (dat == NAND_CMD_ERASE1) + dev->ctlreg |= R852_CTL_WRITE; + + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } + + /* HACK: NAND_CMD_SEQIN is called without NAND_CTRL_CHANGE, but we need + to set write mode */ + if (dat == NAND_CMD_SEQIN && (dev->ctlreg & R852_CTL_COMMAND)) { + dev->ctlreg |= R852_CTL_WRITE; + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } + + if (dat != NAND_CMD_NONE) + r852_write_reg(dev, R852_DATALINE, dat); +} + +/* + * Wait till card is ready. + * based on nand_wait, but returns errors on DMA error + */ +int r852_wait(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct r852_device *dev = (struct r852_device *)chip->priv; + + unsigned long timeout; + int status; + + timeout = jiffies + (chip->state == FL_ERASING ? + msecs_to_jiffies(400) : msecs_to_jiffies(20)); + + while (time_before(jiffies, timeout)) + if (chip->dev_ready(mtd)) + break; + + chip->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); + status = (int)chip->read_byte(mtd); + + /* Unfortunelly, no way to send detailed error status... */ + if (dev->dma_error) { + status |= NAND_STATUS_FAIL; + dev->dma_error = 0; + } + return status; +} + +/* + * Check if card is ready + */ + +int r852_ready(struct mtd_info *mtd) +{ + struct r852_device *dev = r852_get_dev(mtd); + return !(r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_BUSY); +} + + +/* + * Set ECC engine mode +*/ + +void r852_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return; + + switch (mode) { + case NAND_ECC_READ: + case NAND_ECC_WRITE: + /* enable ecc generation/check*/ + dev->ctlreg |= R852_CTL_ECC_ENABLE; + + /* flush ecc buffer */ + r852_write_reg(dev, R852_CTL, + dev->ctlreg | R852_CTL_ECC_ACCESS); + + r852_read_reg_dword(dev, R852_DATALINE); + r852_write_reg(dev, R852_CTL, dev->ctlreg); + return; + + case NAND_ECC_READSYN: + /* disable ecc generation */ + dev->ctlreg &= ~R852_CTL_ECC_ENABLE; + r852_write_reg(dev, R852_CTL, dev->ctlreg); + } +} + +/* + * Calculate ECC, only used for writes + */ + +int r852_ecc_calculate(struct mtd_info *mtd, const uint8_t *dat, + uint8_t *ecc_code) +{ + struct r852_device *dev = r852_get_dev(mtd); + struct sm_oob *oob = (struct sm_oob *)ecc_code; + uint32_t ecc1, ecc2; + + if (dev->card_unstable) + return 0; + + dev->ctlreg &= ~R852_CTL_ECC_ENABLE; + r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); + + ecc1 = r852_read_reg_dword(dev, R852_DATALINE); + ecc2 = r852_read_reg_dword(dev, R852_DATALINE); + + oob->ecc1[0] = (ecc1) & 0xFF; + oob->ecc1[1] = (ecc1 >> 8) & 0xFF; + oob->ecc1[2] = (ecc1 >> 16) & 0xFF; + + oob->ecc2[0] = (ecc2) & 0xFF; + oob->ecc2[1] = (ecc2 >> 8) & 0xFF; + oob->ecc2[2] = (ecc2 >> 16) & 0xFF; + + r852_write_reg(dev, R852_CTL, dev->ctlreg); + return 0; +} + +/* + * Correct the data using ECC, hw did almost everything for us + */ + +int r852_ecc_correct(struct mtd_info *mtd, uint8_t *dat, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + uint16_t ecc_reg; + uint8_t ecc_status, err_byte; + int i, error = 0; + + struct r852_device *dev = r852_get_dev(mtd); + + if (dev->card_unstable) + return 0; + + r852_write_reg(dev, R852_CTL, dev->ctlreg | R852_CTL_ECC_ACCESS); + ecc_reg = r852_read_reg_dword(dev, R852_DATALINE); + r852_write_reg(dev, R852_CTL, dev->ctlreg); + + for (i = 0 ; i <= 1 ; i++) { + + ecc_status = (ecc_reg >> 8) & 0xFF; + + /* ecc uncorrectable error */ + if (ecc_status & R852_ECC_FAIL) { + dbg("ecc: unrecoverable error, in half %d", i); + error = -1; + goto exit; + } + + /* correctable error */ + if (ecc_status & R852_ECC_CORRECTABLE) { + + err_byte = ecc_reg & 0xFF; + dbg("ecc: recoverable error, " + "in half %d, byte %d, bit %d", i, + err_byte, ecc_status & R852_ECC_ERR_BIT_MSK); + + dat[err_byte] ^= + 1 << (ecc_status & R852_ECC_ERR_BIT_MSK); + error++; + } + + dat += 256; + ecc_reg >>= 16; + } +exit: + return error; +} + +/* + * This is copy of nand_read_oob_std + * nand_read_oob_syndrome assumes we can send column address - we can't + */ +static int r852_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + if (sndcmd) { + chip->cmdfunc(mtd, NAND_CMD_READOOB, 0, page); + sndcmd = 0; + } + chip->read_buf(mtd, chip->oob_poi, mtd->oobsize); + return sndcmd; +} + +/* + * Start the nand engine + */ + +void r852_engine_enable(struct r852_device *dev) +{ + if (r852_read_reg_dword(dev, R852_HW) & R852_HW_UNKNOWN) { + r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); + r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); + } else { + r852_write_reg_dword(dev, R852_HW, R852_HW_ENABLED); + r852_write_reg(dev, R852_CTL, R852_CTL_RESET | R852_CTL_ON); + } + msleep(300); + r852_write_reg(dev, R852_CTL, 0); +} + + +/* + * Stop the nand engine + */ + +void r852_engine_disable(struct r852_device *dev) +{ + r852_write_reg_dword(dev, R852_HW, 0); + r852_write_reg(dev, R852_CTL, R852_CTL_RESET); +} + +/* + * Test if card is present + */ + +void r852_card_update_present(struct r852_device *dev) +{ + unsigned long flags; + uint8_t reg; + + spin_lock_irqsave(&dev->irqlock, flags); + reg = r852_read_reg(dev, R852_CARD_STA); + dev->card_detected = !!(reg & R852_CARD_STA_PRESENT); + spin_unlock_irqrestore(&dev->irqlock, flags); +} + +/* + * Update card detection IRQ state according to current card state + * which is read in r852_card_update_present + */ +void r852_update_card_detect(struct r852_device *dev) +{ + int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); + + card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT); + card_detect_reg |= R852_CARD_IRQ_GENABLE; + + card_detect_reg |= dev->card_detected ? + R852_CARD_IRQ_REMOVE : R852_CARD_IRQ_INSERT; + + r852_write_reg(dev, R852_CARD_IRQ_ENABLE, card_detect_reg); +} + +ssize_t r852_media_type_show(struct device *sys_dev, + struct device_attribute *attr, char *buf) +{ + struct mtd_info *mtd = container_of(sys_dev, struct mtd_info, dev); + struct r852_device *dev = r852_get_dev(mtd); + char *data = dev->sm ? "smartmedia" : "xd"; + + strcpy(buf, data); + return strlen(data); +} + +DEVICE_ATTR(media_type, S_IRUGO, r852_media_type_show, NULL); + + +/* Detect properties of card in slot */ +void r852_update_media_status(struct r852_device *dev) +{ + uint8_t reg; + unsigned long flags; + int readonly; + + spin_lock_irqsave(&dev->irqlock, flags); + if (!dev->card_detected) { + message("card removed"); + spin_unlock_irqrestore(&dev->irqlock, flags); + return ; + } + + readonly = r852_read_reg(dev, R852_CARD_STA) & R852_CARD_STA_RO; + reg = r852_read_reg(dev, R852_DMA_CAP); + dev->sm = (reg & (R852_DMA1 | R852_DMA2)) && (reg & R852_SMBIT); + + message("detected %s %s card in slot", + dev->sm ? "SmartMedia" : "xD", + readonly ? "readonly" : "writeable"); + + dev->readonly = readonly; + spin_unlock_irqrestore(&dev->irqlock, flags); +} + +/* + * Register the nand device + * Called when the card is detected + */ +int r852_register_nand_device(struct r852_device *dev) +{ + dev->mtd = kzalloc(sizeof(struct mtd_info), GFP_KERNEL); + + if (!dev->mtd) + goto error1; + + WARN_ON(dev->card_registred); + + dev->mtd->owner = THIS_MODULE; + dev->mtd->priv = dev->chip; + dev->mtd->dev.parent = &dev->pci_dev->dev; + + if (dev->readonly) + dev->chip->options |= NAND_ROM; + + r852_engine_enable(dev); + + if (sm_register_device(dev->mtd)) + goto error2; + + device_create_file(&dev->mtd->dev, &dev_attr_media_type); + dev->card_registred = 1; + return 0; +error2: + kfree(dev->mtd); +error1: + /* Force card redetect */ + dev->card_detected = 0; + return -1; +} + +/* + * Unregister the card + */ + +void r852_unregister_nand_device(struct r852_device *dev) +{ + if (!dev->card_registred) + return; + + device_remove_file(&dev->mtd->dev, &dev_attr_media_type); + nand_release(dev->mtd); + r852_engine_disable(dev); + dev->card_registred = 0; + kfree(dev->mtd); + dev->mtd = NULL; +} + +/* Card state updater */ +void r852_card_detect_work(struct work_struct *work) +{ + struct r852_device *dev = + container_of(work, struct r852_device, card_detect_work.work); + + r852_update_card_detect(dev); + dev->card_unstable = 0; + + /* false alarm */ + if (dev->card_detected == dev->card_registred) + goto exit; + + /* Read media properties */ + r852_update_media_status(dev); + + /* Register the card */ + if (dev->card_detected) + r852_register_nand_device(dev); + else + r852_unregister_nand_device(dev); +exit: + /* Update detection logic */ + r852_update_card_detect(dev); +} + +/* Ack + disable IRQ generation */ +static void r852_disable_irqs(struct r852_device *dev) +{ + uint8_t reg; + reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); + r852_write_reg(dev, R852_CARD_IRQ_ENABLE, reg & ~R852_CARD_IRQ_MASK); + + reg = r852_read_reg_dword(dev, R852_DMA_IRQ_ENABLE); + r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, + reg & ~R852_DMA_IRQ_MASK); + + r852_write_reg(dev, R852_CARD_IRQ_STA, R852_CARD_IRQ_MASK); + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, R852_DMA_IRQ_MASK); +} + +/* Interrupt handler */ +static irqreturn_t r852_irq(int irq, void *data) +{ + struct r852_device *dev = (struct r852_device *)data; + + uint8_t card_status, dma_status; + unsigned long flags; + irqreturn_t ret = IRQ_NONE; + + spin_lock_irqsave(&dev->irqlock, flags); + + /* We can recieve shared interrupt while pci is suspended + in that case reads will return 0xFFFFFFFF.... */ + if (dev->insuspend) + goto out; + + /* handle card detection interrupts first */ + card_status = r852_read_reg(dev, R852_CARD_IRQ_STA); + r852_write_reg(dev, R852_CARD_IRQ_STA, card_status); + + if (card_status & (R852_CARD_IRQ_INSERT|R852_CARD_IRQ_REMOVE)) { + + ret = IRQ_HANDLED; + dev->card_detected = !!(card_status & R852_CARD_IRQ_INSERT); + + /* we shouldn't recieve any interrupts if we wait for card + to settle */ + WARN_ON(dev->card_unstable); + + /* disable irqs while card is unstable */ + /* this will timeout DMA if active, but better that garbage */ + r852_disable_irqs(dev); + + if (dev->card_unstable) + goto out; + + /* let, card state to settle a bit, and then do the work */ + dev->card_unstable = 1; + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, msecs_to_jiffies(100)); + goto out; + } + + + /* Handle dma interrupts */ + dma_status = r852_read_reg_dword(dev, R852_DMA_IRQ_STA); + r852_write_reg_dword(dev, R852_DMA_IRQ_STA, dma_status); + + if (dma_status & R852_DMA_IRQ_MASK) { + + ret = IRQ_HANDLED; + + if (dma_status & R852_DMA_IRQ_ERROR) { + dbg("recieved dma error IRQ"); + r852_dma_done(dev, -EIO); + goto out; + } + + /* recieved DMA interrupt out of nowhere? */ + WARN_ON_ONCE(dev->dma_stage == 0); + + if (dev->dma_stage == 0) + goto out; + + /* done device access */ + if (dev->dma_state == DMA_INTERNAL && + (dma_status & R852_DMA_IRQ_INTERNAL)) { + + dev->dma_state = DMA_MEMORY; + dev->dma_stage++; + } + + /* done memory DMA */ + if (dev->dma_state == DMA_MEMORY && + (dma_status & R852_DMA_IRQ_MEMORY)) { + dev->dma_state = DMA_INTERNAL; + dev->dma_stage++; + } + + /* Enable 2nd half of dma dance */ + if (dev->dma_stage == 2) + r852_dma_enable(dev); + + /* Operation done */ + if (dev->dma_stage == 3) + r852_dma_done(dev, 0); + goto out; + } + + /* Handle unknown interrupts */ + if (dma_status) + dbg("bad dma IRQ status = %x", dma_status); + + if (card_status & ~R852_CARD_STA_CD) + dbg("strange card status = %x", card_status); + +out: + spin_unlock_irqrestore(&dev->irqlock, flags); + return ret; +} + +int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) +{ + int error; + struct nand_chip *chip; + struct r852_device *dev; + + /* pci initialization */ + error = pci_enable_device(pci_dev); + + if (error) + goto error1; + + pci_set_master(pci_dev); + + error = pci_set_dma_mask(pci_dev, DMA_32BIT_MASK); + if (error) + goto error2; + + error = pci_request_regions(pci_dev, DRV_NAME); + + if (error) + goto error3; + + error = -ENOMEM; + + /* init nand chip, but register it only on card insert */ + chip = kzalloc(sizeof(struct nand_chip), GFP_KERNEL); + + if (!chip) + goto error4; + + /* commands */ + chip->cmd_ctrl = r852_cmdctl; + chip->waitfunc = r852_wait; + chip->dev_ready = r852_ready; + + /* I/O */ + chip->read_byte = r852_read_byte; + chip->read_buf = r852_read_buf; + chip->write_buf = r852_write_buf; + chip->verify_buf = r852_verify_buf; + + /* ecc */ + chip->ecc.mode = NAND_ECC_HW_SYNDROME; + chip->ecc.size = R852_DMA_LEN; + chip->ecc.bytes = SM_OOB_SIZE; + chip->ecc.hwctl = r852_ecc_hwctl; + chip->ecc.calculate = r852_ecc_calculate; + chip->ecc.correct = r852_ecc_correct; + + /* TODO: hack */ + chip->ecc.read_oob = r852_read_oob; + + /* init our device structure */ + dev = kzalloc(sizeof(struct r852_device), GFP_KERNEL); + + if (!dev) + goto error5; + + chip->priv = dev; + dev->chip = chip; + dev->pci_dev = pci_dev; + pci_set_drvdata(pci_dev, dev); + + dev->bounce_buffer = pci_alloc_consistent(pci_dev, R852_DMA_LEN, + &dev->phys_bounce_buffer); + + if (!dev->bounce_buffer) + goto error6; + + + error = -ENODEV; + dev->mmio = pci_ioremap_bar(pci_dev, 0); + + if (!dev->mmio) + goto error7; + + error = -ENOMEM; + dev->tmp_buffer = kzalloc(SM_SECTOR_SIZE, GFP_KERNEL); + + if (!dev->tmp_buffer) + goto error8; + + init_completion(&dev->dma_done); + + dev->card_workqueue = create_freezeable_workqueue(DRV_NAME); + + if (!dev->card_workqueue) + goto error9; + + INIT_DELAYED_WORK(&dev->card_detect_work, r852_card_detect_work); + + /* shutdown everything - precation */ + r852_engine_disable(dev); + r852_disable_irqs(dev); + + r852_dma_test(dev); + + /*register irq handler*/ + error = -ENODEV; + if (request_irq(pci_dev->irq, &r852_irq, IRQF_SHARED, + DRV_NAME, dev)) + goto error10; + + dev->irq = pci_dev->irq; + spin_lock_init(&dev->irqlock); + + /* kick initial present test */ + dev->card_detected = 0; + r852_card_update_present(dev); + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, 0); + + + printk(KERN_NOTICE DRV_NAME ": driver loaded succesfully\n"); + return 0; + +error10: + destroy_workqueue(dev->card_workqueue); +error9: + kfree(dev->tmp_buffer); +error8: + pci_iounmap(pci_dev, dev->mmio); +error7: + pci_free_consistent(pci_dev, R852_DMA_LEN, + dev->bounce_buffer, dev->phys_bounce_buffer); +error6: + kfree(dev); +error5: + kfree(chip); +error4: + pci_release_regions(pci_dev); +error3: +error2: + pci_disable_device(pci_dev); +error1: + return error; +} + +void r852_remove(struct pci_dev *pci_dev) +{ + struct r852_device *dev = pci_get_drvdata(pci_dev); + + /* Stop detect workqueue - + we are going to unregister the device anyway*/ + cancel_delayed_work_sync(&dev->card_detect_work); + destroy_workqueue(dev->card_workqueue); + + /* Unregister the device, this might make more IO */ + r852_unregister_nand_device(dev); + + /* Stop interrupts */ + r852_disable_irqs(dev); + synchronize_irq(dev->irq); + free_irq(dev->irq, dev); + + /* Cleanup */ + kfree(dev->tmp_buffer); + pci_iounmap(pci_dev, dev->mmio); + pci_free_consistent(pci_dev, R852_DMA_LEN, + dev->bounce_buffer, dev->phys_bounce_buffer); + + kfree(dev->chip); + kfree(dev); + + /* Shutdown the PCI device */ + pci_release_regions(pci_dev); + pci_disable_device(pci_dev); +} + +void r852_shutdown(struct pci_dev *pci_dev) +{ + struct r852_device *dev = pci_get_drvdata(pci_dev); + + cancel_delayed_work_sync(&dev->card_detect_work); + r852_disable_irqs(dev); + synchronize_irq(dev->irq); + pci_disable_device(pci_dev); +} + +int r852_suspend(struct device *device) +{ + struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + unsigned long flags; + + if (dev->ctlreg & R852_CTL_CARDENABLE) + return -EBUSY; + + /* First make sure the detect work is gone */ + cancel_delayed_work_sync(&dev->card_detect_work); + + /* Turn off the interrupts and stop the device */ + r852_disable_irqs(dev); + r852_engine_disable(dev); + + spin_lock_irqsave(&dev->irqlock, flags); + dev->insuspend = 1; + spin_unlock_irqrestore(&dev->irqlock, flags); + + /* At that point, even if interrupt handler is running, it will quit */ + /* So wait for this to happen explictly */ + synchronize_irq(dev->irq); + + /* If card was pulled off just during the suspend, which is very + unlikely, we will remove it on resume, it too late now + anyway... */ + dev->card_unstable = 0; + + pci_save_state(to_pci_dev(device)); + return pci_prepare_to_sleep(to_pci_dev(device)); +} + +int r852_resume(struct device *device) +{ + struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); + unsigned long flags; + + /* Turn on the hardware */ + pci_back_from_sleep(to_pci_dev(device)); + pci_restore_state(to_pci_dev(device)); + + r852_disable_irqs(dev); + r852_card_update_present(dev); + r852_engine_disable(dev); + + + /* Now its safe for IRQ to run */ + spin_lock_irqsave(&dev->irqlock, flags); + dev->insuspend = 0; + spin_unlock_irqrestore(&dev->irqlock, flags); + + + /* If card status changed, just do the work */ + if (dev->card_detected != dev->card_registred) { + dbg("card was %s during low power state", + dev->card_detected ? "added" : "removed"); + + queue_delayed_work(dev->card_workqueue, + &dev->card_detect_work, 1000); + return 0; + } + + /* Otherwise, initialize the card */ + if (dev->card_registred) { + r852_engine_enable(dev); + dev->chip->select_chip(dev->mtd, 0); + dev->chip->cmdfunc(dev->mtd, NAND_CMD_RESET, -1, -1); + dev->chip->select_chip(dev->mtd, -1); + } + + /* Program card detection IRQ */ + r852_update_card_detect(dev); + return 0; +} + +static const struct pci_device_id r852_pci_id_tbl[] = { + + { PCI_VDEVICE(RICOH, PCI_DEVICE_ID_RICOH_R5C852), }, + { }, +}; + +MODULE_DEVICE_TABLE(pci, r852_pci_id_tbl); + +SIMPLE_DEV_PM_OPS(r852_pm_ops, r852_suspend, r852_resume); + + +static struct pci_driver r852_pci_driver = { + .name = DRV_NAME, + .id_table = r852_pci_id_tbl, + .probe = r852_probe, + .remove = r852_remove, + .shutdown = r852_shutdown, + .driver.pm = &r852_pm_ops, +}; + +static __init int r852_module_init(void) +{ + return pci_register_driver(&r852_pci_driver); +} + +static void __exit r852_module_exit(void) +{ + pci_unregister_driver(&r852_pci_driver); +} + +module_init(r852_module_init); +module_exit(r852_module_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Maxim Levitsky "); +MODULE_DESCRIPTION("Ricoh 85xx xD/smartmedia card reader driver"); diff --git a/drivers/mtd/nand/r852.h b/drivers/mtd/nand/r852.h new file mode 100644 index 00000000000..8096cc280c7 --- /dev/null +++ b/drivers/mtd/nand/r852.h @@ -0,0 +1,163 @@ +/* + * Copyright © 2009 - Maxim Levitsky + * driver for Ricoh xD readers + * + * 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 + + +/* nand interface + ecc + byte write/read does one cycle on nand data lines. + dword write/read does 4 cycles + if R852_CTL_ECC_ACCESS is set in R852_CTL, then dword read reads + results of ecc correction, if DMA read was done before. + If write was done two dword reads read generated ecc checksums +*/ +#define R852_DATALINE 0x00 + +/* control register */ +#define R852_CTL 0x04 +#define R852_CTL_COMMAND 0x01 /* send command (#CLE)*/ +#define R852_CTL_DATA 0x02 /* read/write data (#ALE)*/ +#define R852_CTL_ON 0x04 /* only seem to controls the hd led, */ + /* but has to be set on start...*/ +#define R852_CTL_RESET 0x08 /* unknown, set only on start once*/ +#define R852_CTL_CARDENABLE 0x10 /* probably (#CE) - always set*/ +#define R852_CTL_ECC_ENABLE 0x20 /* enable ecc engine */ +#define R852_CTL_ECC_ACCESS 0x40 /* read/write ecc via reg #0*/ +#define R852_CTL_WRITE 0x80 /* set when performing writes (#WP) */ + +/* card detection status */ +#define R852_CARD_STA 0x05 + +#define R852_CARD_STA_CD 0x01 /* state of #CD line, same as 0x04 */ +#define R852_CARD_STA_RO 0x02 /* card is readonly */ +#define R852_CARD_STA_PRESENT 0x04 /* card is present (#CD) */ +#define R852_CARD_STA_ABSENT 0x08 /* card is absent */ +#define R852_CARD_STA_BUSY 0x80 /* card is busy - (#R/B) */ + +/* card detection irq status & enable*/ +#define R852_CARD_IRQ_STA 0x06 /* IRQ status */ +#define R852_CARD_IRQ_ENABLE 0x07 /* IRQ enable */ + +#define R852_CARD_IRQ_CD 0x01 /* fire when #CD lights, same as 0x04*/ +#define R852_CARD_IRQ_REMOVE 0x04 /* detect card removal */ +#define R852_CARD_IRQ_INSERT 0x08 /* detect card insert */ +#define R852_CARD_IRQ_UNK1 0x10 /* unknown */ +#define R852_CARD_IRQ_GENABLE 0x80 /* general enable */ +#define R852_CARD_IRQ_MASK 0x1D + + + +/* hardware enable */ +#define R852_HW 0x08 +#define R852_HW_ENABLED 0x01 /* hw enabled */ +#define R852_HW_UNKNOWN 0x80 + + +/* dma capabilities */ +#define R852_DMA_CAP 0x09 +#define R852_SMBIT 0x20 /* if set with bit #6 or bit #7, then */ + /* hw is smartmedia */ +#define R852_DMA1 0x40 /* if set w/bit #7, dma is supported */ +#define R852_DMA2 0x80 /* if set w/bit #6, dma is supported */ + + +/* physical DMA address - 32 bit value*/ +#define R852_DMA_ADDR 0x0C + + +/* dma settings */ +#define R852_DMA_SETTINGS 0x10 +#define R852_DMA_MEMORY 0x01 /* (memory <-> internal hw buffer) */ +#define R852_DMA_READ 0x02 /* 0 = write, 1 = read */ +#define R852_DMA_INTERNAL 0x04 /* (internal hw buffer <-> card) */ + +/* dma IRQ status */ +#define R852_DMA_IRQ_STA 0x14 + +/* dma IRQ enable */ +#define R852_DMA_IRQ_ENABLE 0x18 + +#define R852_DMA_IRQ_MEMORY 0x01 /* (memory <-> internal hw buffer) */ +#define R852_DMA_IRQ_ERROR 0x02 /* error did happen */ +#define R852_DMA_IRQ_INTERNAL 0x04 /* (internal hw buffer <-> card) */ +#define R852_DMA_IRQ_MASK 0x07 /* mask of all IRQ bits */ + + +/* ECC syndrome format - read from reg #0 will return two copies of these for + each half of the page. + first byte is error byte location, and second, bit location + flags */ +#define R852_ECC_ERR_BIT_MSK 0x07 /* error bit location */ +#define R852_ECC_CORRECT 0x10 /* no errors - (guessed) */ +#define R852_ECC_CORRECTABLE 0x20 /* correctable error exist */ +#define R852_ECC_FAIL 0x40 /* non correctable error detected */ + +#define R852_DMA_LEN 512 + +#define DMA_INTERNAL 0 +#define DMA_MEMORY 1 + +struct r852_device { + void __iomem *mmio; /* mmio */ + struct mtd_info *mtd; /* mtd backpointer */ + struct nand_chip *chip; /* nand chip backpointer */ + struct pci_dev *pci_dev; /* pci backpointer */ + + /* dma area */ + dma_addr_t phys_dma_addr; /* bus address of buffer*/ + struct completion dma_done; /* data transfer done */ + + dma_addr_t phys_bounce_buffer; /* bus address of bounce buffer */ + uint8_t *bounce_buffer; /* virtual address of bounce buffer */ + + int dma_dir; /* 1 = read, 0 = write */ + int dma_stage; /* 0 - idle, 1 - first step, + 2 - second step */ + + int dma_state; /* 0 = internal, 1 = memory */ + int dma_error; /* dma errors */ + int dma_usable; /* is it possible to use dma */ + + /* card status area */ + struct delayed_work card_detect_work; + struct workqueue_struct *card_workqueue; + int card_registred; /* card registered with mtd */ + int card_detected; /* card detected in slot */ + int card_unstable; /* whenever the card is inserted, + is not known yet */ + int readonly; /* card is readonly */ + int sm; /* Is card smartmedia */ + + /* interrupt handling */ + spinlock_t irqlock; /* IRQ protecting lock */ + int irq; /* irq num */ + int insuspend; /* device is suspended */ + + /* misc */ + void *tmp_buffer; /* temporary buffer */ + uint8_t ctlreg; /* cached contents of control reg */ +}; + +#define DRV_NAME "r852" + + +#define dbg(format, ...) \ + if (debug) \ + printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + +#define dbg_verbose(format, ...) \ + if (debug > 1) \ + printk(KERN_DEBUG DRV_NAME ": " format "\n", ## __VA_ARGS__) + + +#define message(format, ...) \ + printk(KERN_INFO DRV_NAME ": " format "\n", ## __VA_ARGS__) -- cgit v1.2.3-70-g09d2 From 133fa8c7d70d16b07db3a3d87ea18291db8f8ebf Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 26 Feb 2010 22:08:40 +0200 Subject: mtd: Few follow up cleanups for Smartmedia/xD support * Test results of few functions that were declared with __must_check * Fix bogus gcc warning about uinitialized variable 'ret' * Remove unused variable from mtdblock_remove_dev * Don't use deprecated DMA_32BIT_MASK Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 6 ++++-- drivers/mtd/mtdblock.c | 1 - drivers/mtd/nand/r852.c | 6 ++++-- drivers/mtd/sm_ftl.c | 17 ++++++++++------- 4 files changed, 18 insertions(+), 12 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index 9dd23d6acbb..e32c49cb400 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -380,9 +380,11 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) add_disk(gd); - if (new->disk_attributes) - sysfs_create_group(&disk_to_dev(gd)->kobj, + if (new->disk_attributes) { + ret = sysfs_create_group(&disk_to_dev(gd)->kobj, new->disk_attributes); + WARN_ON(ret); + } return 0; error4: module_put(tr->owner); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 7ce30a239ad..e6edbec609f 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -354,7 +354,6 @@ static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { - struct mtdblk_dev *mtdblk = container_of(dev, struct mtdblk_dev, mbd); del_mtd_blktrans_dev(dev); } diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 9307a88e522..7a616a926ee 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -654,7 +654,9 @@ int r852_register_nand_device(struct r852_device *dev) if (sm_register_device(dev->mtd)) goto error2; - device_create_file(&dev->mtd->dev, &dev_attr_media_type); + if (device_create_file(&dev->mtd->dev, &dev_attr_media_type)) + message("can't create media type sysfs attribute"); + dev->card_registred = 1; return 0; error2: @@ -838,7 +840,7 @@ int r852_probe(struct pci_dev *pci_dev, const struct pci_device_id *id) pci_set_master(pci_dev); - error = pci_set_dma_mask(pci_dev, DMA_32BIT_MASK); + error = pci_set_dma_mask(pci_dev, DMA_BIT_MASK(32)); if (error) goto error2; diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index a59ebb48cae..9fb56c76ae8 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -228,7 +228,7 @@ static int sm_read_sector(struct sm_ftl *ftl, struct mtd_info *mtd = ftl->trans->mtd; struct mtd_oob_ops ops; struct sm_oob tmp_oob; - int ret; + int ret = -EIO; int try = 0; /* FTL can contain -1 entries that are by default filled with bits */ @@ -753,6 +753,7 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num) uint16_t block; int lba; int i = 0; + int len; dbg("initializing zone %d", zone_num); @@ -856,7 +857,9 @@ static int sm_init_zone(struct sm_ftl *ftl, int zone_num) i %= (kfifo_len(&zone->free_sectors) / 2); while (i--) { - kfifo_out(&zone->free_sectors, (unsigned char *)&block, 2); + len = kfifo_out(&zone->free_sectors, + (unsigned char *)&block, 2); + WARN_ON(len != 2); kfifo_in(&zone->free_sectors, (const unsigned char *)&block, 2); } return 0; @@ -947,17 +950,17 @@ restart: if (ftl->unstable) return -EIO; - /* No spare blocks */ - /* We could still continue by erasing the current block, + + /* If there are no spare blocks, */ + /* we could still continue by erasing/writing the current block, but for such worn out media it doesn't worth the trouble, and the dangers */ - - if (!kfifo_len(&zone->free_sectors)) { + if (kfifo_out(&zone->free_sectors, + (unsigned char *)&write_sector, 2) != 2) { dbg("no free sectors for write!"); return -EIO; } - kfifo_out(&zone->free_sectors, (unsigned char *)&write_sector, 2); if (sm_write_block(ftl, ftl->cache_data, zone_num, write_sector, ftl->cache_block, ftl->cache_data_invalid_bitmap)) -- cgit v1.2.3-70-g09d2 From d4080cb32ee3d2ed18aa69ffde6010524bd686cd Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 26 Feb 2010 23:10:32 +0200 Subject: mtd: r852 fix pci ID The PCI_DEVICE_ID_RICOH_R5C852 was missed in the edited commit, and on second thought I just open code it. This fixes compile error. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/r852.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 7a616a926ee..218a42dadff 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -1083,7 +1083,7 @@ int r852_resume(struct device *device) static const struct pci_device_id r852_pci_id_tbl[] = { - { PCI_VDEVICE(RICOH, PCI_DEVICE_ID_RICOH_R5C852), }, + { PCI_VDEVICE(RICOH, 0x0852), }, { }, }; -- cgit v1.2.3-70-g09d2 From fb45d3232c641bfee5000c8a81e2005903734702 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Sat, 27 Feb 2010 02:04:02 +0200 Subject: mtd: r852: Few fixes for problems that occur when card is rapidly inserted/removed. First don't enable card detection logic to early. Second be very careful with DMA engine, to be sure it doesn't write to kernel memory driver doesn't own. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/r852.c | 21 +++++++++++++++++---- 1 file changed, 17 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 218a42dadff..cb271167b4a 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -96,14 +96,21 @@ static void r852_dma_enable(struct r852_device *dev) if (dev->dma_dir) dma_reg |= R852_DMA_READ; - if (dev->dma_state == DMA_INTERNAL) + if (dev->dma_state == DMA_INTERNAL) { dma_reg |= R852_DMA_INTERNAL; - else { + /* Precaution to make sure HW doesn't write */ + /* to random kernel memory */ + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_bounce_buffer)); + } else { dma_reg |= R852_DMA_MEMORY; r852_write_reg_dword(dev, R852_DMA_ADDR, cpu_to_le32(dev->phys_dma_addr)); } + /* Precaution: make sure write reached the device */ + r852_read_reg_dword(dev, R852_DMA_ADDR); + r852_write_reg_dword(dev, R852_DMA_SETTINGS, dma_reg); /* Set dma irq */ @@ -129,6 +136,11 @@ static void r852_dma_done(struct r852_device *dev, int error) r852_write_reg_dword(dev, R852_DMA_SETTINGS, 0); r852_write_reg_dword(dev, R852_DMA_IRQ_ENABLE, 0); + /* Precaution to make sure HW doesn't write to random kernel memory */ + r852_write_reg_dword(dev, R852_DMA_ADDR, + cpu_to_le32(dev->phys_bounce_buffer)); + r852_read_reg_dword(dev, R852_DMA_ADDR); + dev->dma_error = error; dev->dma_stage = 0; @@ -579,6 +591,7 @@ void r852_card_update_present(struct r852_device *dev) void r852_update_card_detect(struct r852_device *dev) { int card_detect_reg = r852_read_reg(dev, R852_CARD_IRQ_ENABLE); + dev->card_unstable = 0; card_detect_reg &= ~(R852_CARD_IRQ_REMOVE | R852_CARD_IRQ_INSERT); card_detect_reg |= R852_CARD_IRQ_GENABLE; @@ -690,10 +703,10 @@ void r852_card_detect_work(struct work_struct *work) struct r852_device *dev = container_of(work, struct r852_device, card_detect_work.work); - r852_update_card_detect(dev); + r852_card_update_present(dev); dev->card_unstable = 0; - /* false alarm */ + /* False alarm */ if (dev->card_detected == dev->card_registred) goto exit; -- cgit v1.2.3-70-g09d2 From b2aaf7a2b476820cf5fd7738e8641ed88046acf5 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 1 Mar 2010 13:54:19 -0800 Subject: mtd/nand/r852: fix build for CONFIG_PM=n Fix r852 build for the case of CONFIG_PM=n. drivers/mtd/nand/r852.c:1039: error: implicit declaration of function 'pci_prepare_to_sleep' drivers/mtd/nand/r852.c:1048: error: implicit declaration of function 'pci_back_from_sleep' This patch leaves r852_pm_ops untouched. Signed-off-by: Randy Dunlap Acked-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/r852.c | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index cb271167b4a..96db476ce3e 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -1019,6 +1019,7 @@ void r852_shutdown(struct pci_dev *pci_dev) pci_disable_device(pci_dev); } +#ifdef CONFIG_PM int r852_suspend(struct device *device) { struct r852_device *dev = pci_get_drvdata(to_pci_dev(device)); @@ -1093,6 +1094,10 @@ int r852_resume(struct device *device) r852_update_card_detect(dev); return 0; } +#else +#define r852_suspend NULL +#define r852_resume NULL +#endif static const struct pci_device_id r852_pci_id_tbl[] = { -- cgit v1.2.3-70-g09d2 From ada496578850cb063ccf64d43a293cfcc9d32bf8 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 1 Mar 2010 20:21:06 +1100 Subject: mtd: nand: r852: fix name space clash and include delay.h for msleep(). Signed-off-by: Stephen Rothwell Signed-off-by: David Woodhouse --- drivers/mtd/nand/r852.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 96db476ce3e..f5a0bc7adde 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -13,15 +13,16 @@ #include #include #include +#include #include #include #include "sm_common.h" #include "r852.h" -static int enable_dma = 1; -module_param(enable_dma, bool, S_IRUGO); -MODULE_PARM_DESC(enable_dma, "Enable usage of the DMA (default)"); +static int r852_enable_dma = 1; +module_param(r852_enable_dma, bool, S_IRUGO); +MODULE_PARM_DESC(r852_enable_dma, "Enable usage of the DMA (default)"); static int debug; module_param(debug, int, S_IRUGO | S_IWUSR); @@ -75,7 +76,7 @@ static void r852_dma_test(struct r852_device *dev) if (!dev->dma_usable) message("Non dma capable device detected, dma disabled"); - if (!enable_dma) { + if (!r852_enable_dma) { message("disabling dma on user request"); dev->dma_usable = 0; } -- cgit v1.2.3-70-g09d2 From 1f6ca0d6213278f8608c7e342e423ec0c0198040 Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 1 Mar 2010 20:44:57 +1100 Subject: mtd: nand: r852: declare inline functions static Signed-off-by: Stephen Rothwell Signed-off-by: David Woodhouse --- drivers/mtd/nand/sm_common.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h index 7c03314bdac..18284f5fae6 100644 --- a/drivers/mtd/nand/sm_common.h +++ b/drivers/mtd/nand/sm_common.h @@ -39,17 +39,17 @@ struct sm_oob { extern int sm_register_device(struct mtd_info *mtd); -inline int sm_sector_valid(struct sm_oob *oob) +static inline int sm_sector_valid(struct sm_oob *oob) { return hweight16(oob->data_status) >= 5; } -inline int sm_block_valid(struct sm_oob *oob) +static inline int sm_block_valid(struct sm_oob *oob) { return hweight16(oob->block_status) >= 7; } -inline int sm_block_erased(struct sm_oob *oob) +static inline int sm_block_erased(struct sm_oob *oob) { static const uint32_t erased_pattern[4] = { 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF }; -- cgit v1.2.3-70-g09d2 From 7de6f798e13093536b6cb229213db2fab6e6555f Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 8 Mar 2010 18:45:00 -0800 Subject: mtd: Make SM_FTL depend on MTD_NAND Randy Dunlap observed a build problem with the following config: CONFIG_SM_FTL=y CONFIG_SM_FTL_MUSEUM=y CONFIG_MTD_NAND=m The ECC calculation routines are then built as a module, but referenced by the sm_ftl code in the kernel, resulting in a build failure. The simple fix is to make CONFIG_SM_FTL depend on MTD_NAND unconditionally -- it's pointless without hardware support anyway. Fix some typos which Randy pointed out, too. Reported-By: Randy Dunlap Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 8a912406433..dbee14d3722 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -307,22 +307,22 @@ config SSFDC config SM_FTL tristate "SmartMedia/xD new translation layer" - depends on EXPERIMENTAL && BLOCK + depends on EXPERIMENTAL && BLOCK && MTD_NAND select MTD_BLKDEVS help This enables new and very EXPERMENTAL support for SmartMedia/xD - FTL (Flash tanslation layer) + FTL (Flash translation layer). Write support isn't yet well tested, therefore this code IS likely to eat your card, so please don't use it together with valuable data. Use readonly driver (CONFIG_SSFDC) instead. config SM_FTL_MUSEUM boolean "Additional Support for 1MiB and 2MiB SmartMedia cards" - depends on SM_FTL && MTD_NAND + depends on SM_FTL select MTD_NAND_ECC_SMC help - Very old SmartMedia cards need ECC to be calculated in the FTL - Such cards are very rare, thus enabling this option is mostly useless + Very old SmartMedia cards need ECC to be calculated in the FTL. + Such cards are very rare, thus enabling this option is mostly useless. Also this support is completely UNTESTED. config MTD_OOPS -- cgit v1.2.3-70-g09d2 From e4d64cab99a2d659bf9d6fe9ab67666bceb68d87 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Sat, 27 Feb 2010 02:31:51 +0200 Subject: mtd: blktrans: do blk_cleanup_queue when it is really safe to do so I was calling it in del_mtd_blktrans_dev, but ->request_fn could still be running at that point, thus defer this call to blktrans_dev_release Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/mtd_blkdevs.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c index e32c49cb400..03e19c1965c 100644 --- a/drivers/mtd/mtd_blkdevs.c +++ b/drivers/mtd/mtd_blkdevs.c @@ -32,6 +32,7 @@ void blktrans_dev_release(struct kref *kref) container_of(kref, struct mtd_blktrans_dev, ref); dev->disk->private_data = NULL; + blk_cleanup_queue(dev->rq); put_disk(dev->disk); list_del(&dev->list); kfree(dev); @@ -423,7 +424,6 @@ int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) old->rq->queuedata = NULL; blk_start_queue(old->rq); spin_unlock_irqrestore(&old->queue_lock, flags); - blk_cleanup_queue(old->rq); /* Ask trans driver for release to the mtd device */ mutex_lock(&old->lock); -- cgit v1.2.3-70-g09d2 From f696aa43fadb13a21c4e723fb6e51bf640dd1363 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Thu, 11 Mar 2010 09:10:32 -0800 Subject: mtd/nand/r852: fix build for CONFIG_PCI disabled r852 fails to build when CONFIG_PCI is not enabled since it uses pci_*() calls and is a PCI driver, so it should depend on PCI to prevent build errors. It should also #include . drivers/mtd/nand/r852.c:1053: error: implicit declaration of function 'pci_prepare_to_sleep' drivers/mtd/nand/r852.c:1062: error: implicit declaration of function 'pci_back_from_sleep' Signed-off-by: Randy Dunlap Cc: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 1 + drivers/mtd/nand/r852.c | 1 + 2 files changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 6701a00b7a9..226206e0623 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -105,6 +105,7 @@ config MTD_NAND_IDS config MTD_NAND_RICOH tristate "Ricoh xD card reader" default n + depends on PCI select MTD_SM_COMMON help Enable support for Ricoh R5C852 xD card reader diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index f5a0bc7adde..06f07bb414a 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include -- cgit v1.2.3-70-g09d2 From 0c82d3ce2f479c728f99e228d9ae32a9cd853c5a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 11 Mar 2010 09:25:14 -0800 Subject: mtd/nand/r852: Use pci_dma_mapping_error() ... instead of comparing with DMA_ERROR_CODE, which will only work on powerpc/sparc/x86. Reported-by: Geert Uytterhoeven Signed-off-by: David Woodhouse --- drivers/mtd/nand/r852.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 06f07bb414a..96bfbd8e8fd 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -197,7 +197,7 @@ static void r852_do_dma(struct r852_device *dev, uint8_t *buf, int do_read) R852_DMA_LEN, (do_read ? PCI_DMA_FROMDEVICE : PCI_DMA_TODEVICE)); - if (dev->phys_dma_addr == DMA_ERROR_CODE) + if (pci_dma_mapping_error(dev->pci_dev, dev->phys_dma_addr)) bounce = 1; } -- cgit v1.2.3-70-g09d2 From 42c259193ef3934733e300fefd3f0d0bb3576f3f Mon Sep 17 00:00:00 2001 From: Matteo Croce Date: Wed, 20 Jan 2010 16:29:18 +0100 Subject: mtd: small typo in Makefile Cosmetic fix: the path in the Makefile is wrong Signed-off-by: Matteo Croce Signed-off-by: David Woodhouse --- drivers/mtd/devices/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index ab5c9b92ac8..f3226b1d38f 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -1,5 +1,5 @@ # -# linux/drivers/devices/Makefile +# linux/drivers/mtd/devices/Makefile # obj-$(CONFIG_MTD_DOC2000) += doc2000.o -- cgit v1.2.3-70-g09d2 From 4d682420cead1ce06d8cd44ae193414404f0e7f5 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Wed, 10 Mar 2010 22:15:19 +0100 Subject: mtd: block2mtd: Use kasprintf kasprintf combines kmalloc and sprintf, and takes care of the size calculation itself. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression a,flag; expression list args; statement S; @@ a = - \(kmalloc\|kzalloc\)(...,flag) + kasprintf(flag,args) <... when != a if (a == NULL || ...) S ...> - sprintf(a,args); // Signed-off-by: Julia Lawall Signed-off-by: David Woodhouse --- drivers/mtd/devices/block2mtd.c | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/block2mtd.c b/drivers/mtd/devices/block2mtd.c index 8c295f40d2a..4281f3e0cf7 100644 --- a/drivers/mtd/devices/block2mtd.c +++ b/drivers/mtd/devices/block2mtd.c @@ -275,12 +275,10 @@ static struct block2mtd_dev *add_device(char *devname, int erase_size) /* Setup the MTD structure */ /* make the name contain the block device in */ - name = kmalloc(sizeof("block2mtd: ") + strlen(devname) + 1, - GFP_KERNEL); + name = kasprintf(GFP_KERNEL, "block2mtd: %s", devname); if (!name) goto devinit_err; - sprintf(name, "block2mtd: %s", devname); dev->mtd.name = name; dev->mtd.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; -- cgit v1.2.3-70-g09d2 From 395b228858778d3c44f7c413693a6acaa8bb62dc Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Sat, 20 Mar 2010 15:31:46 +0000 Subject: mtd/maps/pismo: remove dangling pointer and a leak While looking for drivers which forgot to clear i2c_clientdata before freeing the data structure it points to, I found that the pismo driver even has a leak on the probe error path. Signed-off-by: Wolfram Sang Acked-by: Russell King Signed-off-by: David Woodhouse --- drivers/mtd/maps/pismo.c | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/pismo.c b/drivers/mtd/maps/pismo.c index c48cad271f5..0a5e243d2bc 100644 --- a/drivers/mtd/maps/pismo.c +++ b/drivers/mtd/maps/pismo.c @@ -233,6 +233,7 @@ static int __devexit pismo_remove(struct i2c_client *client) /* FIXME: set_vpp needs saner arguments */ pismo_setvpp_remove_fix(pismo); + i2c_set_clientdata(client, NULL); kfree(pismo); return 0; @@ -271,7 +272,7 @@ static int __devinit pismo_probe(struct i2c_client *client, ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom)); if (ret < 0) { dev_err(&client->dev, "error reading EEPROM: %d\n", ret); - return ret; + goto exit_free; } dev_info(&client->dev, "%.15s board found\n", eeprom.board); @@ -282,6 +283,11 @@ static int __devinit pismo_probe(struct i2c_client *client, pdata->cs_addrs[i]); return 0; + + exit_free: + i2c_set_clientdata(client, NULL); + kfree(pismo); + return ret; } static const struct i2c_device_id pismo_id[] = { -- cgit v1.2.3-70-g09d2 From ca7081d96e39ea276892f0577f9cf37568547e88 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 19 Mar 2010 17:22:53 +0200 Subject: mtd: sm_ftl: initialize sysfs attributes This is new requirement in 2.6.34 Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/sm_ftl.c | 2 ++ 1 file changed, 2 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 9fb56c76ae8..2e7307d60af 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -72,6 +72,8 @@ struct attribute_group *sm_create_sysfs_attributes(struct sm_ftl *ftl) vendor_attribute = kzalloc(sizeof(struct sm_sysfs_attribute), GFP_KERNEL); + sysfs_attr_init(&vendor_attribute->dev_attr.attr); + vendor_attribute->data = vendor; vendor_attribute->len = vendor_len; vendor_attribute->dev_attr.attr.name = "vendor"; -- cgit v1.2.3-70-g09d2 From e5f710cfc6947e64672b7205f7992515868c7782 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 19 Mar 2010 17:22:54 +0200 Subject: mtd: nand: split out ECC module This way drivers could use ecc routines without depedency on whole nand Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 3 ++- drivers/mtd/nand/Kconfig | 19 ++++++++++++------- drivers/mtd/nand/Makefile | 3 ++- 3 files changed, 16 insertions(+), 9 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index dbee14d3722..e652080bce5 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -307,8 +307,9 @@ config SSFDC config SM_FTL tristate "SmartMedia/xD new translation layer" - depends on EXPERIMENTAL && BLOCK && MTD_NAND + depends on EXPERIMENTAL && BLOCK select MTD_BLKDEVS + select MTD_NAND_ECC help This enables new and very EXPERMENTAL support for SmartMedia/xD FTL (Flash translation layer). diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 164bd56b9d1..b712aedd89f 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -2,11 +2,23 @@ menuconfig MTD_NAND tristate "NAND Device Support" depends on MTD select MTD_NAND_IDS + select MTD_NAND_ECC help This enables support for accessing all type of NAND flash devices. For further information see . +config MTD_NAND_ECC + tristate + +config MTD_NAND_ECC_SMC + bool "NAND ECC Smart Media byte order" + depends on MTD_NAND_ECC + default n + help + Software ECC according to the Smart Media Specification. + The original Linux implementation had byte 0 and 1 swapped. + if MTD_NAND config MTD_NAND_VERIFY_WRITE @@ -18,13 +30,6 @@ config MTD_NAND_VERIFY_WRITE device thinks the write was successful, a bit could have been flipped accidentally due to device wear or something else. -config MTD_NAND_ECC_SMC - bool "NAND ECC Smart Media byte order" - default n - help - Software ECC according to the Smart Media Specification. - The original Linux implementation had byte 0 and 1 swapped. - config MTD_SM_COMMON tristate default n diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 5fbd1f83afb..04bccf9d7b5 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -2,7 +2,8 @@ # linux/drivers/nand/Makefile # -obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o +obj-$(CONFIG_MTD_NAND) += nand.o +obj-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o obj-$(CONFIG_MTD_SM_COMMON) += sm_common.o -- cgit v1.2.3-70-g09d2 From 01de69c2b0e471844193532a85c173f51d9ea6b2 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Fri, 19 Mar 2010 14:05:51 +0200 Subject: mtd: sm_ftl: remove CONFIG_SM_FTL_MUSEUM and make it always on The sole purpose of this setting was to avoid a dependency on MTD_NAND. Now that we can depend on MTD_NAND_ECC without pulling in all the rest of the NAND code, we might as well do so unconditionally. Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/Kconfig | 9 --------- drivers/mtd/sm_ftl.c | 8 +------- 2 files changed, 1 insertion(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index e652080bce5..f8210bf2d24 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -317,15 +317,6 @@ config SM_FTL eat your card, so please don't use it together with valuable data. Use readonly driver (CONFIG_SSFDC) instead. -config SM_FTL_MUSEUM - boolean "Additional Support for 1MiB and 2MiB SmartMedia cards" - depends on SM_FTL - select MTD_NAND_ECC_SMC - help - Very old SmartMedia cards need ECC to be calculated in the FTL. - Such cards are very rare, thus enabling this option is mostly useless. - Also this support is completely UNTESTED. - config MTD_OOPS tristate "Log panic/oops to an MTD buffer" depends on MTD diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 2e7307d60af..a9b4e344c55 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -15,12 +15,10 @@ #include #include #include +#include #include "nand/sm_common.h" #include "sm_ftl.h" -#ifdef CONFIG_SM_FTL_MUSEUM -#include -#endif struct workqueue_struct *cache_flush_workqueue; @@ -206,7 +204,6 @@ static void sm_break_offset(struct sm_ftl *ftl, loff_t offset, static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob) { -#ifdef CONFIG_SM_FTL_MUSEUM uint8_t ecc[3]; __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc); @@ -218,7 +215,6 @@ static int sm_correct_sector(uint8_t *buffer, struct sm_oob *oob) __nand_calculate_ecc(buffer, SM_SMALL_PAGE, ecc); if (__nand_correct_data(buffer, ecc, oob->ecc2, SM_SMALL_PAGE) < 0) return -EIO; -#endif return 0; } @@ -382,7 +378,6 @@ restart: oob.data_status = 0; } -#ifdef CONFIG_SM_FTL_MUSEUM if (ftl->smallpagenand) { __nand_calculate_ecc(buf + boffset, SM_SMALL_PAGE, oob.ecc1); @@ -390,7 +385,6 @@ restart: __nand_calculate_ecc(buf + boffset + SM_SMALL_PAGE, SM_SMALL_PAGE, oob.ecc2); } -#endif if (!sm_write_sector(ftl, zone, block, boffset, buf + boffset, &oob)) continue; -- cgit v1.2.3-70-g09d2 From 8da552f26107f55186346cff280ec9d53cc42add Mon Sep 17 00:00:00 2001 From: Stephen Rothwell Date: Mon, 29 Mar 2010 16:53:13 +1100 Subject: mtd: SmartMedia/xD FTL: use of kmalloc/kfree requires the include of slab.h Signed-off-by: Stephen Rothwell Signed-off-by: David Woodhouse --- drivers/mtd/sm_ftl.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index a9b4e344c55..4c215896fbc 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include "nand/sm_common.h" #include "sm_ftl.h" -- cgit v1.2.3-70-g09d2 From fed457a83611182f5a2e049cce02f8f4e1b65644 Mon Sep 17 00:00:00 2001 From: Akinobu Mita Date: Mon, 5 Apr 2010 13:53:35 -0700 Subject: bitops: rename for_each_bit() to for_each_set_bit(): mtd Rename for_each_bit() to for_each_set_bit in the kernel source tree. To permit for_each_clear_bit(), should that ever be added. I'll be sending a patch to Linus this week which removes the temporary for_each_bit() macro, so this patch will be needed to avoid build breakage. Suggested-by: Alexey Dobriyan Suggested-by: Andrew Morton Signed-off-by: Akinobu Mita Cc: "David S. Miller" Cc: Russell King Cc: David Woodhouse Cc: Artem Bityutskiy Cc: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: David Woodhouse --- drivers/mtd/sm_ftl.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/sm_ftl.c b/drivers/mtd/sm_ftl.c index 4c215896fbc..67822cf6c02 100644 --- a/drivers/mtd/sm_ftl.c +++ b/drivers/mtd/sm_ftl.c @@ -934,7 +934,7 @@ int sm_cache_flush(struct sm_ftl *ftl) /* Try to read all unread areas of the cache block*/ - for_each_bit(sector_num, &ftl->cache_data_invalid_bitmap, + for_each_set_bit(sector_num, &ftl->cache_data_invalid_bitmap, ftl->block_size / SM_SECTOR_SIZE) { if (!sm_read_sector(ftl, -- cgit v1.2.3-70-g09d2 From 1ca5d2f0196cfca678086fa6f88eec4f9d0307ee Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Fri, 2 Apr 2010 17:46:30 -0500 Subject: mtd/maps/physmap: catch failure to register MTD_PHYSMAP_COMPAT device If the default Kconfig values are used with MTD_PHYSMAP_COMPAT you end up with a resource where end < start. This causes __request_resource to return a conflict which then returns an -EBUSY error code. The current physmap.c code just assumes that the platfom_device_register will always succeed. Catch this failure during the physmap_init and propogate the error. Signed-off-by: H Hartley Sweeten Reported-by: Randy Dunlap Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap.c | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index d9603f7f965..426461a5f0d 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -264,8 +264,11 @@ static int __init physmap_init(void) err = platform_driver_register(&physmap_flash_driver); #ifdef CONFIG_MTD_PHYSMAP_COMPAT - if (err == 0) - platform_device_register(&physmap_flash); + if (err == 0) { + err = platform_device_register(&physmap_flash); + if (err) + platform_driver_unregister(&physmap_flash_driver); + } #endif return err; -- cgit v1.2.3-70-g09d2 From 0bfa4df2b65a94ce761306ab53447010b928b7dd Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Thu, 1 Apr 2010 15:22:50 +0300 Subject: mtd: nandsim: make some structures anonymous We do not need these names. Moreover, there are spelling typos there: "nansin" instead of "nandsim". This patch is just a clean up, no functional changes. Reported-by: Ferenc Wagner Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/nandsim.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nandsim.c b/drivers/mtd/nand/nandsim.c index 8a0a5d16e0e..261337efe0e 100644 --- a/drivers/mtd/nand/nandsim.c +++ b/drivers/mtd/nand/nandsim.c @@ -315,7 +315,7 @@ struct nandsim { union ns_mem buf; /* NAND flash "geometry" */ - struct nandsin_geometry { + struct { uint64_t totsz; /* total flash size, bytes */ uint32_t secsz; /* flash sector (erase block) size, bytes */ uint pgsz; /* NAND flash page size, bytes */ @@ -334,7 +334,7 @@ struct nandsim { } geom; /* NAND flash internal registers */ - struct nandsim_regs { + struct { unsigned command; /* the command register */ u_char status; /* the status register */ uint row; /* the page number */ @@ -345,7 +345,7 @@ struct nandsim { } regs; /* NAND flash lines state */ - struct ns_lines_status { + struct { int ce; /* chip Enable */ int cle; /* command Latch Enable */ int ale; /* address Latch Enable */ -- cgit v1.2.3-70-g09d2 From dad94318907b5947b499f88f38c074227245d15c Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Sat, 6 Mar 2010 20:09:23 +0100 Subject: mtd: kconfig: remove stray endchoice from help text Signed-off-by: Ferenc Wagner Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 2 -- 1 file changed, 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index b712aedd89f..8f402d46a36 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -373,8 +373,6 @@ config MTD_NAND_ATMEL_ECC_NONE If unsure, say N - endchoice - endchoice config MTD_NAND_PXA3xx -- cgit v1.2.3-70-g09d2 From ac39ee304ac33f15107e42adb5ee5b0d0ce2dc4a Mon Sep 17 00:00:00 2001 From: Mike Frysinger Date: Tue, 9 Mar 2010 11:05:48 -0500 Subject: mtd: Blackfin NFC: localize MMR bit masks Convert all magic numbers into appropriate defines, and move the defines out of the global namespace and into this one driver. No other driver needs to care about the MMR layout anyways. Signed-off-by: Mike Frysinger Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/bf5xx_nand.c | 29 +++++++++++++++++++++++++---- 1 file changed, 25 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/bf5xx_nand.c b/drivers/mtd/nand/bf5xx_nand.c index 8506e7e606f..2974995e194 100644 --- a/drivers/mtd/nand/bf5xx_nand.c +++ b/drivers/mtd/nand/bf5xx_nand.c @@ -68,6 +68,27 @@ #define DRV_AUTHOR "Bryan Wu " #define DRV_DESC "BF5xx on-chip NAND FLash Controller Driver" +/* NFC_STAT Masks */ +#define NBUSY 0x01 /* Not Busy */ +#define WB_FULL 0x02 /* Write Buffer Full */ +#define PG_WR_STAT 0x04 /* Page Write Pending */ +#define PG_RD_STAT 0x08 /* Page Read Pending */ +#define WB_EMPTY 0x10 /* Write Buffer Empty */ + +/* NFC_IRQSTAT Masks */ +#define NBUSYIRQ 0x01 /* Not Busy IRQ */ +#define WB_OVF 0x02 /* Write Buffer Overflow */ +#define WB_EDGE 0x04 /* Write Buffer Edge Detect */ +#define RD_RDY 0x08 /* Read Data Ready */ +#define WR_DONE 0x10 /* Page Write Done */ + +/* NFC_RST Masks */ +#define ECC_RST 0x01 /* ECC (and NFC counters) Reset */ + +/* NFC_PGCTL Masks */ +#define PG_RD_START 0x01 /* Page Read Start */ +#define PG_WR_START 0x02 /* Page Write Start */ + #ifdef CONFIG_MTD_NAND_BF5XX_HWECC static int hardware_ecc = 1; #else @@ -487,7 +508,7 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, * transferred to generate the correct ECC register * values. */ - bfin_write_NFC_RST(0x1); + bfin_write_NFC_RST(ECC_RST); SSYNC(); disable_dma(CH_NFC); @@ -497,7 +518,7 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, set_dma_config(CH_NFC, 0x0); set_dma_start_addr(CH_NFC, (unsigned long) buf); -/* The DMAs have different size on BF52x and BF54x */ + /* 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); @@ -517,9 +538,9 @@ static void bf5xx_nand_dma_rw(struct mtd_info *mtd, /* Start PAGE read/write operation */ if (is_read) - bfin_write_NFC_PGCTL(0x1); + bfin_write_NFC_PGCTL(PG_RD_START); else - bfin_write_NFC_PGCTL(0x2); + bfin_write_NFC_PGCTL(PG_WR_START); wait_for_completion(&info->dma_completion); } -- cgit v1.2.3-70-g09d2 From 4a70b7d3953c279738a094d2e5ffe7c66b15a5d0 Mon Sep 17 00:00:00 2001 From: Mika Westerberg Date: Wed, 24 Mar 2010 12:10:48 +0200 Subject: mtd: OneNAND: OMAP2/3: unmap correct DMA buffer Functions omap2_onenand_write_bufferram() and omap3_onenand_write_bufferram() map the write buffer and store the returned handle in variable dma_src. However, when DMA unmap is done, variable dma_dst is used instead of the correct dma_src. This patch fixes them to use the correct DMA buffer. Signed-off-by: Mika Westerberg Tested-by: Arnaud Ebalard Acked-by: Adrian Hunter Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/onenand/omap2.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/omap2.c b/drivers/mtd/onenand/omap2.c index dfbab6c72b7..f52934c7eb0 100644 --- a/drivers/mtd/onenand/omap2.c +++ b/drivers/mtd/onenand/omap2.c @@ -402,7 +402,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, dma_src = dma_map_single(&c->pdev->dev, buf, count, DMA_TO_DEVICE); dma_dst = c->phys_base + bram_offset; - if (dma_mapping_error(&c->pdev->dev, dma_dst)) { + if (dma_mapping_error(&c->pdev->dev, dma_src)) { dev_err(&c->pdev->dev, "Couldn't DMA map a %d byte buffer\n", count); @@ -425,7 +425,7 @@ static int omap3_onenand_write_bufferram(struct mtd_info *mtd, int area, if (*done) break; - dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE); + dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); if (!*done) { dev_err(&c->pdev->dev, "timeout waiting for DMA\n"); @@ -520,7 +520,7 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, dma_src = dma_map_single(&c->pdev->dev, (void *) buffer, count, DMA_TO_DEVICE); dma_dst = c->phys_base + bram_offset; - if (dma_mapping_error(&c->pdev->dev, dma_dst)) { + if (dma_mapping_error(&c->pdev->dev, dma_src)) { dev_err(&c->pdev->dev, "Couldn't DMA map a %d byte buffer\n", count); @@ -538,7 +538,7 @@ static int omap2_onenand_write_bufferram(struct mtd_info *mtd, int area, omap_start_dma(c->dma_channel); wait_for_completion(&c->dma_done); - dma_unmap_single(&c->pdev->dev, dma_dst, count, DMA_TO_DEVICE); + dma_unmap_single(&c->pdev->dev, dma_src, count, DMA_TO_DEVICE); return 0; } -- cgit v1.2.3-70-g09d2 From b92b5c41a05b69f56e3d5e92dce3dbb5f5f5cf81 Mon Sep 17 00:00:00 2001 From: Ferenc Wagner Date: Tue, 23 Mar 2010 18:08:16 +0100 Subject: mtd/nand/fsl_upm: Replace the dangerous to_fsl_upm_nand macro The original macro worked only when applied to variables named 'mtd'. While this could have been fixed by simply renaming the macro argument, a more type-safe replacement is preferred. Signed-off-by: Ferenc Wagner Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/fsl_upm.c | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/fsl_upm.c b/drivers/mtd/nand/fsl_upm.c index d721ec055cb..b4e2ba47d7b 100644 --- a/drivers/mtd/nand/fsl_upm.c +++ b/drivers/mtd/nand/fsl_upm.c @@ -48,7 +48,10 @@ struct fsl_upm_nand { uint32_t wait_flags; }; -#define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd) +static inline struct fsl_upm_nand *to_fsl_upm_nand(struct mtd_info *mtdinfo) +{ + return container_of(mtdinfo, struct fsl_upm_nand, mtd); +} static int fun_chip_ready(struct mtd_info *mtd) { -- cgit v1.2.3-70-g09d2 From c4e773764cead9358fd4b036d1b883fff3968513 Mon Sep 17 00:00:00 2001 From: Stefani Seibold Date: Sun, 18 Apr 2010 22:46:44 +0200 Subject: mtd: fix a huge latency problem in the MTD CFI and LPDDR flash drivers. The use of a memcpy() during a spinlock operation will cause very long thread context switch delays if the flash chip bandwidth is low and the data to be copied large, because a spinlock will disable preemption. For example: A flash with 6,5 MB/s bandwidth will cause under ubifs, which request sometimes 128 KiB (the flash erase size), a preemption delay of 20 milliseconds. High priority threads will not be served during this time, regardless whether this threads access the flash or not. This behavior breaks real time. The patch changes all the use of spin_lock operations for xxxx->mutex into mutex operations, which is exact what the name says and means. I have checked the code of the drivers and there is no use of atomic pathes like interrupt or timers. The mtdoops facility will also not be used by this drivers. So it is dave to replace the spin_lock against mutex. There is no performance regression since the mutex is normally not acquired. Changelog: 06.03.2010 First release 26.03.2010 Fix mutex[1] issue and tested it for compile failure Signed-off-by: Stefani Seibold Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0001.c | 131 +++++++++++++++++----------------- drivers/mtd/chips/cfi_cmdset_0002.c | 122 ++++++++++++++++---------------- drivers/mtd/chips/cfi_cmdset_0020.c | 136 ++++++++++++++++++------------------ drivers/mtd/chips/fwh_lock.h | 6 +- drivers/mtd/chips/gen_probe.c | 3 +- drivers/mtd/lpddr/lpddr_cmds.c | 79 +++++++++++---------- include/linux/mtd/flashchip.h | 4 +- 7 files changed, 239 insertions(+), 242 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 92530433c11..62f3ea9de84 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -725,8 +725,7 @@ static int cfi_intelext_partition_fixup(struct mtd_info *mtd, /* those should be reset too since they create memory references. */ init_waitqueue_head(&chip->wq); - spin_lock_init(&chip->_spinlock); - chip->mutex = &chip->_spinlock; + mutex_init(&chip->mutex); chip++; } } @@ -772,9 +771,9 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long if (chip->priv && map_word_andequal(map, status, status_PWS, status_PWS)) break; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Someone else might have been playing with it. */ return -EAGAIN; } @@ -821,9 +820,9 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long return -EIO; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. So we can just loop here. */ } @@ -850,10 +849,10 @@ static int chip_ready (struct map_info *map, struct flchip *chip, unsigned long sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); return -EAGAIN; } } @@ -899,20 +898,20 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr * it'll happily send us to sleep. In any case, when * get_chip returns success we're clear to go ahead. */ - ret = spin_trylock(contender->mutex); + ret = mutex_trylock(&contender->mutex); spin_unlock(&shared->lock); if (!ret) goto retry; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); ret = chip_ready(map, contender, contender->start, mode); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (ret == -EAGAIN) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } if (ret) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); return ret; } spin_lock(&shared->lock); @@ -921,10 +920,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr * in FL_SYNCING state. Put contender and retry. */ if (chip->state == FL_SYNCING) { put_chip(map, contender, contender->start); - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); } /* Check if we already have suspended erase @@ -934,10 +933,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr spin_unlock(&shared->lock); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); goto retry; } @@ -967,12 +966,12 @@ static void put_chip(struct map_info *map, struct flchip *chip, unsigned long ad if (shared->writing && shared->writing != chip) { /* give back ownership to who we loaned it from */ struct flchip *loaner = shared->writing; - spin_lock(loaner->mutex); + mutex_lock(&loaner->mutex); spin_unlock(&shared->lock); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); put_chip(map, loaner, loaner->start); - spin_lock(chip->mutex); - spin_unlock(loaner->mutex); + mutex_lock(&chip->mutex); + mutex_unlock(&loaner->mutex); wake_up(&chip->wq); return; } @@ -1142,7 +1141,7 @@ static int __xipram xip_wait_for_operation( (void) map_read(map, adr); xip_iprefetch(); local_irq_enable(); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); xip_iprefetch(); cond_resched(); @@ -1152,15 +1151,15 @@ static int __xipram xip_wait_for_operation( * a suspended erase state. If so let's wait * until it's done. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != newstate) { DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } /* Disallow XIP again */ local_irq_disable(); @@ -1216,10 +1215,10 @@ static int inval_cache_and_wait_for_operation( int chip_state = chip->state; unsigned int timeo, sleep_time, reset_timeo; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (inval_len) INVALIDATE_CACHED_RANGE(map, inval_adr, inval_len); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); timeo = chip_op_time_max; if (!timeo) @@ -1239,7 +1238,7 @@ static int inval_cache_and_wait_for_operation( } /* OK Still waiting. Drop the lock, wait a while and retry. */ - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (sleep_time >= 1000000/HZ) { /* * Half of the normal delay still remaining @@ -1254,17 +1253,17 @@ static int inval_cache_and_wait_for_operation( cond_resched(); timeo--; } - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != chip_state) { /* Someone's suspended the operation: sleep */ DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } if (chip->erase_suspended && chip_state == FL_ERASING) { /* Erase suspend occured while sleep: reset timeout */ @@ -1300,7 +1299,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_POINT); @@ -1311,7 +1310,7 @@ static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t a chip->state = FL_POINT; chip->ref_point_counter++; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1396,7 +1395,7 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len) else thislen = len; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_POINT) { chip->ref_point_counter--; if(chip->ref_point_counter == 0) @@ -1405,7 +1404,7 @@ static void cfi_intelext_unpoint(struct mtd_info *mtd, loff_t from, size_t len) printk(KERN_ERR "%s: Warning: unpoint called on non pointed region\n", map->name); /* Should this give an error? */ put_chip(map, chip, chip->start); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); len -= thislen; ofs = 0; @@ -1424,10 +1423,10 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1441,7 +1440,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof put_chip(map, chip, cmd_addr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -1504,10 +1503,10 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, return -EINVAL; } - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, mode); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1553,7 +1552,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, xip_enable(map, chip, adr); out: put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1662,10 +1661,10 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, /* Let's determine this according to the interleave only once */ write_cmd = (cfi->cfiq->P_ID != 0x0200) ? CMD(0xe8) : CMD(0xe9); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1796,7 +1795,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, xip_enable(map, chip, cmd_adr); out: put_chip(map, chip, cmd_adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1875,10 +1874,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, adr += chip->start; retry: - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_ERASING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1934,7 +1933,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, } else if (chipstatus & 0x20 && retries--) { printk(KERN_DEBUG "block erase failed at 0x%08lx: status 0x%lx. Retrying...\n", adr, chipstatus); put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); goto retry; } else { printk(KERN_ERR "%s: block erase failed at 0x%08lx (status 0x%lx)\n", map->name, adr, chipstatus); @@ -1946,7 +1945,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, xip_enable(map, chip, adr); out: put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1979,7 +1978,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd) for (i=0; !ret && inumchips; i++) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, chip->start, FL_SYNCING); if (!ret) { @@ -1990,7 +1989,7 @@ static void cfi_intelext_sync (struct mtd_info *mtd) * with the chip now anyway. */ } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -1998,14 +1997,14 @@ static void cfi_intelext_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; chip->oldstate = FL_READY; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -2051,10 +2050,10 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip adr += chip->start; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_LOCKING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -2088,7 +2087,7 @@ static int __xipram do_xxlock_oneblock(struct map_info *map, struct flchip *chip xip_enable(map, chip, adr); out: put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -2153,10 +2152,10 @@ do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, struct cfi_private *cfi = map->fldrv_priv; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -2175,7 +2174,7 @@ do_otp_read(struct map_info *map, struct flchip *chip, u_long offset, INVALIDATE_CACHED_RANGE(map, chip->start + offset, size); put_chip(map, chip, chip->start); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -2450,7 +2449,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) for (i=0; !ret && inumchips; i++) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); switch (chip->state) { case FL_READY: @@ -2482,7 +2481,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) case FL_PM_SUSPENDED: break; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -2491,7 +2490,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { /* No need to force it into a known state here, @@ -2501,7 +2500,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) chip->oldstate = FL_READY; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -2542,7 +2541,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { @@ -2551,7 +2550,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } if ((mtd->flags & MTD_POWERUP_LOCK) @@ -2571,14 +2570,14 @@ static int cfi_intelext_reset(struct mtd_info *mtd) /* force the completion of any ongoing operation and switch to array mode so any bootloader in flash is accessible for soft reboot. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); if (!ret) { map_write(map, CMD(0xff), chip->start); chip->state = FL_SHUTDOWN; put_chip(map, chip, chip->start); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } return 0; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index ea2a7f66ddf..c93e47d21ce 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -565,9 +565,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr printk(KERN_ERR "Waiting for chip to be ready timed out.\n"); return -EIO; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Someone else might have been playing with it. */ goto retry; } @@ -611,9 +611,9 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr return -EIO; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. So we can just loop here. */ } @@ -637,10 +637,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); goto resettime; } } @@ -772,7 +772,7 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, (void) map_read(map, adr); xip_iprefetch(); local_irq_enable(); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); xip_iprefetch(); cond_resched(); @@ -782,15 +782,15 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, * a suspended erase state. If so let's wait * until it's done. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != FL_XIP_WHILE_ERASING) { DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } /* Disallow XIP again */ local_irq_disable(); @@ -852,17 +852,17 @@ static void __xipram xip_udelay(struct map_info *map, struct flchip *chip, #define UDELAY(map, chip, adr, usec) \ do { \ - spin_unlock(chip->mutex); \ + mutex_unlock(&chip->mutex); \ cfi_udelay(usec); \ - spin_lock(chip->mutex); \ + mutex_lock(&chip->mutex); \ } while (0) #define INVALIDATE_CACHE_UDELAY(map, chip, adr, len, usec) \ do { \ - spin_unlock(chip->mutex); \ + mutex_unlock(&chip->mutex); \ INVALIDATE_CACHED_RANGE(map, adr, len); \ cfi_udelay(usec); \ - spin_lock(chip->mutex); \ + mutex_lock(&chip->mutex); \ } while (0) #endif @@ -878,10 +878,10 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Ensure cmd read/writes are aligned. */ cmd_addr = adr & ~(map_bankwidth(map)-1); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, cmd_addr, FL_READY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -894,7 +894,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof put_chip(map, chip, cmd_addr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -948,7 +948,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi struct cfi_private *cfi = map->fldrv_priv; retry: - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state != FL_READY){ #if 0 @@ -957,7 +957,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); @@ -986,7 +986,7 @@ static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chi cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); wake_up(&chip->wq); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -1055,10 +1055,10 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, adr += chip->start; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1101,11 +1101,11 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -1137,7 +1137,7 @@ static int __xipram do_write_oneword(struct map_info *map, struct flchip *chip, op_done: chip->state = FL_READY; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1169,7 +1169,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, map_word tmp_buf; retry: - spin_lock(cfi->chips[chipnum].mutex); + mutex_lock(&cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) { #if 0 @@ -1178,7 +1178,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&cfi->chips[chipnum].wq, &wait); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); schedule(); remove_wait_queue(&cfi->chips[chipnum].wq, &wait); @@ -1192,7 +1192,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, /* Load 'tmp_buf' with old contents of flash */ tmp_buf = map_read(map, bus_ofs+chipstart); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); /* Number of bytes to copy from buffer */ n = min_t(int, len, map_bankwidth(map)-i); @@ -1247,7 +1247,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, map_word tmp_buf; retry1: - spin_lock(cfi->chips[chipnum].mutex); + mutex_lock(&cfi->chips[chipnum].mutex); if (cfi->chips[chipnum].state != FL_READY) { #if 0 @@ -1256,7 +1256,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&cfi->chips[chipnum].wq, &wait); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); schedule(); remove_wait_queue(&cfi->chips[chipnum].wq, &wait); @@ -1269,7 +1269,7 @@ static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, tmp_buf = map_read(map, ofs + chipstart); - spin_unlock(cfi->chips[chipnum].mutex); + mutex_unlock(&cfi->chips[chipnum].mutex); tmp_buf = map_word_load_partial(map, tmp_buf, buf, 0, len); @@ -1304,10 +1304,10 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, adr += chip->start; cmd_adr = adr; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1362,11 +1362,11 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -1394,7 +1394,7 @@ static int __xipram do_write_buffer(struct map_info *map, struct flchip *chip, op_done: chip->state = FL_READY; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1494,10 +1494,10 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) adr = cfi->addr_unlock1; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1530,10 +1530,10 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } if (chip->erase_suspended) { @@ -1567,7 +1567,7 @@ static int __xipram do_erase_chip(struct map_info *map, struct flchip *chip) chip->state = FL_READY; xip_enable(map, chip, adr); put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1582,10 +1582,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, adr += chip->start; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_ERASING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1618,10 +1618,10 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); continue; } if (chip->erase_suspended) { @@ -1657,7 +1657,7 @@ static int __xipram do_erase_oneblock(struct map_info *map, struct flchip *chip, chip->state = FL_READY; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1709,7 +1709,7 @@ static int do_atmel_lock(struct map_info *map, struct flchip *chip, struct cfi_private *cfi = map->fldrv_priv; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr + chip->start, FL_LOCKING); if (ret) goto out_unlock; @@ -1735,7 +1735,7 @@ static int do_atmel_lock(struct map_info *map, struct flchip *chip, ret = 0; out_unlock: - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1745,7 +1745,7 @@ static int do_atmel_unlock(struct map_info *map, struct flchip *chip, struct cfi_private *cfi = map->fldrv_priv; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr + chip->start, FL_UNLOCKING); if (ret) goto out_unlock; @@ -1763,7 +1763,7 @@ static int do_atmel_unlock(struct map_info *map, struct flchip *chip, ret = 0; out_unlock: - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -1791,7 +1791,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) chip = &cfi->chips[i]; retry: - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1805,7 +1805,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) * with the chip now anyway. */ case FL_SYNCING: - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); break; default: @@ -1813,7 +1813,7 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); @@ -1828,13 +1828,13 @@ static void cfi_amdstd_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1850,7 +1850,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) for (i=0; !ret && inumchips; i++) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1870,7 +1870,7 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) ret = -EAGAIN; break; } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -1879,13 +1879,13 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1904,7 +1904,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { chip->state = FL_READY; @@ -1914,7 +1914,7 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) else printk(KERN_ERR "Argh. Chip not in PM_SUSPENDED state upon resume()\n"); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); } } diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c index 0667a671525..e54e8c169d7 100644 --- a/drivers/mtd/chips/cfi_cmdset_0020.c +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -265,7 +265,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. * If it's in FL_ERASING state, suspend it and make it talk now. @@ -296,15 +296,15 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* make sure we're in 'read status' mode */ map_write(map, CMD(0x70), cmd_addr); chip->state = FL_ERASING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "Chip not ready after erase " "suspended: status = 0x%lx\n", status.x[0]); return -EIO; } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } suspended = 1; @@ -335,13 +335,13 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %lx\n", status.x[0]); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -351,7 +351,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -376,7 +376,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -445,7 +445,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, #ifdef DEBUG_CFI_FEATURES printk("%s: chip->state[%d]\n", __func__, chip->state); #endif - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. * Later, we can actually think about interrupting it @@ -470,14 +470,14 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, break; /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %lx, status = %lx\n", status.x[0], map_read(map, cmd_adr).x[0]); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -486,7 +486,7 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -503,16 +503,16 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, if (map_word_andequal(map, status, status_OK, status_OK)) break; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); if (++z > 100) { /* Argh. Not ready for write to buffer */ DISABLE_VPP(map); map_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %lx\n", status.x[0]); return -EIO; } @@ -532,9 +532,9 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(chip->buffer_write_time); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); timeo = jiffies + (HZ/2); z = 0; @@ -543,11 +543,11 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* Someone's suspended the write. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -563,16 +563,16 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, map_write(map, CMD(0x70), adr); chip->state = FL_STATUS; DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); z++; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } if (!z) { chip->buffer_write_time--; @@ -596,11 +596,11 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* put back into read status register mode */ map_write(map, CMD(0x70), adr); wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return map_word_bitsset(map, status, CMD(0x02)) ? -EROFS : -EIO; } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } @@ -749,7 +749,7 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { @@ -766,13 +766,13 @@ retry: /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -781,7 +781,7 @@ retry: someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -797,9 +797,9 @@ retry: map_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); msleep(1000); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -810,11 +810,11 @@ retry: /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ*20); /* FIXME */ - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); continue; } @@ -828,14 +828,14 @@ retry: chip->state = FL_STATUS; printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } DISABLE_VPP(map); @@ -878,7 +878,7 @@ retry: printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, chipstatus); timeo = jiffies + HZ; chip->state = FL_STATUS; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); goto retry; } printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, chipstatus); @@ -887,7 +887,7 @@ retry: } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -995,7 +995,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) chip = &cfi->chips[i]; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1009,7 +1009,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) * with the chip now anyway. */ case FL_SYNCING: - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); break; default: @@ -1017,7 +1017,7 @@ static void cfi_staa_sync (struct mtd_info *mtd) set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); @@ -1030,13 +1030,13 @@ static void cfi_staa_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1054,7 +1054,7 @@ static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, un timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { @@ -1071,13 +1071,13 @@ retry: /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in lock\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -1086,7 +1086,7 @@ retry: someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -1098,9 +1098,9 @@ retry: map_write(map, CMD(0x01), adr); chip->state = FL_LOCKING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); msleep(1000); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1118,21 +1118,21 @@ retry: chip->state = FL_STATUS; printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; DISABLE_VPP(map); wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -1203,7 +1203,7 @@ static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, timeo = jiffies + HZ; retry: - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Check that the chip's ready to talk to us. */ switch (chip->state) { @@ -1220,13 +1220,13 @@ retry: /* Urgh. Chip not yet ready to talk to us. */ if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in unlock\n"); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); goto retry; @@ -1235,7 +1235,7 @@ retry: someone changes the status */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + HZ; @@ -1247,9 +1247,9 @@ retry: map_write(map, CMD(0xD0), adr); chip->state = FL_UNLOCKING; - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); msleep(1000); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1267,21 +1267,21 @@ retry: chip->state = FL_STATUS; printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %lx, status = %lx.\n", status.x[0], map_read(map, adr).x[0]); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return -EIO; } /* Latency issues. Drop the unlock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; DISABLE_VPP(map); wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len) @@ -1334,7 +1334,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) for (i=0; !ret && inumchips; i++) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); switch(chip->state) { case FL_READY: @@ -1354,7 +1354,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) ret = -EAGAIN; break; } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } /* Unlock the chips again */ @@ -1363,7 +1363,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_PM_SUSPENDED) { /* No need to force it into a known state here, @@ -1372,7 +1372,7 @@ static int cfi_staa_suspend(struct mtd_info *mtd) chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } } @@ -1390,7 +1390,7 @@ static void cfi_staa_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + mutex_lock(&chip->mutex); /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { @@ -1399,7 +1399,7 @@ static void cfi_staa_resume(struct mtd_info *mtd) wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + mutex_unlock(&chip->mutex); } } diff --git a/drivers/mtd/chips/fwh_lock.h b/drivers/mtd/chips/fwh_lock.h index 57e0e4e921f..d1806497719 100644 --- a/drivers/mtd/chips/fwh_lock.h +++ b/drivers/mtd/chips/fwh_lock.h @@ -58,10 +58,10 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, * to flash memory - that means that we don't have to check status * and timeout. */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, adr, FL_LOCKING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -72,7 +72,7 @@ static int fwh_xxlock_oneblock(struct map_info *map, struct flchip *chip, /* Done and happy. */ chip->state = chip->oldstate; put_chip(map, chip, adr); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return 0; } diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index e2dc96441e0..fcc1bc02c8a 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -155,8 +155,7 @@ static struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chi pchip->start = (i << cfi.chipshift); pchip->state = FL_READY; init_waitqueue_head(&pchip->wq); - spin_lock_init(&pchip->_spinlock); - pchip->mutex = &pchip->_spinlock; + mutex_init(&pchip->mutex); } } diff --git a/drivers/mtd/lpddr/lpddr_cmds.c b/drivers/mtd/lpddr/lpddr_cmds.c index e22ca49583e..eb6f437ca9e 100644 --- a/drivers/mtd/lpddr/lpddr_cmds.c +++ b/drivers/mtd/lpddr/lpddr_cmds.c @@ -106,8 +106,7 @@ struct mtd_info *lpddr_cmdset(struct map_info *map) /* those should be reset too since they create memory references. */ init_waitqueue_head(&chip->wq); - spin_lock_init(&chip->_spinlock); - chip->mutex = &chip->_spinlock; + mutex_init(&chip->mutex); chip++; } } @@ -143,7 +142,7 @@ static int wait_for_ready(struct map_info *map, struct flchip *chip, } /* OK Still waiting. Drop the lock, wait a while and retry. */ - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (sleep_time >= 1000000/HZ) { /* * Half of the normal delay still remaining @@ -158,17 +157,17 @@ static int wait_for_ready(struct map_info *map, struct flchip *chip, cond_resched(); timeo--; } - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); while (chip->state != chip_state) { /* Someone's suspended the operation: sleep */ DECLARE_WAITQUEUE(wait, current); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); } if (chip->erase_suspended || chip->write_suspended) { /* Suspend has occured while sleep: reset timeout */ @@ -229,20 +228,20 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) * it'll happily send us to sleep. In any case, when * get_chip returns success we're clear to go ahead. */ - ret = spin_trylock(contender->mutex); + ret = mutex_trylock(&contender->mutex); spin_unlock(&shared->lock); if (!ret) goto retry; - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); ret = chip_ready(map, contender, mode); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (ret == -EAGAIN) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } if (ret) { - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); return ret; } spin_lock(&shared->lock); @@ -251,10 +250,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) * state. Put contender and retry. */ if (chip->state == FL_SYNCING) { put_chip(map, contender); - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); goto retry; } - spin_unlock(contender->mutex); + mutex_unlock(&contender->mutex); } /* Check if we have suspended erase on this chip. @@ -264,10 +263,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, int mode) spin_unlock(&shared->lock); set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); goto retry; } @@ -336,10 +335,10 @@ static int chip_ready(struct map_info *map, struct flchip *chip, int mode) sleep: set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); return -EAGAIN; } } @@ -355,12 +354,12 @@ static void put_chip(struct map_info *map, struct flchip *chip) if (shared->writing && shared->writing != chip) { /* give back the ownership */ struct flchip *loaner = shared->writing; - spin_lock(loaner->mutex); + mutex_lock(&loaner->mutex); spin_unlock(&shared->lock); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); put_chip(map, loaner); - spin_lock(chip->mutex); - spin_unlock(loaner->mutex); + mutex_lock(&chip->mutex); + mutex_unlock(&loaner->mutex); wake_up(&chip->wq); return; } @@ -413,10 +412,10 @@ int do_write_buffer(struct map_info *map, struct flchip *chip, wbufsize = 1 << lpddr->qinfo->BufSizeShift; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } /* Figure out the number of words to write */ @@ -477,7 +476,7 @@ int do_write_buffer(struct map_info *map, struct flchip *chip, } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -489,10 +488,10 @@ int do_erase_oneblock(struct mtd_info *mtd, loff_t adr) struct flchip *chip = &lpddr->chips[chipnum]; int ret; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_ERASING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } send_pfow_command(map, LPDDR_BLOCK_ERASE, adr, 0, NULL); @@ -504,7 +503,7 @@ int do_erase_oneblock(struct mtd_info *mtd, loff_t adr) goto out; } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -517,10 +516,10 @@ static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, struct flchip *chip = &lpddr->chips[chipnum]; int ret = 0; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_READY); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -528,7 +527,7 @@ static int lpddr_read(struct mtd_info *mtd, loff_t adr, size_t len, *retlen = len; put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -568,9 +567,9 @@ static int lpddr_point(struct mtd_info *mtd, loff_t adr, size_t len, else thislen = len; /* get the chip */ - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_POINT); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); if (ret) break; @@ -610,7 +609,7 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len) else thislen = len; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); if (chip->state == FL_POINT) { chip->ref_point_counter--; if (chip->ref_point_counter == 0) @@ -620,7 +619,7 @@ static void lpddr_unpoint (struct mtd_info *mtd, loff_t adr, size_t len) "pointed region\n", map->name); put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); len -= thislen; ofs = 0; @@ -726,10 +725,10 @@ int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) int chipnum = adr >> lpddr->chipshift; struct flchip *chip = &lpddr->chips[chipnum]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_LOCKING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -749,7 +748,7 @@ int do_xxlock(struct mtd_info *mtd, loff_t adr, uint32_t len, int thunk) goto out; } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -770,10 +769,10 @@ int word_program(struct map_info *map, loff_t adr, uint32_t curval) int chipnum = adr >> lpddr->chipshift; struct flchip *chip = &lpddr->chips[chipnum]; - spin_lock(chip->mutex); + mutex_lock(&chip->mutex); ret = get_chip(map, chip, FL_WRITING); if (ret) { - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } @@ -787,7 +786,7 @@ int word_program(struct map_info *map, loff_t adr, uint32_t curval) } out: put_chip(map, chip); - spin_unlock(chip->mutex); + mutex_unlock(&chip->mutex); return ret; } diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index d0bf422ae37..f43e9b49b75 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -15,6 +15,7 @@ * has asm/spinlock.h, or 2.4, which has linux/spinlock.h */ #include +#include typedef enum { FL_READY, @@ -74,8 +75,7 @@ struct flchip { unsigned int erase_suspended:1; unsigned long in_progress_block_addr; - spinlock_t *mutex; - spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */ + struct mutex mutex; wait_queue_head_t wq; /* Wait on here when we're waiting for the chip to be ready */ int word_write_time; -- cgit v1.2.3-70-g09d2 From 258006d1abcf3f2990d3ebd77d75af335ff24d81 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Fri, 2 Apr 2010 14:47:38 +0200 Subject: mtd: maps: Eliminate use after free Moved the debugging message before the call to map_destroy, which frees its argument. The message is also slightly changed to reflect its new position. A simplified version of the semantic patch that finds this problem is as follows: (http://coccinelle.lip6.fr/) // @@ expression E,E2; @@ del_mtd_device(E) ... ( E = E2 | * E ) // Signed-off-by: Julia Lawall Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/maps/pcmciamtd.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 689d6a79ffc..81159d708f8 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -692,8 +692,8 @@ static void pcmciamtd_detach(struct pcmcia_device *link) if(dev->mtd_info) { del_mtd_device(dev->mtd_info); + info("mtd%d: Removing", dev->mtd_info->index); map_destroy(dev->mtd_info); - info("mtd%d: Removed", dev->mtd_info->index); } pcmciamtd_release(link); -- cgit v1.2.3-70-g09d2 From 1dd2a092af8ed53eb744c5b9993fa775616cf699 Mon Sep 17 00:00:00 2001 From: Huang Weiyi Date: Thu, 8 Apr 2010 19:49:18 +0800 Subject: mtd/nand/bcm_umi: remove unused #include Remove unused #include ('s) in drivers/mtd/nand/bcm_umi_nand.c Signed-off-by: Huang Weiyi Acked-by: Leo Chen Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/bcm_umi_nand.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/bcm_umi_nand.c b/drivers/mtd/nand/bcm_umi_nand.c index 7eb8674c9cf..a97e6f167c0 100644 --- a/drivers/mtd/nand/bcm_umi_nand.c +++ b/drivers/mtd/nand/bcm_umi_nand.c @@ -13,7 +13,6 @@ *****************************************************************************/ /* ---- Include Files ---------------------------------------------------- */ -#include #include #include #include -- cgit v1.2.3-70-g09d2 From bff3c10d369440bc87ba612b45ba2777d2bf017f Mon Sep 17 00:00:00 2001 From: Marc Kleine-Budde Date: Fri, 23 Apr 2010 23:25:44 +0200 Subject: mtd: pxa32xx_nand: add support for partition table parsing The pxa32xx_nand driver doesn't support partition tables from the command line. This patch adds support for it. Signed-off-by: Marc Kleine-Budde Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/pxa3xx_nand.c | 11 +++++++++++ 1 file changed, 11 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/pxa3xx_nand.c b/drivers/mtd/nand/pxa3xx_nand.c index 1a5a0365c98..55a3d2c05f2 100644 --- a/drivers/mtd/nand/pxa3xx_nand.c +++ b/drivers/mtd/nand/pxa3xx_nand.c @@ -1319,6 +1319,17 @@ static int pxa3xx_nand_probe(struct platform_device *pdev) goto fail_free_irq; } + if (mtd_has_cmdlinepart()) { + static const char *probes[] = { "cmdlinepart", NULL }; + struct mtd_partition *parts; + int nr_parts; + + nr_parts = parse_mtd_partitions(mtd, probes, &parts, 0); + + if (nr_parts) + return add_mtd_partitions(mtd, parts, nr_parts); + } + return add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); fail_free_irq: -- cgit v1.2.3-70-g09d2 From eafe1311aa3cdb13efa25c60251bce12e60ae38a Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Thu, 29 Apr 2010 10:26:56 -0700 Subject: mtd: cfi_cmdset_0002: Add reboot notifier for AMD flashes Ensure that the flash device is in a quiescent state before rebooting. The implementation is closely modeled after the cfi_cmdset_0001 reboot notifier, commit 963a6fb0a0d336d0513083b7e4b5c3ff9d6d2061 . Signed-off-by: Kevin Cernekee Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 56 +++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index c93e47d21ce..c16b8cecc3a 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -32,6 +32,7 @@ #include #include #include +#include #include #include #include @@ -56,6 +57,7 @@ static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); static void cfi_amdstd_sync (struct mtd_info *); static int cfi_amdstd_suspend (struct mtd_info *); static void cfi_amdstd_resume (struct mtd_info *); +static int cfi_amdstd_reboot(struct notifier_block *, unsigned long, void *); static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static void cfi_amdstd_destroy(struct mtd_info *); @@ -351,6 +353,8 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) mtd->name = map->name; mtd->writesize = 1; + mtd->reboot_notifier.notifier_call = cfi_amdstd_reboot; + if (cfi->cfi_mode==CFI_MODE_CFI){ unsigned char bootloc; /* @@ -487,6 +491,7 @@ static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) #endif __module_get(THIS_MODULE); + register_reboot_notifier(&mtd->reboot_notifier); return mtd; setup_err: @@ -628,6 +633,10 @@ static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr chip->state = FL_READY; return 0; + case FL_SHUTDOWN: + /* The machine is rebooting */ + return -EIO; + case FL_POINT: /* Only if there's no operation suspended... */ if (mode == FL_READY && chip->oldstate == FL_READY) @@ -1918,11 +1927,58 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) } } + +/* + * Ensure that the flash device is put back into read array mode before + * unloading the driver or rebooting. On some systems, rebooting while + * the flash is in query/program/erase mode will prevent the CPU from + * fetching the bootloader code, requiring a hard reset or power cycle. + */ +static int cfi_amdstd_reset(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i, ret; + struct flchip *chip; + + for (i = 0; i < cfi->numchips; i++) { + + chip = &cfi->chips[i]; + + mutex_lock(&chip->mutex); + + ret = get_chip(map, chip, chip->start, FL_SHUTDOWN); + if (!ret) { + map_write(map, CMD(0xF0), chip->start); + chip->state = FL_SHUTDOWN; + put_chip(map, chip, chip->start); + } + + mutex_unlock(&chip->mutex); + } + + return 0; +} + + +static int cfi_amdstd_reboot(struct notifier_block *nb, unsigned long val, + void *v) +{ + struct mtd_info *mtd; + + mtd = container_of(nb, struct mtd_info, reboot_notifier); + cfi_amdstd_reset(mtd); + return NOTIFY_DONE; +} + + static void cfi_amdstd_destroy(struct mtd_info *mtd) { struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; + cfi_amdstd_reset(mtd); + unregister_reboot_notifier(&mtd->reboot_notifier); kfree(cfi->cmdset_priv); kfree(cfi->cfiq); kfree(cfi); -- cgit v1.2.3-70-g09d2 From 29da3380de40e8aa908eb70fa09a54c288b0b3f4 Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Thu, 29 Apr 2010 17:52:57 -0500 Subject: mtd: sst25l: remove unnecessary MTD_DEBUG_LEVEL2 messages All the SST25L series flash parts have uniform erase sectors. Remove the extra MTD_DEBUG_LEVEL2 messages showing the eraseregions info since they could never be shown. Signed-off-by: H Hartley Sweeten Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/devices/sst25l.c | 11 ----------- 1 file changed, 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index 0a11721f146..175c5233f29 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -410,17 +410,6 @@ static int __init sst25l_probe(struct spi_device *spi) flash->mtd.erasesize, flash->mtd.erasesize / 1024, flash->mtd.numeraseregions); - if (flash->mtd.numeraseregions) - for (i = 0; i < flash->mtd.numeraseregions; i++) - DEBUG(MTD_DEBUG_LEVEL2, - "mtd.eraseregions[%d] = { .offset = 0x%llx, " - ".erasesize = 0x%.8x (%uKiB), " - ".numblocks = %d }\n", - i, (long long)flash->mtd.eraseregions[i].offset, - flash->mtd.eraseregions[i].erasesize, - flash->mtd.eraseregions[i].erasesize / 1024, - flash->mtd.eraseregions[i].numblocks); - if (mtd_has_partitions()) { struct mtd_partition *parts = NULL; int nr_parts = 0; -- cgit v1.2.3-70-g09d2 From 6f1f3d0ab5c3eeea9f04486481c25e9afdfa26c5 Mon Sep 17 00:00:00 2001 From: Steve Deiters Date: Wed, 5 May 2010 13:48:38 -0500 Subject: mtd: mpc5121_nfc: Changed SVR check to allow MPC5123. The revision in SVR for MPC5123 is 3. The NFC is the same as MPC5121 revision 2. Signed-off-by: Steve Deiters Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/nand/mpc5121_nfc.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index d7333f4dae8..f713b15fa45 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -666,10 +666,10 @@ static int __devinit mpc5121_nfc_probe(struct of_device *op, /* * Check SoC revision. This driver supports only NFC - * in MPC5121 revision 2. + * in MPC5121 revision 2 and MPC5123 revision 3. */ rev = (mfspr(SPRN_SVR) >> 4) & 0xF; - if (rev != 2) { + if ((rev != 2) && (rev != 3)) { dev_err(dev, "SoC revision %u is not supported!\n", rev); return -ENXIO; } -- cgit v1.2.3-70-g09d2 From ce082596ae4308f67f0953a67db508bb085520fa Mon Sep 17 00:00:00 2001 From: Jason Roberts Date: Thu, 13 May 2010 15:57:33 +0100 Subject: mtd/nand: Add Intel Moorestown/Denali NAND support There is more work to be done on this but it is basically working now. Signed-off-by: Jason Roberts Signed-off-by: David Woodhouse --- drivers/mtd/nand/Kconfig | 17 + drivers/mtd/nand/Makefile | 1 + drivers/mtd/nand/denali.c | 2134 +++++++++++++++++++++++++++++++++++++++++++++ drivers/mtd/nand/denali.h | 816 +++++++++++++++++ 4 files changed, 2968 insertions(+) create mode 100644 drivers/mtd/nand/denali.c create mode 100644 drivers/mtd/nand/denali.h (limited to 'drivers') diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 8f402d46a36..98a04b3c952 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -50,6 +50,23 @@ config MTD_NAND_AUTCPU12 This enables the driver for the autronix autcpu12 board to access the SmartMediaCard. +config MTD_NAND_DENALI + depends on PCI + tristate "Support Denali NAND controller on Intel Moorestown" + help + Enable the driver for NAND flash on Intel Moorestown, using the + Denali NAND controller core. + +config MTD_NAND_DENALI_SCRATCH_REG_ADDR + hex "Denali NAND size scratch register address" + default "0xFF108018" + help + Some platforms place the NAND chip size in a scratch register + because (some versions of) the driver aren't able to automatically + determine the size of certain chips. Set the address of the + scratch register here to enable this feature. On Intel Moorestown + boards, the scratch register is at 0xFF108018. + config MTD_NAND_EDB7312 tristate "Support for Cirrus Logic EBD7312 evaluation board" depends on ARCH_EDB7312 diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index 04bccf9d7b5..e8ab884ba47 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -11,6 +11,7 @@ obj-$(CONFIG_MTD_NAND_CAFE) += cafe_nand.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o obj-$(CONFIG_MTD_NAND_AMS_DELTA) += ams-delta.o obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o +obj-$(CONFIG_MTD_NAND_DENALI) += denali.o obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o obj-$(CONFIG_MTD_NAND_AU1550) += au1550nd.o obj-$(CONFIG_MTD_NAND_BF5XX) += bf5xx_nand.o diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c new file mode 100644 index 00000000000..8a6ce0dd953 --- /dev/null +++ b/drivers/mtd/nand/denali.c @@ -0,0 +1,2134 @@ +/* + * NAND Flash Controller Device Driver + * Copyright © 2009-2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#include "denali.h" + +MODULE_LICENSE("GPL"); + +/* We define a module parameter that allows the user to override + * the hardware and decide what timing mode should be used. + */ +#define NAND_DEFAULT_TIMINGS -1 + +static int onfi_timing_mode = NAND_DEFAULT_TIMINGS; +module_param(onfi_timing_mode, int, S_IRUGO); +MODULE_PARM_DESC(onfi_timing_mode, "Overrides default ONFI setting. -1 indicates" + " use default timings"); + +#define DENALI_NAND_NAME "denali-nand" + +/* We define a macro here that combines all interrupts this driver uses into + * a single constant value, for convenience. */ +#define DENALI_IRQ_ALL (INTR_STATUS0__DMA_CMD_COMP | \ + INTR_STATUS0__ECC_TRANSACTION_DONE | \ + INTR_STATUS0__ECC_ERR | \ + INTR_STATUS0__PROGRAM_FAIL | \ + INTR_STATUS0__LOAD_COMP | \ + INTR_STATUS0__PROGRAM_COMP | \ + INTR_STATUS0__TIME_OUT | \ + INTR_STATUS0__ERASE_FAIL | \ + INTR_STATUS0__RST_COMP | \ + INTR_STATUS0__ERASE_COMP) + +/* indicates whether or not the internal value for the flash bank is + valid or not */ +#define CHIP_SELECT_INVALID -1 + +#define SUPPORT_8BITECC 1 + +/* This macro divides two integers and rounds fractional values up + * to the nearest integer value. */ +#define CEIL_DIV(X, Y) (((X)%(Y)) ? ((X)/(Y)+1) : ((X)/(Y))) + +/* this macro allows us to convert from an MTD structure to our own + * device context (denali) structure. + */ +#define mtd_to_denali(m) container_of(m, struct denali_nand_info, mtd) + +/* These constants are defined by the driver to enable common driver + configuration options. */ +#define SPARE_ACCESS 0x41 +#define MAIN_ACCESS 0x42 +#define MAIN_SPARE_ACCESS 0x43 + +#define DENALI_READ 0 +#define DENALI_WRITE 0x100 + +/* types of device accesses. We can issue commands and get status */ +#define COMMAND_CYCLE 0 +#define ADDR_CYCLE 1 +#define STATUS_CYCLE 2 + +/* this is a helper macro that allows us to + * format the bank into the proper bits for the controller */ +#define BANK(x) ((x) << 24) + +/* List of platforms this NAND controller has be integrated into */ +static const struct pci_device_id denali_pci_ids[] = { + { PCI_VDEVICE(INTEL, 0x0701), INTEL_CE4100 }, + { PCI_VDEVICE(INTEL, 0x0809), INTEL_MRST }, + { /* end: all zeroes */ } +}; + + +/* these are static lookup tables that give us easy access to + registers in the NAND controller. + */ +static const uint32_t intr_status_addresses[4] = {INTR_STATUS0, + INTR_STATUS1, + INTR_STATUS2, + INTR_STATUS3}; + +static const uint32_t device_reset_banks[4] = {DEVICE_RESET__BANK0, + DEVICE_RESET__BANK1, + DEVICE_RESET__BANK2, + DEVICE_RESET__BANK3}; + +static const uint32_t operation_timeout[4] = {INTR_STATUS0__TIME_OUT, + INTR_STATUS1__TIME_OUT, + INTR_STATUS2__TIME_OUT, + INTR_STATUS3__TIME_OUT}; + +static const uint32_t reset_complete[4] = {INTR_STATUS0__RST_COMP, + INTR_STATUS1__RST_COMP, + INTR_STATUS2__RST_COMP, + INTR_STATUS3__RST_COMP}; + +/* specifies the debug level of the driver */ +static int nand_debug_level = 0; + +/* forward declarations */ +static void clear_interrupts(struct denali_nand_info *denali); +static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask); +static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask); +static uint32_t read_interrupt_status(struct denali_nand_info *denali); + +#define DEBUG_DENALI 0 + +/* This is a wrapper for writing to the denali registers. + * this allows us to create debug information so we can + * observe how the driver is programming the device. + * it uses standard linux convention for (val, addr) */ +static void denali_write32(uint32_t value, void *addr) +{ + iowrite32(value, addr); + +#if DEBUG_DENALI + printk(KERN_ERR "wrote: 0x%x -> 0x%x\n", value, (uint32_t)((uint32_t)addr & 0x1fff)); +#endif +} + +/* Certain operations for the denali NAND controller use an indexed mode to read/write + data. The operation is performed by writing the address value of the command to + the device memory followed by the data. This function abstracts this common + operation. +*/ +static void index_addr(struct denali_nand_info *denali, uint32_t address, uint32_t data) +{ + denali_write32(address, denali->flash_mem); + denali_write32(data, denali->flash_mem + 0x10); +} + +/* Perform an indexed read of the device */ +static void index_addr_read_data(struct denali_nand_info *denali, + uint32_t address, uint32_t *pdata) +{ + denali_write32(address, denali->flash_mem); + *pdata = ioread32(denali->flash_mem + 0x10); +} + +/* We need to buffer some data for some of the NAND core routines. + * The operations manage buffering that data. */ +static void reset_buf(struct denali_nand_info *denali) +{ + denali->buf.head = denali->buf.tail = 0; +} + +static void write_byte_to_buf(struct denali_nand_info *denali, uint8_t byte) +{ + BUG_ON(denali->buf.tail >= sizeof(denali->buf.buf)); + denali->buf.buf[denali->buf.tail++] = byte; +} + +/* reads the status of the device */ +static void read_status(struct denali_nand_info *denali) +{ + uint32_t cmd = 0x0; + + /* initialize the data buffer to store status */ + reset_buf(denali); + + /* initiate a device status read */ + cmd = MODE_11 | BANK(denali->flash_bank); + index_addr(denali, cmd | COMMAND_CYCLE, 0x70); + denali_write32(cmd | STATUS_CYCLE, denali->flash_mem); + + /* update buffer with status value */ + write_byte_to_buf(denali, ioread32(denali->flash_mem + 0x10)); + +#if DEBUG_DENALI + printk("device reporting status value of 0x%2x\n", denali->buf.buf[0]); +#endif +} + +/* resets a specific device connected to the core */ +static void reset_bank(struct denali_nand_info *denali) +{ + uint32_t irq_status = 0; + uint32_t irq_mask = reset_complete[denali->flash_bank] | + operation_timeout[denali->flash_bank]; + int bank = 0; + + clear_interrupts(denali); + + bank = device_reset_banks[denali->flash_bank]; + denali_write32(bank, denali->flash_reg + DEVICE_RESET); + + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status & operation_timeout[denali->flash_bank]) + { + printk(KERN_ERR "reset bank failed.\n"); + } +} + +/* Reset the flash controller */ +static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali) +{ + uint32_t i; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) + denali_write32(reset_complete[i] | operation_timeout[i], + denali->flash_reg + intr_status_addresses[i]); + + for (i = 0 ; i < LLD_MAX_FLASH_BANKS; i++) { + denali_write32(device_reset_banks[i], denali->flash_reg + DEVICE_RESET); + while (!(ioread32(denali->flash_reg + intr_status_addresses[i]) & + (reset_complete[i] | operation_timeout[i]))) + ; + if (ioread32(denali->flash_reg + intr_status_addresses[i]) & + operation_timeout[i]) + nand_dbg_print(NAND_DBG_WARN, + "NAND Reset operation timed out on bank %d\n", i); + } + + for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) + denali_write32(reset_complete[i] | operation_timeout[i], + denali->flash_reg + intr_status_addresses[i]); + + return PASS; +} + +/* this routine calculates the ONFI timing values for a given mode and programs + * the clocking register accordingly. The mode is determined by the get_onfi_nand_para + routine. + */ +static void NAND_ONFi_Timing_Mode(struct denali_nand_info *denali, uint16_t mode) +{ + uint16_t Trea[6] = {40, 30, 25, 20, 20, 16}; + uint16_t Trp[6] = {50, 25, 17, 15, 12, 10}; + uint16_t Treh[6] = {30, 15, 15, 10, 10, 7}; + uint16_t Trc[6] = {100, 50, 35, 30, 25, 20}; + uint16_t Trhoh[6] = {0, 15, 15, 15, 15, 15}; + uint16_t Trloh[6] = {0, 0, 0, 0, 5, 5}; + uint16_t Tcea[6] = {100, 45, 30, 25, 25, 25}; + uint16_t Tadl[6] = {200, 100, 100, 100, 70, 70}; + uint16_t Trhw[6] = {200, 100, 100, 100, 100, 100}; + uint16_t Trhz[6] = {200, 100, 100, 100, 100, 100}; + uint16_t Twhr[6] = {120, 80, 80, 60, 60, 60}; + uint16_t Tcs[6] = {70, 35, 25, 25, 20, 15}; + + uint16_t TclsRising = 1; + uint16_t data_invalid_rhoh, data_invalid_rloh, data_invalid; + uint16_t dv_window = 0; + uint16_t en_lo, en_hi; + uint16_t acc_clks; + uint16_t addr_2_data, re_2_we, re_2_re, we_2_re, cs_cnt; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + en_lo = CEIL_DIV(Trp[mode], CLK_X); + en_hi = CEIL_DIV(Treh[mode], CLK_X); +#if ONFI_BLOOM_TIME + if ((en_hi * CLK_X) < (Treh[mode] + 2)) + en_hi++; +#endif + + if ((en_lo + en_hi) * CLK_X < Trc[mode]) + en_lo += CEIL_DIV((Trc[mode] - (en_lo + en_hi) * CLK_X), CLK_X); + + if ((en_lo + en_hi) < CLK_MULTI) + en_lo += CLK_MULTI - en_lo - en_hi; + + while (dv_window < 8) { + data_invalid_rhoh = en_lo * CLK_X + Trhoh[mode]; + + data_invalid_rloh = (en_lo + en_hi) * CLK_X + Trloh[mode]; + + data_invalid = + data_invalid_rhoh < + data_invalid_rloh ? data_invalid_rhoh : data_invalid_rloh; + + dv_window = data_invalid - Trea[mode]; + + if (dv_window < 8) + en_lo++; + } + + acc_clks = CEIL_DIV(Trea[mode], CLK_X); + + while (((acc_clks * CLK_X) - Trea[mode]) < 3) + acc_clks++; + + if ((data_invalid - acc_clks * CLK_X) < 2) + nand_dbg_print(NAND_DBG_WARN, "%s, Line %d: Warning!\n", + __FILE__, __LINE__); + + addr_2_data = CEIL_DIV(Tadl[mode], CLK_X); + re_2_we = CEIL_DIV(Trhw[mode], CLK_X); + re_2_re = CEIL_DIV(Trhz[mode], CLK_X); + we_2_re = CEIL_DIV(Twhr[mode], CLK_X); + cs_cnt = CEIL_DIV((Tcs[mode] - Trp[mode]), CLK_X); + if (!TclsRising) + cs_cnt = CEIL_DIV(Tcs[mode], CLK_X); + if (cs_cnt == 0) + cs_cnt = 1; + + if (Tcea[mode]) { + while (((cs_cnt * CLK_X) + Trea[mode]) < Tcea[mode]) + cs_cnt++; + } + +#if MODE5_WORKAROUND + if (mode == 5) + acc_clks = 5; +#endif + + /* Sighting 3462430: Temporary hack for MT29F128G08CJABAWP:B */ + if ((ioread32(denali->flash_reg + MANUFACTURER_ID) == 0) && + (ioread32(denali->flash_reg + DEVICE_ID) == 0x88)) + acc_clks = 6; + + denali_write32(acc_clks, denali->flash_reg + ACC_CLKS); + denali_write32(re_2_we, denali->flash_reg + RE_2_WE); + denali_write32(re_2_re, denali->flash_reg + RE_2_RE); + denali_write32(we_2_re, denali->flash_reg + WE_2_RE); + denali_write32(addr_2_data, denali->flash_reg + ADDR_2_DATA); + denali_write32(en_lo, denali->flash_reg + RDWR_EN_LO_CNT); + denali_write32(en_hi, denali->flash_reg + RDWR_EN_HI_CNT); + denali_write32(cs_cnt, denali->flash_reg + CS_SETUP_CNT); +} + +/* configures the initial ECC settings for the controller */ +static void set_ecc_config(struct denali_nand_info *denali) +{ +#if SUPPORT_8BITECC + if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) < 4096) || + (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) <= 128)) + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + + if ((ioread32(denali->flash_reg + ECC_CORRECTION) & ECC_CORRECTION__VALUE) + == 1) { + denali->dev_info.wECCBytesPerSector = 4; + denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; + denali->dev_info.wNumPageSpareFlag = + denali->dev_info.wPageSpareSize - + denali->dev_info.wPageDataSize / + (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * + denali->dev_info.wECCBytesPerSector + - denali->dev_info.wSpareSkipBytes; + } else { + denali->dev_info.wECCBytesPerSector = + (ioread32(denali->flash_reg + ECC_CORRECTION) & + ECC_CORRECTION__VALUE) * 13 / 8; + if ((denali->dev_info.wECCBytesPerSector) % 2 == 0) + denali->dev_info.wECCBytesPerSector += 2; + else + denali->dev_info.wECCBytesPerSector += 1; + + denali->dev_info.wECCBytesPerSector *= denali->dev_info.wDevicesConnected; + denali->dev_info.wNumPageSpareFlag = denali->dev_info.wPageSpareSize - + denali->dev_info.wPageDataSize / + (ECC_SECTOR_SIZE * denali->dev_info.wDevicesConnected) * + denali->dev_info.wECCBytesPerSector + - denali->dev_info.wSpareSkipBytes; + } +} + +/* queries the NAND device to see what ONFI modes it supports. */ +static uint16_t get_onfi_nand_para(struct denali_nand_info *denali) +{ + int i; + uint16_t blks_lun_l, blks_lun_h, n_of_luns; + uint32_t blockperlun, id; + + denali_write32(DEVICE_RESET__BANK0, denali->flash_reg + DEVICE_RESET); + + while (!((ioread32(denali->flash_reg + INTR_STATUS0) & + INTR_STATUS0__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS0) & + INTR_STATUS0__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS0) & INTR_STATUS0__RST_COMP) { + denali_write32(DEVICE_RESET__BANK1, denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS1) & + INTR_STATUS1__RST_COMP) { + denali_write32(DEVICE_RESET__BANK2, + denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__TIME_OUT))) + ; + + if (ioread32(denali->flash_reg + INTR_STATUS2) & + INTR_STATUS2__RST_COMP) { + denali_write32(DEVICE_RESET__BANK3, + denali->flash_reg + DEVICE_RESET); + while (!((ioread32(denali->flash_reg + INTR_STATUS3) & + INTR_STATUS3__RST_COMP) | + (ioread32(denali->flash_reg + INTR_STATUS3) & + INTR_STATUS3__TIME_OUT))) + ; + } else { + printk(KERN_ERR "Getting a time out for bank 2!\n"); + } + } else { + printk(KERN_ERR "Getting a time out for bank 1!\n"); + } + } + + denali_write32(INTR_STATUS0__TIME_OUT, denali->flash_reg + INTR_STATUS0); + denali_write32(INTR_STATUS1__TIME_OUT, denali->flash_reg + INTR_STATUS1); + denali_write32(INTR_STATUS2__TIME_OUT, denali->flash_reg + INTR_STATUS2); + denali_write32(INTR_STATUS3__TIME_OUT, denali->flash_reg + INTR_STATUS3); + + denali->dev_info.wONFIDevFeatures = + ioread32(denali->flash_reg + ONFI_DEVICE_FEATURES); + denali->dev_info.wONFIOptCommands = + ioread32(denali->flash_reg + ONFI_OPTIONAL_COMMANDS); + denali->dev_info.wONFITimingMode = + ioread32(denali->flash_reg + ONFI_TIMING_MODE); + denali->dev_info.wONFIPgmCacheTimingMode = + ioread32(denali->flash_reg + ONFI_PGM_CACHE_TIMING_MODE); + + n_of_luns = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & + ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS; + blks_lun_l = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L); + blks_lun_h = ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U); + + blockperlun = (blks_lun_h << 16) | blks_lun_l; + + denali->dev_info.wTotalBlocks = n_of_luns * blockperlun; + + if (!(ioread32(denali->flash_reg + ONFI_TIMING_MODE) & + ONFI_TIMING_MODE__VALUE)) + return FAIL; + + for (i = 5; i > 0; i--) { + if (ioread32(denali->flash_reg + ONFI_TIMING_MODE) & (0x01 << i)) + break; + } + + NAND_ONFi_Timing_Mode(denali, i); + + index_addr(denali, MODE_11 | 0, 0x90); + index_addr(denali, MODE_11 | 1, 0); + + for (i = 0; i < 3; i++) + index_addr_read_data(denali, MODE_11 | 2, &id); + + nand_dbg_print(NAND_DBG_DEBUG, "3rd ID: 0x%x\n", id); + + denali->dev_info.MLCDevice = id & 0x0C; + + /* By now, all the ONFI devices we know support the page cache */ + /* rw feature. So here we enable the pipeline_rw_ahead feature */ + /* iowrite32(1, denali->flash_reg + CACHE_WRITE_ENABLE); */ + /* iowrite32(1, denali->flash_reg + CACHE_READ_ENABLE); */ + + return PASS; +} + +static void get_samsung_nand_para(struct denali_nand_info *denali) +{ + uint8_t no_of_planes; + uint32_t blk_size; + uint64_t plane_size, capacity; + uint32_t id_bytes[5]; + int i; + + index_addr(denali, (uint32_t)(MODE_11 | 0), 0x90); + index_addr(denali, (uint32_t)(MODE_11 | 1), 0); + for (i = 0; i < 5; i++) + index_addr_read_data(denali, (uint32_t)(MODE_11 | 2), &id_bytes[i]); + + nand_dbg_print(NAND_DBG_DEBUG, + "ID bytes: 0x%x, 0x%x, 0x%x, 0x%x, 0x%x\n", + id_bytes[0], id_bytes[1], id_bytes[2], + id_bytes[3], id_bytes[4]); + + if ((id_bytes[1] & 0xff) == 0xd3) { /* Samsung K9WAG08U1A */ + /* Set timing register values according to datasheet */ + denali_write32(5, denali->flash_reg + ACC_CLKS); + denali_write32(20, denali->flash_reg + RE_2_WE); + denali_write32(12, denali->flash_reg + WE_2_RE); + denali_write32(14, denali->flash_reg + ADDR_2_DATA); + denali_write32(3, denali->flash_reg + RDWR_EN_LO_CNT); + denali_write32(2, denali->flash_reg + RDWR_EN_HI_CNT); + denali_write32(2, denali->flash_reg + CS_SETUP_CNT); + } + + no_of_planes = 1 << ((id_bytes[4] & 0x0c) >> 2); + plane_size = (uint64_t)64 << ((id_bytes[4] & 0x70) >> 4); + blk_size = 64 << ((ioread32(denali->flash_reg + DEVICE_PARAM_1) & 0x30) >> 4); + capacity = (uint64_t)128 * plane_size * no_of_planes; + + do_div(capacity, blk_size); + denali->dev_info.wTotalBlocks = capacity; +} + +static void get_toshiba_nand_para(struct denali_nand_info *denali) +{ + void __iomem *scratch_reg; + uint32_t tmp; + + /* Workaround to fix a controller bug which reports a wrong */ + /* spare area size for some kind of Toshiba NAND device */ + if ((ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE) == 4096) && + (ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE) == 64)) { + denali_write32(216, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + tmp = ioread32(denali->flash_reg + DEVICES_CONNECTED) * + ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + denali_write32(tmp, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); +#if SUPPORT_15BITECC + denali_write32(15, denali->flash_reg + ECC_CORRECTION); +#elif SUPPORT_8BITECC + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + } + + /* As Toshiba NAND can not provide it's block number, */ + /* so here we need user to provide the correct block */ + /* number in a scratch register before the Linux NAND */ + /* driver is loaded. If no valid value found in the scratch */ + /* register, then we use default block number value */ + scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); + if (!scratch_reg) { + printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", + __FILE__, __LINE__); + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } else { + nand_dbg_print(NAND_DBG_WARN, + "Spectra: ioremap reg address: 0x%p\n", scratch_reg); + denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); + if (denali->dev_info.wTotalBlocks < 512) + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + iounmap(scratch_reg); + } +} + +static void get_hynix_nand_para(struct denali_nand_info *denali) +{ + void __iomem *scratch_reg; + uint32_t main_size, spare_size; + + switch (denali->dev_info.wDeviceID) { + case 0xD5: /* Hynix H27UAG8T2A, H27UBG8U5A or H27UCG8VFA */ + case 0xD7: /* Hynix H27UDG8VEM, H27UCG8UDM or H27UCG8V5A */ + denali_write32(128, denali->flash_reg + PAGES_PER_BLOCK); + denali_write32(4096, denali->flash_reg + DEVICE_MAIN_AREA_SIZE); + denali_write32(224, denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + main_size = 4096 * ioread32(denali->flash_reg + DEVICES_CONNECTED); + spare_size = 224 * ioread32(denali->flash_reg + DEVICES_CONNECTED); + denali_write32(main_size, denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); + denali_write32(spare_size, denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + denali_write32(0, denali->flash_reg + DEVICE_WIDTH); +#if SUPPORT_15BITECC + denali_write32(15, denali->flash_reg + ECC_CORRECTION); +#elif SUPPORT_8BITECC + denali_write32(8, denali->flash_reg + ECC_CORRECTION); +#endif + denali->dev_info.MLCDevice = 1; + break; + default: + nand_dbg_print(NAND_DBG_WARN, + "Spectra: Unknown Hynix NAND (Device ID: 0x%x)." + "Will use default parameter values instead.\n", + denali->dev_info.wDeviceID); + } + + scratch_reg = ioremap_nocache(SCRATCH_REG_ADDR, SCRATCH_REG_SIZE); + if (!scratch_reg) { + printk(KERN_ERR "Spectra: ioremap failed in %s, Line %d", + __FILE__, __LINE__); + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } else { + nand_dbg_print(NAND_DBG_WARN, + "Spectra: ioremap reg address: 0x%p\n", scratch_reg); + denali->dev_info.wTotalBlocks = 1 << ioread8(scratch_reg); + if (denali->dev_info.wTotalBlocks < 512) + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + iounmap(scratch_reg); + } +} + +/* determines how many NAND chips are connected to the controller. Note for + Intel CE4100 devices we don't support more than one device. + */ +static void find_valid_banks(struct denali_nand_info *denali) +{ + uint32_t id[LLD_MAX_FLASH_BANKS]; + int i; + + denali->total_used_banks = 1; + for (i = 0; i < LLD_MAX_FLASH_BANKS; i++) { + index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 0), 0x90); + index_addr(denali, (uint32_t)(MODE_11 | (i << 24) | 1), 0); + index_addr_read_data(denali, (uint32_t)(MODE_11 | (i << 24) | 2), &id[i]); + + nand_dbg_print(NAND_DBG_DEBUG, + "Return 1st ID for bank[%d]: %x\n", i, id[i]); + + if (i == 0) { + if (!(id[i] & 0x0ff)) + break; /* WTF? */ + } else { + if ((id[i] & 0x0ff) == (id[0] & 0x0ff)) + denali->total_used_banks++; + else + break; + } + } + + if (denali->platform == INTEL_CE4100) + { + /* Platform limitations of the CE4100 device limit + * users to a single chip solution for NAND. + * Multichip support is not enabled. + */ + if (denali->total_used_banks != 1) + { + printk(KERN_ERR "Sorry, Intel CE4100 only supports " + "a single NAND device.\n"); + BUG(); + } + } + nand_dbg_print(NAND_DBG_DEBUG, + "denali->total_used_banks: %d\n", denali->total_used_banks); +} + +static void detect_partition_feature(struct denali_nand_info *denali) +{ + if (ioread32(denali->flash_reg + FEATURES) & FEATURES__PARTITION) { + if ((ioread32(denali->flash_reg + PERM_SRC_ID_1) & + PERM_SRC_ID_1__SRCID) == SPECTRA_PARTITION_ID) { + denali->dev_info.wSpectraStartBlock = + ((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & + MIN_MAX_BANK_1__MIN_VALUE) * + denali->dev_info.wTotalBlocks) + + + (ioread32(denali->flash_reg + MIN_BLK_ADDR_1) & + MIN_BLK_ADDR_1__VALUE); + + denali->dev_info.wSpectraEndBlock = + (((ioread32(denali->flash_reg + MIN_MAX_BANK_1) & + MIN_MAX_BANK_1__MAX_VALUE) >> 2) * + denali->dev_info.wTotalBlocks) + + + (ioread32(denali->flash_reg + MAX_BLK_ADDR_1) & + MAX_BLK_ADDR_1__VALUE); + + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + + if (denali->dev_info.wSpectraEndBlock >= + denali->dev_info.wTotalBlocks) { + denali->dev_info.wSpectraEndBlock = + denali->dev_info.wTotalBlocks - 1; + } + + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } else { + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; + denali->dev_info.wSpectraEndBlock = + denali->dev_info.wTotalBlocks - 1; + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } + } else { + denali->dev_info.wTotalBlocks *= denali->total_used_banks; + denali->dev_info.wSpectraStartBlock = SPECTRA_START_BLOCK; + denali->dev_info.wSpectraEndBlock = denali->dev_info.wTotalBlocks - 1; + denali->dev_info.wDataBlockNum = + denali->dev_info.wSpectraEndBlock - + denali->dev_info.wSpectraStartBlock + 1; + } +} + +static void dump_device_info(struct denali_nand_info *denali) +{ + nand_dbg_print(NAND_DBG_DEBUG, "denali->dev_info:\n"); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceMaker: 0x%x\n", + denali->dev_info.wDeviceMaker); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceID: 0x%x\n", + denali->dev_info.wDeviceID); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceType: 0x%x\n", + denali->dev_info.wDeviceType); + nand_dbg_print(NAND_DBG_DEBUG, "SpectraStartBlock: %d\n", + denali->dev_info.wSpectraStartBlock); + nand_dbg_print(NAND_DBG_DEBUG, "SpectraEndBlock: %d\n", + denali->dev_info.wSpectraEndBlock); + nand_dbg_print(NAND_DBG_DEBUG, "TotalBlocks: %d\n", + denali->dev_info.wTotalBlocks); + nand_dbg_print(NAND_DBG_DEBUG, "PagesPerBlock: %d\n", + denali->dev_info.wPagesPerBlock); + nand_dbg_print(NAND_DBG_DEBUG, "PageSize: %d\n", + denali->dev_info.wPageSize); + nand_dbg_print(NAND_DBG_DEBUG, "PageDataSize: %d\n", + denali->dev_info.wPageDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "PageSpareSize: %d\n", + denali->dev_info.wPageSpareSize); + nand_dbg_print(NAND_DBG_DEBUG, "NumPageSpareFlag: %d\n", + denali->dev_info.wNumPageSpareFlag); + nand_dbg_print(NAND_DBG_DEBUG, "ECCBytesPerSector: %d\n", + denali->dev_info.wECCBytesPerSector); + nand_dbg_print(NAND_DBG_DEBUG, "BlockSize: %d\n", + denali->dev_info.wBlockSize); + nand_dbg_print(NAND_DBG_DEBUG, "BlockDataSize: %d\n", + denali->dev_info.wBlockDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "DataBlockNum: %d\n", + denali->dev_info.wDataBlockNum); + nand_dbg_print(NAND_DBG_DEBUG, "PlaneNum: %d\n", + denali->dev_info.bPlaneNum); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceMainAreaSize: %d\n", + denali->dev_info.wDeviceMainAreaSize); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceSpareAreaSize: %d\n", + denali->dev_info.wDeviceSpareAreaSize); + nand_dbg_print(NAND_DBG_DEBUG, "DevicesConnected: %d\n", + denali->dev_info.wDevicesConnected); + nand_dbg_print(NAND_DBG_DEBUG, "DeviceWidth: %d\n", + denali->dev_info.wDeviceWidth); + nand_dbg_print(NAND_DBG_DEBUG, "HWRevision: 0x%x\n", + denali->dev_info.wHWRevision); + nand_dbg_print(NAND_DBG_DEBUG, "HWFeatures: 0x%x\n", + denali->dev_info.wHWFeatures); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIDevFeatures: 0x%x\n", + denali->dev_info.wONFIDevFeatures); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIOptCommands: 0x%x\n", + denali->dev_info.wONFIOptCommands); + nand_dbg_print(NAND_DBG_DEBUG, "ONFITimingMode: 0x%x\n", + denali->dev_info.wONFITimingMode); + nand_dbg_print(NAND_DBG_DEBUG, "ONFIPgmCacheTimingMode: 0x%x\n", + denali->dev_info.wONFIPgmCacheTimingMode); + nand_dbg_print(NAND_DBG_DEBUG, "MLCDevice: %s\n", + denali->dev_info.MLCDevice ? "Yes" : "No"); + nand_dbg_print(NAND_DBG_DEBUG, "SpareSkipBytes: %d\n", + denali->dev_info.wSpareSkipBytes); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageNumber: %d\n", + denali->dev_info.nBitsInPageNumber); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInPageDataSize: %d\n", + denali->dev_info.nBitsInPageDataSize); + nand_dbg_print(NAND_DBG_DEBUG, "BitsInBlockDataSize: %d\n", + denali->dev_info.nBitsInBlockDataSize); +} + +static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali) +{ + uint16_t status = PASS; + uint8_t no_of_planes; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + denali->dev_info.wDeviceMaker = ioread32(denali->flash_reg + MANUFACTURER_ID); + denali->dev_info.wDeviceID = ioread32(denali->flash_reg + DEVICE_ID); + denali->dev_info.bDeviceParam0 = ioread32(denali->flash_reg + DEVICE_PARAM_0); + denali->dev_info.bDeviceParam1 = ioread32(denali->flash_reg + DEVICE_PARAM_1); + denali->dev_info.bDeviceParam2 = ioread32(denali->flash_reg + DEVICE_PARAM_2); + + denali->dev_info.MLCDevice = ioread32(denali->flash_reg + DEVICE_PARAM_0) & 0x0c; + + if (ioread32(denali->flash_reg + ONFI_DEVICE_NO_OF_LUNS) & + ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE) { /* ONFI 1.0 NAND */ + if (FAIL == get_onfi_nand_para(denali)) + return FAIL; + } else if (denali->dev_info.wDeviceMaker == 0xEC) { /* Samsung NAND */ + get_samsung_nand_para(denali); + } else if (denali->dev_info.wDeviceMaker == 0x98) { /* Toshiba NAND */ + get_toshiba_nand_para(denali); + } else if (denali->dev_info.wDeviceMaker == 0xAD) { /* Hynix NAND */ + get_hynix_nand_para(denali); + } else { + denali->dev_info.wTotalBlocks = GLOB_HWCTL_DEFAULT_BLKS; + } + + nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" + "acc_clks: %d, re_2_we: %d, we_2_re: %d," + "addr_2_data: %d, rdwr_en_lo_cnt: %d, " + "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n", + ioread32(denali->flash_reg + ACC_CLKS), + ioread32(denali->flash_reg + RE_2_WE), + ioread32(denali->flash_reg + WE_2_RE), + ioread32(denali->flash_reg + ADDR_2_DATA), + ioread32(denali->flash_reg + RDWR_EN_LO_CNT), + ioread32(denali->flash_reg + RDWR_EN_HI_CNT), + ioread32(denali->flash_reg + CS_SETUP_CNT)); + + denali->dev_info.wHWRevision = ioread32(denali->flash_reg + REVISION); + denali->dev_info.wHWFeatures = ioread32(denali->flash_reg + FEATURES); + + denali->dev_info.wDeviceMainAreaSize = + ioread32(denali->flash_reg + DEVICE_MAIN_AREA_SIZE); + denali->dev_info.wDeviceSpareAreaSize = + ioread32(denali->flash_reg + DEVICE_SPARE_AREA_SIZE); + + denali->dev_info.wPageDataSize = + ioread32(denali->flash_reg + LOGICAL_PAGE_DATA_SIZE); + + /* Note: When using the Micon 4K NAND device, the controller will report + * Page Spare Size as 216 bytes. But Micron's Spec say it's 218 bytes. + * And if force set it to 218 bytes, the controller can not work + * correctly. So just let it be. But keep in mind that this bug may + * cause + * other problems in future. - Yunpeng 2008-10-10 + */ + denali->dev_info.wPageSpareSize = + ioread32(denali->flash_reg + LOGICAL_PAGE_SPARE_SIZE); + + denali->dev_info.wPagesPerBlock = ioread32(denali->flash_reg + PAGES_PER_BLOCK); + + denali->dev_info.wPageSize = + denali->dev_info.wPageDataSize + denali->dev_info.wPageSpareSize; + denali->dev_info.wBlockSize = + denali->dev_info.wPageSize * denali->dev_info.wPagesPerBlock; + denali->dev_info.wBlockDataSize = + denali->dev_info.wPagesPerBlock * denali->dev_info.wPageDataSize; + + denali->dev_info.wDeviceWidth = ioread32(denali->flash_reg + DEVICE_WIDTH); + denali->dev_info.wDeviceType = + ((ioread32(denali->flash_reg + DEVICE_WIDTH) > 0) ? 16 : 8); + + denali->dev_info.wDevicesConnected = ioread32(denali->flash_reg + DEVICES_CONNECTED); + + denali->dev_info.wSpareSkipBytes = + ioread32(denali->flash_reg + SPARE_AREA_SKIP_BYTES) * + denali->dev_info.wDevicesConnected; + + denali->dev_info.nBitsInPageNumber = + ilog2(denali->dev_info.wPagesPerBlock); + denali->dev_info.nBitsInPageDataSize = + ilog2(denali->dev_info.wPageDataSize); + denali->dev_info.nBitsInBlockDataSize = + ilog2(denali->dev_info.wBlockDataSize); + + set_ecc_config(denali); + + no_of_planes = ioread32(denali->flash_reg + NUMBER_OF_PLANES) & + NUMBER_OF_PLANES__VALUE; + + switch (no_of_planes) { + case 0: + case 1: + case 3: + case 7: + denali->dev_info.bPlaneNum = no_of_planes + 1; + break; + default: + status = FAIL; + break; + } + + find_valid_banks(denali); + + detect_partition_feature(denali); + + dump_device_info(denali); + + /* If the user specified to override the default timings + * with a specific ONFI mode, we apply those changes here. + */ + if (onfi_timing_mode != NAND_DEFAULT_TIMINGS) + { + NAND_ONFi_Timing_Mode(denali, onfi_timing_mode); + } + + return status; +} + +static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, + uint16_t INT_ENABLE) +{ + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + if (INT_ENABLE) + denali_write32(1, denali->flash_reg + GLOBAL_INT_ENABLE); + else + denali_write32(0, denali->flash_reg + GLOBAL_INT_ENABLE); +} + +/* validation function to verify that the controlling software is making + a valid request + */ +static inline bool is_flash_bank_valid(int flash_bank) +{ + return (flash_bank >= 0 && flash_bank < 4); +} + +static void denali_irq_init(struct denali_nand_info *denali) +{ + uint32_t int_mask = 0; + + /* Disable global interrupts */ + NAND_LLD_Enable_Disable_Interrupts(denali, false); + + int_mask = DENALI_IRQ_ALL; + + /* Clear all status bits */ + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS0); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS1); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS2); + denali_write32(0xFFFF, denali->flash_reg + INTR_STATUS3); + + denali_irq_enable(denali, int_mask); +} + +static void denali_irq_cleanup(int irqnum, struct denali_nand_info *denali) +{ + NAND_LLD_Enable_Disable_Interrupts(denali, false); + free_irq(irqnum, denali); +} + +static void denali_irq_enable(struct denali_nand_info *denali, uint32_t int_mask) +{ + denali_write32(int_mask, denali->flash_reg + INTR_EN0); + denali_write32(int_mask, denali->flash_reg + INTR_EN1); + denali_write32(int_mask, denali->flash_reg + INTR_EN2); + denali_write32(int_mask, denali->flash_reg + INTR_EN3); +} + +/* This function only returns when an interrupt that this driver cares about + * occurs. This is to reduce the overhead of servicing interrupts + */ +static inline uint32_t denali_irq_detected(struct denali_nand_info *denali) +{ + return (read_interrupt_status(denali) & DENALI_IRQ_ALL); +} + +/* Interrupts are cleared by writing a 1 to the appropriate status bit */ +static inline void clear_interrupt(struct denali_nand_info *denali, uint32_t irq_mask) +{ + uint32_t intr_status_reg = 0; + + intr_status_reg = intr_status_addresses[denali->flash_bank]; + + denali_write32(irq_mask, denali->flash_reg + intr_status_reg); +} + +static void clear_interrupts(struct denali_nand_info *denali) +{ + uint32_t status = 0x0; + spin_lock_irq(&denali->irq_lock); + + status = read_interrupt_status(denali); + +#if DEBUG_DENALI + denali->irq_debug_array[denali->idx++] = 0x30000000 | status; + denali->idx %= 32; +#endif + + denali->irq_status = 0x0; + spin_unlock_irq(&denali->irq_lock); +} + +static uint32_t read_interrupt_status(struct denali_nand_info *denali) +{ + uint32_t intr_status_reg = 0; + + intr_status_reg = intr_status_addresses[denali->flash_bank]; + + return ioread32(denali->flash_reg + intr_status_reg); +} + +#if DEBUG_DENALI +static void print_irq_log(struct denali_nand_info *denali) +{ + int i = 0; + + printk("ISR debug log index = %X\n", denali->idx); + for (i = 0; i < 32; i++) + { + printk("%08X: %08X\n", i, denali->irq_debug_array[i]); + } +} +#endif + +/* This is the interrupt service routine. It handles all interrupts + * sent to this device. Note that on CE4100, this is a shared + * interrupt. + */ +static irqreturn_t denali_isr(int irq, void *dev_id) +{ + struct denali_nand_info *denali = dev_id; + uint32_t irq_status = 0x0; + irqreturn_t result = IRQ_NONE; + + spin_lock(&denali->irq_lock); + + /* check to see if a valid NAND chip has + * been selected. + */ + if (is_flash_bank_valid(denali->flash_bank)) + { + /* check to see if controller generated + * the interrupt, since this is a shared interrupt */ + if ((irq_status = denali_irq_detected(denali)) != 0) + { +#if DEBUG_DENALI + denali->irq_debug_array[denali->idx++] = 0x10000000 | irq_status; + denali->idx %= 32; + + printk("IRQ status = 0x%04x\n", irq_status); +#endif + /* handle interrupt */ + /* first acknowledge it */ + clear_interrupt(denali, irq_status); + /* store the status in the device context for someone + to read */ + denali->irq_status |= irq_status; + /* notify anyone who cares that it happened */ + complete(&denali->complete); + /* tell the OS that we've handled this */ + result = IRQ_HANDLED; + } + } + spin_unlock(&denali->irq_lock); + return result; +} +#define BANK(x) ((x) << 24) + +static uint32_t wait_for_irq(struct denali_nand_info *denali, uint32_t irq_mask) +{ + unsigned long comp_res = 0; + uint32_t intr_status = 0; + bool retry = false; + unsigned long timeout = msecs_to_jiffies(1000); + + do + { +#if DEBUG_DENALI + printk("waiting for 0x%x\n", irq_mask); +#endif + comp_res = wait_for_completion_timeout(&denali->complete, timeout); + spin_lock_irq(&denali->irq_lock); + intr_status = denali->irq_status; + +#if DEBUG_DENALI + denali->irq_debug_array[denali->idx++] = 0x20000000 | (irq_mask << 16) | intr_status; + denali->idx %= 32; +#endif + + if (intr_status & irq_mask) + { + denali->irq_status &= ~irq_mask; + spin_unlock_irq(&denali->irq_lock); +#if DEBUG_DENALI + if (retry) printk("status on retry = 0x%x\n", intr_status); +#endif + /* our interrupt was detected */ + break; + } + else + { + /* these are not the interrupts you are looking for - + need to wait again */ + spin_unlock_irq(&denali->irq_lock); +#if DEBUG_DENALI + print_irq_log(denali); + printk("received irq nobody cared: irq_status = 0x%x," + " irq_mask = 0x%x, timeout = %ld\n", intr_status, irq_mask, comp_res); +#endif + retry = true; + } + } while (comp_res != 0); + + if (comp_res == 0) + { + /* timeout */ + printk(KERN_ERR "timeout occurred, status = 0x%x, mask = 0x%x\n", + intr_status, irq_mask); + + intr_status = 0; + } + return intr_status; +} + +/* This helper function setups the registers for ECC and whether or not + the spare area will be transfered. */ +static void setup_ecc_for_xfer(struct denali_nand_info *denali, bool ecc_en, + bool transfer_spare) +{ + int ecc_en_flag = 0, transfer_spare_flag = 0; + + /* set ECC, transfer spare bits if needed */ + ecc_en_flag = ecc_en ? ECC_ENABLE__FLAG : 0; + transfer_spare_flag = transfer_spare ? TRANSFER_SPARE_REG__FLAG : 0; + + /* Enable spare area/ECC per user's request. */ + denali_write32(ecc_en_flag, denali->flash_reg + ECC_ENABLE); + denali_write32(transfer_spare_flag, denali->flash_reg + TRANSFER_SPARE_REG); +} + +/* sends a pipeline command operation to the controller. See the Denali NAND + controller's user guide for more information (section 4.2.3.6). + */ +static int denali_send_pipeline_cmd(struct denali_nand_info *denali, bool ecc_en, + bool transfer_spare, int access_type, + int op) +{ + int status = PASS; + uint32_t addr = 0x0, cmd = 0x0, page_count = 1, irq_status = 0, + irq_mask = 0; + + if (op == DENALI_READ) irq_mask = INTR_STATUS0__LOAD_COMP; + else if (op == DENALI_WRITE) irq_mask = 0; + else BUG(); + + setup_ecc_for_xfer(denali, ecc_en, transfer_spare); + +#if DEBUG_DENALI + spin_lock_irq(&denali->irq_lock); + denali->irq_debug_array[denali->idx++] = 0x40000000 | ioread32(denali->flash_reg + ECC_ENABLE) | (access_type << 4); + denali->idx %= 32; + spin_unlock_irq(&denali->irq_lock); +#endif + + + /* clear interrupts */ + clear_interrupts(denali); + + addr = BANK(denali->flash_bank) | denali->page; + + if (op == DENALI_WRITE && access_type != SPARE_ACCESS) + { + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + else if (op == DENALI_WRITE && access_type == SPARE_ACCESS) + { + /* read spare area */ + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, access_type); + + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + else if (op == DENALI_READ) + { + /* setup page read request for access type */ + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, access_type); + + /* page 33 of the NAND controller spec indicates we should not + use the pipeline commands in Spare area only mode. So we + don't. + */ + if (access_type == SPARE_ACCESS) + { + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + else + { + index_addr(denali, (uint32_t)cmd, 0x2000 | op | page_count); + + /* wait for command to be accepted + * can always use status0 bit as the mask is identical for each + * bank. */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "cmd, page, addr on timeout " + "(0x%x, 0x%x, 0x%x)\n", cmd, denali->page, addr); + status = FAIL; + } + else + { + cmd = MODE_01 | addr; + denali_write32(cmd, denali->flash_mem); + } + } + } + return status; +} + +/* helper function that simply writes a buffer to the flash */ +static int write_data_to_flash_mem(struct denali_nand_info *denali, const uint8_t *buf, + int len) +{ + uint32_t i = 0, *buf32; + + /* verify that the len is a multiple of 4. see comment in + * read_data_from_flash_mem() */ + BUG_ON((len % 4) != 0); + + /* write the data to the flash memory */ + buf32 = (uint32_t *)buf; + for (i = 0; i < len / 4; i++) + { + denali_write32(*buf32++, denali->flash_mem + 0x10); + } + return i*4; /* intent is to return the number of bytes read */ +} + +/* helper function that simply reads a buffer from the flash */ +static int read_data_from_flash_mem(struct denali_nand_info *denali, uint8_t *buf, + int len) +{ + uint32_t i = 0, *buf32; + + /* we assume that len will be a multiple of 4, if not + * it would be nice to know about it ASAP rather than + * have random failures... + * + * This assumption is based on the fact that this + * function is designed to be used to read flash pages, + * which are typically multiples of 4... + */ + + BUG_ON((len % 4) != 0); + + /* transfer the data from the flash */ + buf32 = (uint32_t *)buf; + for (i = 0; i < len / 4; i++) + { + *buf32++ = ioread32(denali->flash_mem + 0x10); + } + return i*4; /* intent is to return the number of bytes read */ +} + +/* writes OOB data to the device */ +static int write_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__PROGRAM_COMP | + INTR_STATUS0__PROGRAM_FAIL; + int status = 0; + + denali->page = page; + + if (denali_send_pipeline_cmd(denali, false, false, SPARE_ACCESS, + DENALI_WRITE) == PASS) + { + write_data_to_flash_mem(denali, buf, mtd->oobsize); + +#if DEBUG_DENALI + spin_lock_irq(&denali->irq_lock); + denali->irq_debug_array[denali->idx++] = 0x80000000 | mtd->oobsize; + denali->idx %= 32; + spin_unlock_irq(&denali->irq_lock); +#endif + + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "OOB write failed\n"); + status = -EIO; + } + } + else + { + printk(KERN_ERR "unable to send pipeline command\n"); + status = -EIO; + } + return status; +} + +/* reads OOB data from the device */ +static void read_oob_data(struct mtd_info *mtd, uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint32_t irq_mask = INTR_STATUS0__LOAD_COMP, irq_status = 0, addr = 0x0, cmd = 0x0; + + denali->page = page; + +#if DEBUG_DENALI + printk("read_oob %d\n", page); +#endif + if (denali_send_pipeline_cmd(denali, false, true, SPARE_ACCESS, + DENALI_READ) == PASS) + { + read_data_from_flash_mem(denali, buf, mtd->oobsize); + + /* wait for command to be accepted + * can always use status0 bit as the mask is identical for each + * bank. */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "page on OOB timeout %d\n", denali->page); + } + + /* We set the device back to MAIN_ACCESS here as I observed + * instability with the controller if you do a block erase + * and the last transaction was a SPARE_ACCESS. Block erase + * is reliable (according to the MTD test infrastructure) + * if you are in MAIN_ACCESS. + */ + addr = BANK(denali->flash_bank) | denali->page; + cmd = MODE_10 | addr; + index_addr(denali, (uint32_t)cmd, MAIN_ACCESS); + +#if DEBUG_DENALI + spin_lock_irq(&denali->irq_lock); + denali->irq_debug_array[denali->idx++] = 0x60000000 | mtd->oobsize; + denali->idx %= 32; + spin_unlock_irq(&denali->irq_lock); +#endif + } +} + +/* this function examines buffers to see if they contain data that + * indicate that the buffer is part of an erased region of flash. + */ +bool is_erased(uint8_t *buf, int len) +{ + int i = 0; + for (i = 0; i < len; i++) + { + if (buf[i] != 0xFF) + { + return false; + } + } + return true; +} +#define ECC_SECTOR_SIZE 512 + +#define ECC_SECTOR(x) (((x) & ECC_ERROR_ADDRESS__SECTOR_NR) >> 12) +#define ECC_BYTE(x) (((x) & ECC_ERROR_ADDRESS__OFFSET)) +#define ECC_CORRECTION_VALUE(x) ((x) & ERR_CORRECTION_INFO__BYTEMASK) +#define ECC_ERROR_CORRECTABLE(x) (!((x) & ERR_CORRECTION_INFO)) +#define ECC_ERR_DEVICE(x) ((x) & ERR_CORRECTION_INFO__DEVICE_NR >> 8) +#define ECC_LAST_ERR(x) ((x) & ERR_CORRECTION_INFO__LAST_ERR_INFO) + +static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, + uint8_t *oobbuf, uint32_t irq_status) +{ + bool check_erased_page = false; + + if (irq_status & INTR_STATUS0__ECC_ERR) + { + /* read the ECC errors. we'll ignore them for now */ + uint32_t err_address = 0, err_correction_info = 0; + uint32_t err_byte = 0, err_sector = 0, err_device = 0; + uint32_t err_correction_value = 0; + + do + { + err_address = ioread32(denali->flash_reg + + ECC_ERROR_ADDRESS); + err_sector = ECC_SECTOR(err_address); + err_byte = ECC_BYTE(err_address); + + + err_correction_info = ioread32(denali->flash_reg + + ERR_CORRECTION_INFO); + err_correction_value = + ECC_CORRECTION_VALUE(err_correction_info); + err_device = ECC_ERR_DEVICE(err_correction_info); + + if (ECC_ERROR_CORRECTABLE(err_correction_info)) + { + /* offset in our buffer is computed as: + sector number * sector size + offset in + sector + */ + int offset = err_sector * ECC_SECTOR_SIZE + + err_byte; + if (offset < denali->mtd.writesize) + { + /* correct the ECC error */ + buf[offset] ^= err_correction_value; + denali->mtd.ecc_stats.corrected++; + } + else + { + /* bummer, couldn't correct the error */ + printk(KERN_ERR "ECC offset invalid\n"); + denali->mtd.ecc_stats.failed++; + } + } + else + { + /* if the error is not correctable, need to + * look at the page to see if it is an erased page. + * if so, then it's not a real ECC error */ + check_erased_page = true; + } + +#if DEBUG_DENALI + printk("Detected ECC error in page %d: err_addr = 0x%08x," + " info to fix is 0x%08x\n", denali->page, err_address, + err_correction_info); +#endif + } while (!ECC_LAST_ERR(err_correction_info)); + } + return check_erased_page; +} + +/* programs the controller to either enable/disable DMA transfers */ +static void enable_dma(struct denali_nand_info *denali, bool en) +{ + uint32_t reg_val = 0x0; + + if (en) reg_val = DMA_ENABLE__FLAG; + + denali_write32(reg_val, denali->flash_reg + DMA_ENABLE); + ioread32(denali->flash_reg + DMA_ENABLE); +} + +/* setups the HW to perform the data DMA */ +static void setup_dma(struct denali_nand_info *denali, int op) +{ + uint32_t mode = 0x0; + const int page_count = 1; + dma_addr_t addr = denali->buf.dma_buf; + + mode = MODE_10 | BANK(denali->flash_bank); + + /* DMA is a four step process */ + + /* 1. setup transfer type and # of pages */ + index_addr(denali, mode | denali->page, 0x2000 | op | page_count); + + /* 2. set memory high address bits 23:8 */ + index_addr(denali, mode | ((uint16_t)(addr >> 16) << 8), 0x2200); + + /* 3. set memory low address bits 23:8 */ + index_addr(denali, mode | ((uint16_t)addr << 8), 0x2300); + + /* 4. interrupt when complete, burst len = 64 bytes*/ + index_addr(denali, mode | 0x14000, 0x2400); +} + +/* writes a page. user specifies type, and this function handles the + configuration details. */ +static void write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf, bool raw_xfer) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct pci_dev *pci_dev = denali->dev; + + dma_addr_t addr = denali->buf.dma_buf; + size_t size = denali->mtd.writesize + denali->mtd.oobsize; + + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP | + INTR_STATUS0__PROGRAM_FAIL; + + /* if it is a raw xfer, we want to disable ecc, and send + * the spare area. + * !raw_xfer - enable ecc + * raw_xfer - transfer spare + */ + setup_ecc_for_xfer(denali, !raw_xfer, raw_xfer); + + /* copy buffer into DMA buffer */ + memcpy(denali->buf.buf, buf, mtd->writesize); + + if (raw_xfer) + { + /* transfer the data to the spare area */ + memcpy(denali->buf.buf + mtd->writesize, + chip->oob_poi, + mtd->oobsize); + } + + pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_TODEVICE); + + clear_interrupts(denali); + enable_dma(denali, true); + + setup_dma(denali, DENALI_WRITE); + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + if (irq_status == 0) + { + printk(KERN_ERR "timeout on write_page (type = %d)\n", raw_xfer); + denali->status = + (irq_status & INTR_STATUS0__PROGRAM_FAIL) ? NAND_STATUS_FAIL : + PASS; + } + + enable_dma(denali, false); + pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_TODEVICE); +} + +/* NAND core entry points */ + +/* this is the callback that the NAND core calls to write a page. Since + writing a page with ECC or without is similar, all the work is done + by write_page above. */ +static void denali_write_page(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + /* for regular page writes, we let HW handle all the ECC + * data written to the device. */ + write_page(mtd, chip, buf, false); +} + +/* This is the callback that the NAND core calls to write a page without ECC. + raw access is similiar to ECC page writes, so all the work is done in the + write_page() function above. + */ +static void denali_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + const uint8_t *buf) +{ + /* for raw page writes, we want to disable ECC and simply write + whatever data is in the buffer. */ + write_page(mtd, chip, buf, true); +} + +static int denali_write_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page) +{ + return write_oob_data(mtd, chip->oob_poi, page); +} + +static int denali_read_oob(struct mtd_info *mtd, struct nand_chip *chip, + int page, int sndcmd) +{ + read_oob_data(mtd, chip->oob_poi, page); + + return 0; /* notify NAND core to send command to + * NAND device. */ +} + +static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct pci_dev *pci_dev = denali->dev; + + dma_addr_t addr = denali->buf.dma_buf; + size_t size = denali->mtd.writesize + denali->mtd.oobsize; + + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__ECC_TRANSACTION_DONE | + INTR_STATUS0__ECC_ERR; + bool check_erased_page = false; + + setup_ecc_for_xfer(denali, true, false); + + enable_dma(denali, true); + pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + clear_interrupts(denali); + setup_dma(denali, DENALI_READ); + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + memcpy(buf, denali->buf.buf, mtd->writesize); + + check_erased_page = handle_ecc(denali, buf, chip->oob_poi, irq_status); + enable_dma(denali, false); + + if (check_erased_page) + { + read_oob_data(&denali->mtd, chip->oob_poi, denali->page); + + /* check ECC failures that may have occurred on erased pages */ + if (check_erased_page) + { + if (!is_erased(buf, denali->mtd.writesize)) + { + denali->mtd.ecc_stats.failed++; + } + if (!is_erased(buf, denali->mtd.oobsize)) + { + denali->mtd.ecc_stats.failed++; + } + } + } + return 0; +} + +static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, + uint8_t *buf, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + struct pci_dev *pci_dev = denali->dev; + + dma_addr_t addr = denali->buf.dma_buf; + size_t size = denali->mtd.writesize + denali->mtd.oobsize; + + uint32_t irq_status = 0; + uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP; + + setup_ecc_for_xfer(denali, false, true); + enable_dma(denali, true); + + pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + clear_interrupts(denali); + setup_dma(denali, DENALI_READ); + + /* wait for operation to complete */ + irq_status = wait_for_irq(denali, irq_mask); + + pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); + + enable_dma(denali, false); + + memcpy(buf, denali->buf.buf, mtd->writesize); + memcpy(chip->oob_poi, denali->buf.buf + mtd->writesize, mtd->oobsize); + + return 0; +} + +static uint8_t denali_read_byte(struct mtd_info *mtd) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + uint8_t result = 0xff; + + if (denali->buf.head < denali->buf.tail) + { + result = denali->buf.buf[denali->buf.head++]; + } + +#if DEBUG_DENALI + printk("read byte -> 0x%02x\n", result); +#endif + return result; +} + +static void denali_select_chip(struct mtd_info *mtd, int chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); +#if DEBUG_DENALI + printk("denali select chip %d\n", chip); +#endif + spin_lock_irq(&denali->irq_lock); + denali->flash_bank = chip; + spin_unlock_irq(&denali->irq_lock); +} + +static int denali_waitfunc(struct mtd_info *mtd, struct nand_chip *chip) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + int status = denali->status; + denali->status = 0; + +#if DEBUG_DENALI + printk("waitfunc %d\n", status); +#endif + return status; +} + +static void denali_erase(struct mtd_info *mtd, int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + + uint32_t cmd = 0x0, irq_status = 0; + +#if DEBUG_DENALI + printk("erase page: %d\n", page); +#endif + /* clear interrupts */ + clear_interrupts(denali); + + /* setup page read request for access type */ + cmd = MODE_10 | BANK(denali->flash_bank) | page; + index_addr(denali, (uint32_t)cmd, 0x1); + + /* wait for erase to complete or failure to occur */ + irq_status = wait_for_irq(denali, INTR_STATUS0__ERASE_COMP | + INTR_STATUS0__ERASE_FAIL); + + denali->status = (irq_status & INTR_STATUS0__ERASE_FAIL) ? NAND_STATUS_FAIL : + PASS; +} + +static void denali_cmdfunc(struct mtd_info *mtd, unsigned int cmd, int col, + int page) +{ + struct denali_nand_info *denali = mtd_to_denali(mtd); + +#if DEBUG_DENALI + printk("cmdfunc: 0x%x %d %d\n", cmd, col, page); +#endif + switch (cmd) + { + case NAND_CMD_PAGEPROG: + break; + case NAND_CMD_STATUS: + read_status(denali); + break; + case NAND_CMD_READID: + reset_buf(denali); + if (denali->flash_bank < denali->total_used_banks) + { + /* write manufacturer information into nand + buffer for NAND subsystem to fetch. + */ + write_byte_to_buf(denali, denali->dev_info.wDeviceMaker); + write_byte_to_buf(denali, denali->dev_info.wDeviceID); + write_byte_to_buf(denali, denali->dev_info.bDeviceParam0); + write_byte_to_buf(denali, denali->dev_info.bDeviceParam1); + write_byte_to_buf(denali, denali->dev_info.bDeviceParam2); + } + else + { + int i; + for (i = 0; i < 5; i++) + write_byte_to_buf(denali, 0xff); + } + break; + case NAND_CMD_READ0: + case NAND_CMD_SEQIN: + denali->page = page; + break; + case NAND_CMD_RESET: + reset_bank(denali); + break; + case NAND_CMD_READOOB: + /* TODO: Read OOB data */ + break; + default: + printk(KERN_ERR ": unsupported command received 0x%x\n", cmd); + break; + } +} + +/* stubs for ECC functions not used by the NAND core */ +static int denali_ecc_calculate(struct mtd_info *mtd, const uint8_t *data, + uint8_t *ecc_code) +{ + printk(KERN_ERR "denali_ecc_calculate called unexpectedly\n"); + BUG(); + return -EIO; +} + +static int denali_ecc_correct(struct mtd_info *mtd, uint8_t *data, + uint8_t *read_ecc, uint8_t *calc_ecc) +{ + printk(KERN_ERR "denali_ecc_correct called unexpectedly\n"); + BUG(); + return -EIO; +} + +static void denali_ecc_hwctl(struct mtd_info *mtd, int mode) +{ + printk(KERN_ERR "denali_ecc_hwctl called unexpectedly\n"); + BUG(); +} +/* end NAND core entry points */ + +/* Initialization code to bring the device up to a known good state */ +static void denali_hw_init(struct denali_nand_info *denali) +{ + denali_irq_init(denali); + NAND_Flash_Reset(denali); + denali_write32(0x0F, denali->flash_reg + RB_PIN_ENABLED); + denali_write32(CHIP_EN_DONT_CARE__FLAG, denali->flash_reg + CHIP_ENABLE_DONT_CARE); + + denali_write32(0x0, denali->flash_reg + SPARE_AREA_SKIP_BYTES); + denali_write32(0xffff, denali->flash_reg + SPARE_AREA_MARKER); + + /* Should set value for these registers when init */ + denali_write32(0, denali->flash_reg + TWO_ROW_ADDR_CYCLES); + denali_write32(1, denali->flash_reg + ECC_ENABLE); +} + +/* ECC layout for SLC devices. Denali spec indicates SLC fixed at 4 bytes */ +#define ECC_BYTES_SLC 4 * (2048 / ECC_SECTOR_SIZE) +static struct nand_ecclayout nand_oob_slc = { + .eccbytes = 4, + .eccpos = { 0, 1, 2, 3 }, /* not used */ + .oobfree = {{ + .offset = ECC_BYTES_SLC, + .length = 64 - ECC_BYTES_SLC + }} +}; + +#define ECC_BYTES_MLC 14 * (2048 / ECC_SECTOR_SIZE) +static struct nand_ecclayout nand_oob_mlc_14bit = { + .eccbytes = 14, + .eccpos = { 0, 1, 2, 3, 5, 6, 7, 8, 9, 10, 11, 12, 13 }, /* not used */ + .oobfree = {{ + .offset = ECC_BYTES_MLC, + .length = 64 - ECC_BYTES_MLC + }} +}; + +static uint8_t bbt_pattern[] = {'B', 'b', 't', '0' }; +static uint8_t mirror_pattern[] = {'1', 't', 'b', 'B' }; + +static struct nand_bbt_descr bbt_main_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = bbt_pattern, +}; + +static struct nand_bbt_descr bbt_mirror_descr = { + .options = NAND_BBT_LASTBLOCK | NAND_BBT_CREATE | NAND_BBT_WRITE + | NAND_BBT_2BIT | NAND_BBT_VERSION | NAND_BBT_PERCHIP, + .offs = 8, + .len = 4, + .veroffs = 12, + .maxblocks = 4, + .pattern = mirror_pattern, +}; + +/* initalize driver data structures */ +void denali_drv_init(struct denali_nand_info *denali) +{ + denali->idx = 0; + + /* setup interrupt handler */ + /* the completion object will be used to notify + * the callee that the interrupt is done */ + init_completion(&denali->complete); + + /* the spinlock will be used to synchronize the ISR + * with any element that might be access shared + * data (interrupt status) */ + spin_lock_init(&denali->irq_lock); + + /* indicate that MTD has not selected a valid bank yet */ + denali->flash_bank = CHIP_SELECT_INVALID; + + /* initialize our irq_status variable to indicate no interrupts */ + denali->irq_status = 0; +} + +/* driver entry point */ +static int denali_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + int ret = -ENODEV; + resource_size_t csr_base, mem_base; + unsigned long csr_len, mem_len; + struct denali_nand_info *denali; + + nand_dbg_print(NAND_DBG_TRACE, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + denali = kzalloc(sizeof(*denali), GFP_KERNEL); + if (!denali) + return -ENOMEM; + + ret = pci_enable_device(dev); + if (ret) { + printk(KERN_ERR "Spectra: pci_enable_device failed.\n"); + goto failed_enable; + } + + if (id->driver_data == INTEL_CE4100) { + /* Due to a silicon limitation, we can only support + * ONFI timing mode 1 and below. + */ + if (onfi_timing_mode < -1 || onfi_timing_mode > 1) + { + printk("Intel CE4100 only supports ONFI timing mode 1 " + "or below\n"); + ret = -EINVAL; + goto failed_enable; + } + denali->platform = INTEL_CE4100; + mem_base = pci_resource_start(dev, 0); + mem_len = pci_resource_len(dev, 1); + csr_base = pci_resource_start(dev, 1); + csr_len = pci_resource_len(dev, 1); + } else { + denali->platform = INTEL_MRST; + csr_base = pci_resource_start(dev, 0); + csr_len = pci_resource_start(dev, 0); + mem_base = pci_resource_start(dev, 1); + mem_len = pci_resource_len(dev, 1); + if (!mem_len) { + mem_base = csr_base + csr_len; + mem_len = csr_len; + nand_dbg_print(NAND_DBG_WARN, + "Spectra: No second BAR for PCI device; assuming %08Lx\n", + (uint64_t)csr_base); + } + } + + /* Is 32-bit DMA supported? */ + ret = pci_set_dma_mask(dev, DMA_BIT_MASK(32)); + + if (ret) + { + printk(KERN_ERR "Spectra: no usable DMA configuration\n"); + goto failed_enable; + } + denali->buf.dma_buf = pci_map_single(dev, denali->buf.buf, DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); + + if (pci_dma_mapping_error(dev, denali->buf.dma_buf)) + { + printk(KERN_ERR "Spectra: failed to map DMA buffer\n"); + goto failed_enable; + } + + pci_set_master(dev); + denali->dev = dev; + + ret = pci_request_regions(dev, DENALI_NAND_NAME); + if (ret) { + printk(KERN_ERR "Spectra: Unable to request memory regions\n"); + goto failed_req_csr; + } + + denali->flash_reg = ioremap_nocache(csr_base, csr_len); + if (!denali->flash_reg) { + printk(KERN_ERR "Spectra: Unable to remap memory region\n"); + ret = -ENOMEM; + goto failed_remap_csr; + } + nand_dbg_print(NAND_DBG_DEBUG, "Spectra: CSR 0x%08Lx -> 0x%p (0x%lx)\n", + (uint64_t)csr_base, denali->flash_reg, csr_len); + + denali->flash_mem = ioremap_nocache(mem_base, mem_len); + if (!denali->flash_mem) { + printk(KERN_ERR "Spectra: ioremap_nocache failed!"); + iounmap(denali->flash_reg); + ret = -ENOMEM; + goto failed_remap_csr; + } + + nand_dbg_print(NAND_DBG_WARN, + "Spectra: Remapped flash base address: " + "0x%p, len: %ld\n", + denali->flash_mem, csr_len); + + denali_hw_init(denali); + denali_drv_init(denali); + + nand_dbg_print(NAND_DBG_DEBUG, "Spectra: IRQ %d\n", dev->irq); + if (request_irq(dev->irq, denali_isr, IRQF_SHARED, + DENALI_NAND_NAME, denali)) { + printk(KERN_ERR "Spectra: Unable to allocate IRQ\n"); + ret = -ENODEV; + goto failed_request_irq; + } + + /* now that our ISR is registered, we can enable interrupts */ + NAND_LLD_Enable_Disable_Interrupts(denali, true); + + pci_set_drvdata(dev, denali); + + NAND_Read_Device_ID(denali); + + /* MTD supported page sizes vary by kernel. We validate our + kernel supports the device here. + */ + if (denali->dev_info.wPageSize > NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE) + { + ret = -ENODEV; + printk(KERN_ERR "Spectra: device size not supported by this " + "version of MTD."); + goto failed_nand; + } + + nand_dbg_print(NAND_DBG_DEBUG, "Dump timing register values:" + "acc_clks: %d, re_2_we: %d, we_2_re: %d," + "addr_2_data: %d, rdwr_en_lo_cnt: %d, " + "rdwr_en_hi_cnt: %d, cs_setup_cnt: %d\n", + ioread32(denali->flash_reg + ACC_CLKS), + ioread32(denali->flash_reg + RE_2_WE), + ioread32(denali->flash_reg + WE_2_RE), + ioread32(denali->flash_reg + ADDR_2_DATA), + ioread32(denali->flash_reg + RDWR_EN_LO_CNT), + ioread32(denali->flash_reg + RDWR_EN_HI_CNT), + ioread32(denali->flash_reg + CS_SETUP_CNT)); + + denali->mtd.name = "Denali NAND"; + denali->mtd.owner = THIS_MODULE; + denali->mtd.priv = &denali->nand; + + /* register the driver with the NAND core subsystem */ + denali->nand.select_chip = denali_select_chip; + denali->nand.cmdfunc = denali_cmdfunc; + denali->nand.read_byte = denali_read_byte; + denali->nand.waitfunc = denali_waitfunc; + + /* scan for NAND devices attached to the controller + * this is the first stage in a two step process to register + * with the nand subsystem */ + if (nand_scan_ident(&denali->mtd, LLD_MAX_FLASH_BANKS, NULL)) + { + ret = -ENXIO; + goto failed_nand; + } + + /* second stage of the NAND scan + * this stage requires information regarding ECC and + * bad block management. */ + + /* Bad block management */ + denali->nand.bbt_td = &bbt_main_descr; + denali->nand.bbt_md = &bbt_mirror_descr; + + /* skip the scan for now until we have OOB read and write support */ + denali->nand.options |= NAND_USE_FLASH_BBT | NAND_SKIP_BBTSCAN; + denali->nand.ecc.mode = NAND_ECC_HW_SYNDROME; + + if (denali->dev_info.MLCDevice) + { + denali->nand.ecc.layout = &nand_oob_mlc_14bit; + denali->nand.ecc.bytes = ECC_BYTES_MLC; + } + else /* SLC */ + { + denali->nand.ecc.layout = &nand_oob_slc; + denali->nand.ecc.bytes = ECC_BYTES_SLC; + } + + /* These functions are required by the NAND core framework, otherwise, + the NAND core will assert. However, we don't need them, so we'll stub + them out. */ + denali->nand.ecc.calculate = denali_ecc_calculate; + denali->nand.ecc.correct = denali_ecc_correct; + denali->nand.ecc.hwctl = denali_ecc_hwctl; + + /* override the default read operations */ + denali->nand.ecc.size = denali->mtd.writesize; + denali->nand.ecc.read_page = denali_read_page; + denali->nand.ecc.read_page_raw = denali_read_page_raw; + denali->nand.ecc.write_page = denali_write_page; + denali->nand.ecc.write_page_raw = denali_write_page_raw; + denali->nand.ecc.read_oob = denali_read_oob; + denali->nand.ecc.write_oob = denali_write_oob; + denali->nand.erase_cmd = denali_erase; + + if (nand_scan_tail(&denali->mtd)) + { + ret = -ENXIO; + goto failed_nand; + } + + ret = add_mtd_device(&denali->mtd); + if (ret) { + printk(KERN_ERR "Spectra: Failed to register MTD device: %d\n", ret); + goto failed_nand; + } + return 0; + + failed_nand: + denali_irq_cleanup(dev->irq, denali); + failed_request_irq: + iounmap(denali->flash_reg); + iounmap(denali->flash_mem); + failed_remap_csr: + pci_release_regions(dev); + failed_req_csr: + pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); + failed_enable: + kfree(denali); + return ret; +} + +/* driver exit point */ +static void denali_pci_remove(struct pci_dev *dev) +{ + struct denali_nand_info *denali = pci_get_drvdata(dev); + + nand_dbg_print(NAND_DBG_WARN, "%s, Line %d, Function: %s\n", + __FILE__, __LINE__, __func__); + + nand_release(&denali->mtd); + del_mtd_device(&denali->mtd); + + denali_irq_cleanup(dev->irq, denali); + + iounmap(denali->flash_reg); + iounmap(denali->flash_mem); + pci_release_regions(dev); + pci_disable_device(dev); + pci_unmap_single(dev, denali->buf.dma_buf, DENALI_BUF_SIZE, + PCI_DMA_BIDIRECTIONAL); + pci_set_drvdata(dev, NULL); + kfree(denali); +} + +MODULE_DEVICE_TABLE(pci, denali_pci_ids); + +static struct pci_driver denali_pci_driver = { + .name = DENALI_NAND_NAME, + .id_table = denali_pci_ids, + .probe = denali_pci_probe, + .remove = denali_pci_remove, +}; + +static int __devinit denali_init(void) +{ + printk(KERN_INFO "Spectra MTD driver built on %s @ %s\n", __DATE__, __TIME__); + return pci_register_driver(&denali_pci_driver); +} + +/* Free memory */ +static void __devexit denali_exit(void) +{ + pci_unregister_driver(&denali_pci_driver); +} + +module_init(denali_init); +module_exit(denali_exit); diff --git a/drivers/mtd/nand/denali.h b/drivers/mtd/nand/denali.h new file mode 100644 index 00000000000..422a29ab2f6 --- /dev/null +++ b/drivers/mtd/nand/denali.h @@ -0,0 +1,816 @@ +/* + * NAND Flash Controller Device Driver + * Copyright (c) 2009 - 2010, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#include + +#define DEVICE_RESET 0x0 +#define DEVICE_RESET__BANK0 0x0001 +#define DEVICE_RESET__BANK1 0x0002 +#define DEVICE_RESET__BANK2 0x0004 +#define DEVICE_RESET__BANK3 0x0008 + +#define TRANSFER_SPARE_REG 0x10 +#define TRANSFER_SPARE_REG__FLAG 0x0001 + +#define LOAD_WAIT_CNT 0x20 +#define LOAD_WAIT_CNT__VALUE 0xffff + +#define PROGRAM_WAIT_CNT 0x30 +#define PROGRAM_WAIT_CNT__VALUE 0xffff + +#define ERASE_WAIT_CNT 0x40 +#define ERASE_WAIT_CNT__VALUE 0xffff + +#define INT_MON_CYCCNT 0x50 +#define INT_MON_CYCCNT__VALUE 0xffff + +#define RB_PIN_ENABLED 0x60 +#define RB_PIN_ENABLED__BANK0 0x0001 +#define RB_PIN_ENABLED__BANK1 0x0002 +#define RB_PIN_ENABLED__BANK2 0x0004 +#define RB_PIN_ENABLED__BANK3 0x0008 + +#define MULTIPLANE_OPERATION 0x70 +#define MULTIPLANE_OPERATION__FLAG 0x0001 + +#define MULTIPLANE_READ_ENABLE 0x80 +#define MULTIPLANE_READ_ENABLE__FLAG 0x0001 + +#define COPYBACK_DISABLE 0x90 +#define COPYBACK_DISABLE__FLAG 0x0001 + +#define CACHE_WRITE_ENABLE 0xa0 +#define CACHE_WRITE_ENABLE__FLAG 0x0001 + +#define CACHE_READ_ENABLE 0xb0 +#define CACHE_READ_ENABLE__FLAG 0x0001 + +#define PREFETCH_MODE 0xc0 +#define PREFETCH_MODE__PREFETCH_EN 0x0001 +#define PREFETCH_MODE__PREFETCH_BURST_LENGTH 0xfff0 + +#define CHIP_ENABLE_DONT_CARE 0xd0 +#define CHIP_EN_DONT_CARE__FLAG 0x01 + +#define ECC_ENABLE 0xe0 +#define ECC_ENABLE__FLAG 0x0001 + +#define GLOBAL_INT_ENABLE 0xf0 +#define GLOBAL_INT_EN_FLAG 0x01 + +#define WE_2_RE 0x100 +#define WE_2_RE__VALUE 0x003f + +#define ADDR_2_DATA 0x110 +#define ADDR_2_DATA__VALUE 0x003f + +#define RE_2_WE 0x120 +#define RE_2_WE__VALUE 0x003f + +#define ACC_CLKS 0x130 +#define ACC_CLKS__VALUE 0x000f + +#define NUMBER_OF_PLANES 0x140 +#define NUMBER_OF_PLANES__VALUE 0x0007 + +#define PAGES_PER_BLOCK 0x150 +#define PAGES_PER_BLOCK__VALUE 0xffff + +#define DEVICE_WIDTH 0x160 +#define DEVICE_WIDTH__VALUE 0x0003 + +#define DEVICE_MAIN_AREA_SIZE 0x170 +#define DEVICE_MAIN_AREA_SIZE__VALUE 0xffff + +#define DEVICE_SPARE_AREA_SIZE 0x180 +#define DEVICE_SPARE_AREA_SIZE__VALUE 0xffff + +#define TWO_ROW_ADDR_CYCLES 0x190 +#define TWO_ROW_ADDR_CYCLES__FLAG 0x0001 + +#define MULTIPLANE_ADDR_RESTRICT 0x1a0 +#define MULTIPLANE_ADDR_RESTRICT__FLAG 0x0001 + +#define ECC_CORRECTION 0x1b0 +#define ECC_CORRECTION__VALUE 0x001f + +#define READ_MODE 0x1c0 +#define READ_MODE__VALUE 0x000f + +#define WRITE_MODE 0x1d0 +#define WRITE_MODE__VALUE 0x000f + +#define COPYBACK_MODE 0x1e0 +#define COPYBACK_MODE__VALUE 0x000f + +#define RDWR_EN_LO_CNT 0x1f0 +#define RDWR_EN_LO_CNT__VALUE 0x001f + +#define RDWR_EN_HI_CNT 0x200 +#define RDWR_EN_HI_CNT__VALUE 0x001f + +#define MAX_RD_DELAY 0x210 +#define MAX_RD_DELAY__VALUE 0x000f + +#define CS_SETUP_CNT 0x220 +#define CS_SETUP_CNT__VALUE 0x001f + +#define SPARE_AREA_SKIP_BYTES 0x230 +#define SPARE_AREA_SKIP_BYTES__VALUE 0x003f + +#define SPARE_AREA_MARKER 0x240 +#define SPARE_AREA_MARKER__VALUE 0xffff + +#define DEVICES_CONNECTED 0x250 +#define DEVICES_CONNECTED__VALUE 0x0007 + +#define DIE_MASK 0x260 +#define DIE_MASK__VALUE 0x00ff + +#define FIRST_BLOCK_OF_NEXT_PLANE 0x270 +#define FIRST_BLOCK_OF_NEXT_PLANE__VALUE 0xffff + +#define WRITE_PROTECT 0x280 +#define WRITE_PROTECT__FLAG 0x0001 + +#define RE_2_RE 0x290 +#define RE_2_RE__VALUE 0x003f + +#define MANUFACTURER_ID 0x300 +#define MANUFACTURER_ID__VALUE 0x00ff + +#define DEVICE_ID 0x310 +#define DEVICE_ID__VALUE 0x00ff + +#define DEVICE_PARAM_0 0x320 +#define DEVICE_PARAM_0__VALUE 0x00ff + +#define DEVICE_PARAM_1 0x330 +#define DEVICE_PARAM_1__VALUE 0x00ff + +#define DEVICE_PARAM_2 0x340 +#define DEVICE_PARAM_2__VALUE 0x00ff + +#define LOGICAL_PAGE_DATA_SIZE 0x350 +#define LOGICAL_PAGE_DATA_SIZE__VALUE 0xffff + +#define LOGICAL_PAGE_SPARE_SIZE 0x360 +#define LOGICAL_PAGE_SPARE_SIZE__VALUE 0xffff + +#define REVISION 0x370 +#define REVISION__VALUE 0xffff + +#define ONFI_DEVICE_FEATURES 0x380 +#define ONFI_DEVICE_FEATURES__VALUE 0x003f + +#define ONFI_OPTIONAL_COMMANDS 0x390 +#define ONFI_OPTIONAL_COMMANDS__VALUE 0x003f + +#define ONFI_TIMING_MODE 0x3a0 +#define ONFI_TIMING_MODE__VALUE 0x003f + +#define ONFI_PGM_CACHE_TIMING_MODE 0x3b0 +#define ONFI_PGM_CACHE_TIMING_MODE__VALUE 0x003f + +#define ONFI_DEVICE_NO_OF_LUNS 0x3c0 +#define ONFI_DEVICE_NO_OF_LUNS__NO_OF_LUNS 0x00ff +#define ONFI_DEVICE_NO_OF_LUNS__ONFI_DEVICE 0x0100 + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L 0x3d0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_L__VALUE 0xffff + +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U 0x3e0 +#define ONFI_DEVICE_NO_OF_BLOCKS_PER_LUN_U__VALUE 0xffff + +#define FEATURES 0x3f0 +#define FEATURES__N_BANKS 0x0003 +#define FEATURES__ECC_MAX_ERR 0x003c +#define FEATURES__DMA 0x0040 +#define FEATURES__CMD_DMA 0x0080 +#define FEATURES__PARTITION 0x0100 +#define FEATURES__XDMA_SIDEBAND 0x0200 +#define FEATURES__GPREG 0x0400 +#define FEATURES__INDEX_ADDR 0x0800 + +#define TRANSFER_MODE 0x400 +#define TRANSFER_MODE__VALUE 0x0003 + +#define INTR_STATUS0 0x410 +#define INTR_STATUS0__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS0__ECC_ERR 0x0002 +#define INTR_STATUS0__DMA_CMD_COMP 0x0004 +#define INTR_STATUS0__TIME_OUT 0x0008 +#define INTR_STATUS0__PROGRAM_FAIL 0x0010 +#define INTR_STATUS0__ERASE_FAIL 0x0020 +#define INTR_STATUS0__LOAD_COMP 0x0040 +#define INTR_STATUS0__PROGRAM_COMP 0x0080 +#define INTR_STATUS0__ERASE_COMP 0x0100 +#define INTR_STATUS0__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS0__LOCKED_BLK 0x0400 +#define INTR_STATUS0__UNSUP_CMD 0x0800 +#define INTR_STATUS0__INT_ACT 0x1000 +#define INTR_STATUS0__RST_COMP 0x2000 +#define INTR_STATUS0__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS0__PAGE_XFER_INC 0x8000 + +#define INTR_EN0 0x420 +#define INTR_EN0__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN0__ECC_ERR 0x0002 +#define INTR_EN0__DMA_CMD_COMP 0x0004 +#define INTR_EN0__TIME_OUT 0x0008 +#define INTR_EN0__PROGRAM_FAIL 0x0010 +#define INTR_EN0__ERASE_FAIL 0x0020 +#define INTR_EN0__LOAD_COMP 0x0040 +#define INTR_EN0__PROGRAM_COMP 0x0080 +#define INTR_EN0__ERASE_COMP 0x0100 +#define INTR_EN0__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN0__LOCKED_BLK 0x0400 +#define INTR_EN0__UNSUP_CMD 0x0800 +#define INTR_EN0__INT_ACT 0x1000 +#define INTR_EN0__RST_COMP 0x2000 +#define INTR_EN0__PIPE_CMD_ERR 0x4000 +#define INTR_EN0__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT0 0x430 +#define PAGE_CNT0__VALUE 0x00ff + +#define ERR_PAGE_ADDR0 0x440 +#define ERR_PAGE_ADDR0__VALUE 0xffff + +#define ERR_BLOCK_ADDR0 0x450 +#define ERR_BLOCK_ADDR0__VALUE 0xffff + +#define INTR_STATUS1 0x460 +#define INTR_STATUS1__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS1__ECC_ERR 0x0002 +#define INTR_STATUS1__DMA_CMD_COMP 0x0004 +#define INTR_STATUS1__TIME_OUT 0x0008 +#define INTR_STATUS1__PROGRAM_FAIL 0x0010 +#define INTR_STATUS1__ERASE_FAIL 0x0020 +#define INTR_STATUS1__LOAD_COMP 0x0040 +#define INTR_STATUS1__PROGRAM_COMP 0x0080 +#define INTR_STATUS1__ERASE_COMP 0x0100 +#define INTR_STATUS1__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS1__LOCKED_BLK 0x0400 +#define INTR_STATUS1__UNSUP_CMD 0x0800 +#define INTR_STATUS1__INT_ACT 0x1000 +#define INTR_STATUS1__RST_COMP 0x2000 +#define INTR_STATUS1__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS1__PAGE_XFER_INC 0x8000 + +#define INTR_EN1 0x470 +#define INTR_EN1__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN1__ECC_ERR 0x0002 +#define INTR_EN1__DMA_CMD_COMP 0x0004 +#define INTR_EN1__TIME_OUT 0x0008 +#define INTR_EN1__PROGRAM_FAIL 0x0010 +#define INTR_EN1__ERASE_FAIL 0x0020 +#define INTR_EN1__LOAD_COMP 0x0040 +#define INTR_EN1__PROGRAM_COMP 0x0080 +#define INTR_EN1__ERASE_COMP 0x0100 +#define INTR_EN1__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN1__LOCKED_BLK 0x0400 +#define INTR_EN1__UNSUP_CMD 0x0800 +#define INTR_EN1__INT_ACT 0x1000 +#define INTR_EN1__RST_COMP 0x2000 +#define INTR_EN1__PIPE_CMD_ERR 0x4000 +#define INTR_EN1__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT1 0x480 +#define PAGE_CNT1__VALUE 0x00ff + +#define ERR_PAGE_ADDR1 0x490 +#define ERR_PAGE_ADDR1__VALUE 0xffff + +#define ERR_BLOCK_ADDR1 0x4a0 +#define ERR_BLOCK_ADDR1__VALUE 0xffff + +#define INTR_STATUS2 0x4b0 +#define INTR_STATUS2__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS2__ECC_ERR 0x0002 +#define INTR_STATUS2__DMA_CMD_COMP 0x0004 +#define INTR_STATUS2__TIME_OUT 0x0008 +#define INTR_STATUS2__PROGRAM_FAIL 0x0010 +#define INTR_STATUS2__ERASE_FAIL 0x0020 +#define INTR_STATUS2__LOAD_COMP 0x0040 +#define INTR_STATUS2__PROGRAM_COMP 0x0080 +#define INTR_STATUS2__ERASE_COMP 0x0100 +#define INTR_STATUS2__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS2__LOCKED_BLK 0x0400 +#define INTR_STATUS2__UNSUP_CMD 0x0800 +#define INTR_STATUS2__INT_ACT 0x1000 +#define INTR_STATUS2__RST_COMP 0x2000 +#define INTR_STATUS2__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS2__PAGE_XFER_INC 0x8000 + +#define INTR_EN2 0x4c0 +#define INTR_EN2__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN2__ECC_ERR 0x0002 +#define INTR_EN2__DMA_CMD_COMP 0x0004 +#define INTR_EN2__TIME_OUT 0x0008 +#define INTR_EN2__PROGRAM_FAIL 0x0010 +#define INTR_EN2__ERASE_FAIL 0x0020 +#define INTR_EN2__LOAD_COMP 0x0040 +#define INTR_EN2__PROGRAM_COMP 0x0080 +#define INTR_EN2__ERASE_COMP 0x0100 +#define INTR_EN2__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN2__LOCKED_BLK 0x0400 +#define INTR_EN2__UNSUP_CMD 0x0800 +#define INTR_EN2__INT_ACT 0x1000 +#define INTR_EN2__RST_COMP 0x2000 +#define INTR_EN2__PIPE_CMD_ERR 0x4000 +#define INTR_EN2__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT2 0x4d0 +#define PAGE_CNT2__VALUE 0x00ff + +#define ERR_PAGE_ADDR2 0x4e0 +#define ERR_PAGE_ADDR2__VALUE 0xffff + +#define ERR_BLOCK_ADDR2 0x4f0 +#define ERR_BLOCK_ADDR2__VALUE 0xffff + +#define INTR_STATUS3 0x500 +#define INTR_STATUS3__ECC_TRANSACTION_DONE 0x0001 +#define INTR_STATUS3__ECC_ERR 0x0002 +#define INTR_STATUS3__DMA_CMD_COMP 0x0004 +#define INTR_STATUS3__TIME_OUT 0x0008 +#define INTR_STATUS3__PROGRAM_FAIL 0x0010 +#define INTR_STATUS3__ERASE_FAIL 0x0020 +#define INTR_STATUS3__LOAD_COMP 0x0040 +#define INTR_STATUS3__PROGRAM_COMP 0x0080 +#define INTR_STATUS3__ERASE_COMP 0x0100 +#define INTR_STATUS3__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_STATUS3__LOCKED_BLK 0x0400 +#define INTR_STATUS3__UNSUP_CMD 0x0800 +#define INTR_STATUS3__INT_ACT 0x1000 +#define INTR_STATUS3__RST_COMP 0x2000 +#define INTR_STATUS3__PIPE_CMD_ERR 0x4000 +#define INTR_STATUS3__PAGE_XFER_INC 0x8000 + +#define INTR_EN3 0x510 +#define INTR_EN3__ECC_TRANSACTION_DONE 0x0001 +#define INTR_EN3__ECC_ERR 0x0002 +#define INTR_EN3__DMA_CMD_COMP 0x0004 +#define INTR_EN3__TIME_OUT 0x0008 +#define INTR_EN3__PROGRAM_FAIL 0x0010 +#define INTR_EN3__ERASE_FAIL 0x0020 +#define INTR_EN3__LOAD_COMP 0x0040 +#define INTR_EN3__PROGRAM_COMP 0x0080 +#define INTR_EN3__ERASE_COMP 0x0100 +#define INTR_EN3__PIPE_CPYBCK_CMD_COMP 0x0200 +#define INTR_EN3__LOCKED_BLK 0x0400 +#define INTR_EN3__UNSUP_CMD 0x0800 +#define INTR_EN3__INT_ACT 0x1000 +#define INTR_EN3__RST_COMP 0x2000 +#define INTR_EN3__PIPE_CMD_ERR 0x4000 +#define INTR_EN3__PAGE_XFER_INC 0x8000 + +#define PAGE_CNT3 0x520 +#define PAGE_CNT3__VALUE 0x00ff + +#define ERR_PAGE_ADDR3 0x530 +#define ERR_PAGE_ADDR3__VALUE 0xffff + +#define ERR_BLOCK_ADDR3 0x540 +#define ERR_BLOCK_ADDR3__VALUE 0xffff + +#define DATA_INTR 0x550 +#define DATA_INTR__WRITE_SPACE_AV 0x0001 +#define DATA_INTR__READ_DATA_AV 0x0002 + +#define DATA_INTR_EN 0x560 +#define DATA_INTR_EN__WRITE_SPACE_AV 0x0001 +#define DATA_INTR_EN__READ_DATA_AV 0x0002 + +#define GPREG_0 0x570 +#define GPREG_0__VALUE 0xffff + +#define GPREG_1 0x580 +#define GPREG_1__VALUE 0xffff + +#define GPREG_2 0x590 +#define GPREG_2__VALUE 0xffff + +#define GPREG_3 0x5a0 +#define GPREG_3__VALUE 0xffff + +#define ECC_THRESHOLD 0x600 +#define ECC_THRESHOLD__VALUE 0x03ff + +#define ECC_ERROR_BLOCK_ADDRESS 0x610 +#define ECC_ERROR_BLOCK_ADDRESS__VALUE 0xffff + +#define ECC_ERROR_PAGE_ADDRESS 0x620 +#define ECC_ERROR_PAGE_ADDRESS__VALUE 0x0fff +#define ECC_ERROR_PAGE_ADDRESS__BANK 0xf000 + +#define ECC_ERROR_ADDRESS 0x630 +#define ECC_ERROR_ADDRESS__OFFSET 0x0fff +#define ECC_ERROR_ADDRESS__SECTOR_NR 0xf000 + +#define ERR_CORRECTION_INFO 0x640 +#define ERR_CORRECTION_INFO__BYTEMASK 0x00ff +#define ERR_CORRECTION_INFO__DEVICE_NR 0x0f00 +#define ERR_CORRECTION_INFO__ERROR_TYPE 0x4000 +#define ERR_CORRECTION_INFO__LAST_ERR_INFO 0x8000 + +#define DMA_ENABLE 0x700 +#define DMA_ENABLE__FLAG 0x0001 + +#define IGNORE_ECC_DONE 0x710 +#define IGNORE_ECC_DONE__FLAG 0x0001 + +#define DMA_INTR 0x720 +#define DMA_INTR__TARGET_ERROR 0x0001 +#define DMA_INTR__DESC_COMP_CHANNEL0 0x0002 +#define DMA_INTR__DESC_COMP_CHANNEL1 0x0004 +#define DMA_INTR__DESC_COMP_CHANNEL2 0x0008 +#define DMA_INTR__DESC_COMP_CHANNEL3 0x0010 +#define DMA_INTR__MEMCOPY_DESC_COMP 0x0020 + +#define DMA_INTR_EN 0x730 +#define DMA_INTR_EN__TARGET_ERROR 0x0001 +#define DMA_INTR_EN__DESC_COMP_CHANNEL0 0x0002 +#define DMA_INTR_EN__DESC_COMP_CHANNEL1 0x0004 +#define DMA_INTR_EN__DESC_COMP_CHANNEL2 0x0008 +#define DMA_INTR_EN__DESC_COMP_CHANNEL3 0x0010 +#define DMA_INTR_EN__MEMCOPY_DESC_COMP 0x0020 + +#define TARGET_ERR_ADDR_LO 0x740 +#define TARGET_ERR_ADDR_LO__VALUE 0xffff + +#define TARGET_ERR_ADDR_HI 0x750 +#define TARGET_ERR_ADDR_HI__VALUE 0xffff + +#define CHNL_ACTIVE 0x760 +#define CHNL_ACTIVE__CHANNEL0 0x0001 +#define CHNL_ACTIVE__CHANNEL1 0x0002 +#define CHNL_ACTIVE__CHANNEL2 0x0004 +#define CHNL_ACTIVE__CHANNEL3 0x0008 + +#define ACTIVE_SRC_ID 0x800 +#define ACTIVE_SRC_ID__VALUE 0x00ff + +#define PTN_INTR 0x810 +#define PTN_INTR__CONFIG_ERROR 0x0001 +#define PTN_INTR__ACCESS_ERROR_BANK0 0x0002 +#define PTN_INTR__ACCESS_ERROR_BANK1 0x0004 +#define PTN_INTR__ACCESS_ERROR_BANK2 0x0008 +#define PTN_INTR__ACCESS_ERROR_BANK3 0x0010 +#define PTN_INTR__REG_ACCESS_ERROR 0x0020 + +#define PTN_INTR_EN 0x820 +#define PTN_INTR_EN__CONFIG_ERROR 0x0001 +#define PTN_INTR_EN__ACCESS_ERROR_BANK0 0x0002 +#define PTN_INTR_EN__ACCESS_ERROR_BANK1 0x0004 +#define PTN_INTR_EN__ACCESS_ERROR_BANK2 0x0008 +#define PTN_INTR_EN__ACCESS_ERROR_BANK3 0x0010 +#define PTN_INTR_EN__REG_ACCESS_ERROR 0x0020 + +#define PERM_SRC_ID_0 0x830 +#define PERM_SRC_ID_0__SRCID 0x00ff +#define PERM_SRC_ID_0__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_0__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_0__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_0__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_0 0x840 +#define MIN_BLK_ADDR_0__VALUE 0xffff + +#define MAX_BLK_ADDR_0 0x850 +#define MAX_BLK_ADDR_0__VALUE 0xffff + +#define MIN_MAX_BANK_0 0x860 +#define MIN_MAX_BANK_0__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_0__MAX_VALUE 0x000c + +#define PERM_SRC_ID_1 0x870 +#define PERM_SRC_ID_1__SRCID 0x00ff +#define PERM_SRC_ID_1__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_1__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_1__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_1__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_1 0x880 +#define MIN_BLK_ADDR_1__VALUE 0xffff + +#define MAX_BLK_ADDR_1 0x890 +#define MAX_BLK_ADDR_1__VALUE 0xffff + +#define MIN_MAX_BANK_1 0x8a0 +#define MIN_MAX_BANK_1__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_1__MAX_VALUE 0x000c + +#define PERM_SRC_ID_2 0x8b0 +#define PERM_SRC_ID_2__SRCID 0x00ff +#define PERM_SRC_ID_2__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_2__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_2__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_2__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_2 0x8c0 +#define MIN_BLK_ADDR_2__VALUE 0xffff + +#define MAX_BLK_ADDR_2 0x8d0 +#define MAX_BLK_ADDR_2__VALUE 0xffff + +#define MIN_MAX_BANK_2 0x8e0 +#define MIN_MAX_BANK_2__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_2__MAX_VALUE 0x000c + +#define PERM_SRC_ID_3 0x8f0 +#define PERM_SRC_ID_3__SRCID 0x00ff +#define PERM_SRC_ID_3__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_3__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_3__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_3__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_3 0x900 +#define MIN_BLK_ADDR_3__VALUE 0xffff + +#define MAX_BLK_ADDR_3 0x910 +#define MAX_BLK_ADDR_3__VALUE 0xffff + +#define MIN_MAX_BANK_3 0x920 +#define MIN_MAX_BANK_3__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_3__MAX_VALUE 0x000c + +#define PERM_SRC_ID_4 0x930 +#define PERM_SRC_ID_4__SRCID 0x00ff +#define PERM_SRC_ID_4__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_4__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_4__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_4__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_4 0x940 +#define MIN_BLK_ADDR_4__VALUE 0xffff + +#define MAX_BLK_ADDR_4 0x950 +#define MAX_BLK_ADDR_4__VALUE 0xffff + +#define MIN_MAX_BANK_4 0x960 +#define MIN_MAX_BANK_4__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_4__MAX_VALUE 0x000c + +#define PERM_SRC_ID_5 0x970 +#define PERM_SRC_ID_5__SRCID 0x00ff +#define PERM_SRC_ID_5__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_5__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_5__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_5__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_5 0x980 +#define MIN_BLK_ADDR_5__VALUE 0xffff + +#define MAX_BLK_ADDR_5 0x990 +#define MAX_BLK_ADDR_5__VALUE 0xffff + +#define MIN_MAX_BANK_5 0x9a0 +#define MIN_MAX_BANK_5__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_5__MAX_VALUE 0x000c + +#define PERM_SRC_ID_6 0x9b0 +#define PERM_SRC_ID_6__SRCID 0x00ff +#define PERM_SRC_ID_6__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_6__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_6__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_6__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_6 0x9c0 +#define MIN_BLK_ADDR_6__VALUE 0xffff + +#define MAX_BLK_ADDR_6 0x9d0 +#define MAX_BLK_ADDR_6__VALUE 0xffff + +#define MIN_MAX_BANK_6 0x9e0 +#define MIN_MAX_BANK_6__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_6__MAX_VALUE 0x000c + +#define PERM_SRC_ID_7 0x9f0 +#define PERM_SRC_ID_7__SRCID 0x00ff +#define PERM_SRC_ID_7__DIRECT_ACCESS_ACTIVE 0x0800 +#define PERM_SRC_ID_7__WRITE_ACTIVE 0x2000 +#define PERM_SRC_ID_7__READ_ACTIVE 0x4000 +#define PERM_SRC_ID_7__PARTITION_VALID 0x8000 + +#define MIN_BLK_ADDR_7 0xa00 +#define MIN_BLK_ADDR_7__VALUE 0xffff + +#define MAX_BLK_ADDR_7 0xa10 +#define MAX_BLK_ADDR_7__VALUE 0xffff + +#define MIN_MAX_BANK_7 0xa20 +#define MIN_MAX_BANK_7__MIN_VALUE 0x0003 +#define MIN_MAX_BANK_7__MAX_VALUE 0x000c + +/* flash.h */ +struct device_info_tag { + uint16_t wDeviceMaker; + uint16_t wDeviceID; + uint8_t bDeviceParam0; + uint8_t bDeviceParam1; + uint8_t bDeviceParam2; + uint32_t wDeviceType; + uint32_t wSpectraStartBlock; + uint32_t wSpectraEndBlock; + uint32_t wTotalBlocks; + uint16_t wPagesPerBlock; + uint16_t wPageSize; + uint16_t wPageDataSize; + uint16_t wPageSpareSize; + uint16_t wNumPageSpareFlag; + uint16_t wECCBytesPerSector; + uint32_t wBlockSize; + uint32_t wBlockDataSize; + uint32_t wDataBlockNum; + uint8_t bPlaneNum; + uint16_t wDeviceMainAreaSize; + uint16_t wDeviceSpareAreaSize; + uint16_t wDevicesConnected; + uint16_t wDeviceWidth; + uint16_t wHWRevision; + uint16_t wHWFeatures; + + uint16_t wONFIDevFeatures; + uint16_t wONFIOptCommands; + uint16_t wONFITimingMode; + uint16_t wONFIPgmCacheTimingMode; + + uint16_t MLCDevice; + uint16_t wSpareSkipBytes; + + uint8_t nBitsInPageNumber; + uint8_t nBitsInPageDataSize; + uint8_t nBitsInBlockDataSize; +}; + +/* ffsdefs.h */ +#define CLEAR 0 /*use this to clear a field instead of "fail"*/ +#define SET 1 /*use this to set a field instead of "pass"*/ +#define FAIL 1 /*failed flag*/ +#define PASS 0 /*success flag*/ +#define ERR -1 /*error flag*/ + +/* lld.h */ +#define GOOD_BLOCK 0 +#define DEFECTIVE_BLOCK 1 +#define READ_ERROR 2 + +#define CLK_X 5 +#define CLK_MULTI 4 + +/* ffsport.h */ +#define VERBOSE 1 + +#define NAND_DBG_WARN 1 +#define NAND_DBG_DEBUG 2 +#define NAND_DBG_TRACE 3 + +#ifdef VERBOSE +#define nand_dbg_print(level, args...) \ + do { \ + if (level <= nand_debug_level) \ + printk(KERN_ALERT args); \ + } while (0) +#else +#define nand_dbg_print(level, args...) +#endif + + +/* spectraswconfig.h */ +#define CMD_DMA 0 + +#define SPECTRA_PARTITION_ID 0 +/**** Block Table and Reserved Block Parameters *****/ +#define SPECTRA_START_BLOCK 3 +#define NUM_FREE_BLOCKS_GATE 30 + +/* KBV - Updated to LNW scratch register address */ +#define SCRATCH_REG_ADDR CONFIG_MTD_NAND_DENALI_SCRATCH_REG_ADDR +#define SCRATCH_REG_SIZE 64 + +#define GLOB_HWCTL_DEFAULT_BLKS 2048 + +#define SUPPORT_15BITECC 1 +#define SUPPORT_8BITECC 1 + +#define CUSTOM_CONF_PARAMS 0 + +#define ONFI_BLOOM_TIME 1 +#define MODE5_WORKAROUND 0 + +/* lld_nand.h */ +/* + * NAND Flash Controller Device Driver + * Copyright (c) 2009, Intel Corporation and its suppliers. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along with + * this program; if not, write to the Free Software Foundation, Inc., + * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA. + * + */ + +#ifndef _LLD_NAND_ +#define _LLD_NAND_ + +#define MODE_00 0x00000000 +#define MODE_01 0x04000000 +#define MODE_10 0x08000000 +#define MODE_11 0x0C000000 + + +#define DATA_TRANSFER_MODE 0 +#define PROTECTION_PER_BLOCK 1 +#define LOAD_WAIT_COUNT 2 +#define PROGRAM_WAIT_COUNT 3 +#define ERASE_WAIT_COUNT 4 +#define INT_MONITOR_CYCLE_COUNT 5 +#define READ_BUSY_PIN_ENABLED 6 +#define MULTIPLANE_OPERATION_SUPPORT 7 +#define PRE_FETCH_MODE 8 +#define CE_DONT_CARE_SUPPORT 9 +#define COPYBACK_SUPPORT 10 +#define CACHE_WRITE_SUPPORT 11 +#define CACHE_READ_SUPPORT 12 +#define NUM_PAGES_IN_BLOCK 13 +#define ECC_ENABLE_SELECT 14 +#define WRITE_ENABLE_2_READ_ENABLE 15 +#define ADDRESS_2_DATA 16 +#define READ_ENABLE_2_WRITE_ENABLE 17 +#define TWO_ROW_ADDRESS_CYCLES 18 +#define MULTIPLANE_ADDRESS_RESTRICT 19 +#define ACC_CLOCKS 20 +#define READ_WRITE_ENABLE_LOW_COUNT 21 +#define READ_WRITE_ENABLE_HIGH_COUNT 22 + +#define ECC_SECTOR_SIZE 512 +#define LLD_MAX_FLASH_BANKS 4 + +#define DENALI_BUF_SIZE NAND_MAX_PAGESIZE + NAND_MAX_OOBSIZE + +struct nand_buf +{ + int head; + int tail; + uint8_t buf[DENALI_BUF_SIZE]; + dma_addr_t dma_buf; +}; + +#define INTEL_CE4100 1 +#define INTEL_MRST 2 + +struct denali_nand_info { + struct mtd_info mtd; + struct nand_chip nand; + struct device_info_tag dev_info; + int flash_bank; /* currently selected chip */ + int status; + int platform; + struct nand_buf buf; + struct pci_dev *dev; + int total_used_banks; + uint32_t block; /* stored for future use */ + uint16_t page; + void __iomem *flash_reg; /* Mapped io reg base address */ + void __iomem *flash_mem; /* Mapped io reg base address */ + + /* elements used by ISR */ + struct completion complete; + spinlock_t irq_lock; + uint32_t irq_status; + int irq_debug_array[32]; + int idx; +}; + +static uint16_t NAND_Flash_Reset(struct denali_nand_info *denali); +static uint16_t NAND_Read_Device_ID(struct denali_nand_info *denali); +static void NAND_LLD_Enable_Disable_Interrupts(struct denali_nand_info *denali, uint16_t INT_ENABLE); + +#endif /*_LLD_NAND_*/ + -- cgit v1.2.3-70-g09d2 From aadff49c56f921d18cc280cbf087a550c67bbd02 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 13 May 2010 16:12:43 +0100 Subject: mtd/nand: Fix denali build on ppc64 MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit drivers/mtd/nand/denali.c:1427: error: conflicting types for ‘enable_dma’ arch/powerpc/include/asm/dma.h:189: note: previous definition of ‘enable_dma’ was here Signed-off-by: David Woodhouse --- drivers/mtd/nand/denali.c | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/denali.c b/drivers/mtd/nand/denali.c index 8a6ce0dd953..ca03428b59c 100644 --- a/drivers/mtd/nand/denali.c +++ b/drivers/mtd/nand/denali.c @@ -1424,7 +1424,7 @@ static bool handle_ecc(struct denali_nand_info *denali, uint8_t *buf, } /* programs the controller to either enable/disable DMA transfers */ -static void enable_dma(struct denali_nand_info *denali, bool en) +static void denali_enable_dma(struct denali_nand_info *denali, bool en) { uint32_t reg_val = 0x0; @@ -1435,7 +1435,7 @@ static void enable_dma(struct denali_nand_info *denali, bool en) } /* setups the HW to perform the data DMA */ -static void setup_dma(struct denali_nand_info *denali, int op) +static void denali_setup_dma(struct denali_nand_info *denali, int op) { uint32_t mode = 0x0; const int page_count = 1; @@ -1494,9 +1494,9 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_TODEVICE); clear_interrupts(denali); - enable_dma(denali, true); + denali_enable_dma(denali, true); - setup_dma(denali, DENALI_WRITE); + denali_setup_dma(denali, DENALI_WRITE); /* wait for operation to complete */ irq_status = wait_for_irq(denali, irq_mask); @@ -1509,7 +1509,7 @@ static void write_page(struct mtd_info *mtd, struct nand_chip *chip, PASS; } - enable_dma(denali, false); + denali_enable_dma(denali, false); pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_TODEVICE); } @@ -1569,11 +1569,11 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, setup_ecc_for_xfer(denali, true, false); - enable_dma(denali, true); + denali_enable_dma(denali, true); pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); clear_interrupts(denali); - setup_dma(denali, DENALI_READ); + denali_setup_dma(denali, DENALI_READ); /* wait for operation to complete */ irq_status = wait_for_irq(denali, irq_mask); @@ -1583,7 +1583,7 @@ static int denali_read_page(struct mtd_info *mtd, struct nand_chip *chip, memcpy(buf, denali->buf.buf, mtd->writesize); check_erased_page = handle_ecc(denali, buf, chip->oob_poi, irq_status); - enable_dma(denali, false); + denali_enable_dma(denali, false); if (check_erased_page) { @@ -1618,19 +1618,19 @@ static int denali_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip, uint32_t irq_mask = INTR_STATUS0__DMA_CMD_COMP; setup_ecc_for_xfer(denali, false, true); - enable_dma(denali, true); + denali_enable_dma(denali, true); pci_dma_sync_single_for_device(pci_dev, addr, size, PCI_DMA_FROMDEVICE); clear_interrupts(denali); - setup_dma(denali, DENALI_READ); + denali_setup_dma(denali, DENALI_READ); /* wait for operation to complete */ irq_status = wait_for_irq(denali, irq_mask); pci_dma_sync_single_for_cpu(pci_dev, addr, size, PCI_DMA_FROMDEVICE); - enable_dma(denali, false); + denali_enable_dma(denali, false); memcpy(buf, denali->buf.buf, mtd->writesize); memcpy(chip->oob_poi, denali->buf.buf + mtd->writesize, mtd->oobsize); -- cgit v1.2.3-70-g09d2 From ecce2a6f9bdc7635838baeff8a09a76c9a70e7e0 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 13 May 2010 22:07:46 +0200 Subject: drivers/mtd/nand: Use kzalloc Use kzalloc rather than the combination of kmalloc and memset. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,size,flags; statement S; @@ -x = kmalloc(size,flags); +x = kzalloc(size,flags); if (x == NULL) S -memset(x, 0, size); // Signed-off-by: Julia Lawall Signed-off-by: David Woodhouse --- drivers/mtd/nand/s3c2410.c | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/s3c2410.c b/drivers/mtd/nand/s3c2410.c index dc02dcd0c08..239aadfd01b 100644 --- a/drivers/mtd/nand/s3c2410.c +++ b/drivers/mtd/nand/s3c2410.c @@ -929,14 +929,13 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) pr_debug("s3c2410_nand_probe(%p)\n", pdev); - info = kmalloc(sizeof(*info), GFP_KERNEL); + info = kzalloc(sizeof(*info), GFP_KERNEL); if (info == NULL) { dev_err(&pdev->dev, "no memory for flash info\n"); err = -ENOMEM; goto exit_error; } - memset(info, 0, sizeof(*info)); platform_set_drvdata(pdev, info); spin_lock_init(&info->controller.lock); @@ -994,15 +993,13 @@ static int s3c24xx_nand_probe(struct platform_device *pdev) /* allocate our information */ size = nr_sets * sizeof(*info->mtds); - info->mtds = kmalloc(size, GFP_KERNEL); + info->mtds = kzalloc(size, GFP_KERNEL); if (info->mtds == NULL) { dev_err(&pdev->dev, "failed to allocate mtd storage\n"); err = -ENOMEM; goto exit_error; } - memset(info->mtds, 0, size); - /* initialise all possible chips */ nmtd = info->mtds; -- cgit v1.2.3-70-g09d2 From 2bfefa4c9632fb09bfe3277cf7b690818b147654 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 13 May 2010 22:03:15 +0200 Subject: drivers/mtd: Use kzalloc Use kzalloc rather than the combination of kmalloc and memset. The semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression x,size,flags; statement S; @@ -x = kmalloc(size,flags); +x = kzalloc(size,flags); if (x == NULL) S -memset(x, 0, size); // Signed-off-by: Julia Lawall Signed-off-by: David Woodhouse --- drivers/mtd/lpddr/qinfo_probe.c | 7 ++----- drivers/mtd/maps/ixp2000.c | 3 +-- drivers/mtd/maps/ixp4xx.c | 3 +-- drivers/mtd/maps/pxa2xx-flash.c | 3 +-- drivers/mtd/tests/mtd_pagetest.c | 3 +-- drivers/mtd/tests/mtd_readtest.c | 3 +-- drivers/mtd/tests/mtd_speedtest.c | 3 +-- drivers/mtd/tests/mtd_stresstest.c | 3 +-- drivers/mtd/tests/mtd_subpagetest.c | 3 +-- 9 files changed, 10 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/lpddr/qinfo_probe.c b/drivers/mtd/lpddr/qinfo_probe.c index 79bf40f48b7..dbfe17baf04 100644 --- a/drivers/mtd/lpddr/qinfo_probe.c +++ b/drivers/mtd/lpddr/qinfo_probe.c @@ -134,13 +134,12 @@ out: static int lpddr_chip_setup(struct map_info *map, struct lpddr_private *lpddr) { - lpddr->qinfo = kmalloc(sizeof(struct qinfo_chip), GFP_KERNEL); + lpddr->qinfo = kzalloc(sizeof(struct qinfo_chip), GFP_KERNEL); if (!lpddr->qinfo) { printk(KERN_WARNING "%s: no memory for LPDDR qinfo structure\n", map->name); return 0; } - memset(lpddr->qinfo, 0, sizeof(struct qinfo_chip)); /* Get the ManuID */ lpddr->ManufactId = CMDVAL(map_read(map, map->pfow_base + PFOW_MANUFACTURER_ID)); @@ -185,13 +184,11 @@ static struct lpddr_private *lpddr_probe_chip(struct map_info *map) lpddr.numchips = 1; numvirtchips = lpddr.numchips * lpddr.qinfo->HWPartsNum; - retlpddr = kmalloc(sizeof(struct lpddr_private) + + retlpddr = kzalloc(sizeof(struct lpddr_private) + numvirtchips * sizeof(struct flchip), GFP_KERNEL); if (!retlpddr) return NULL; - memset(retlpddr, 0, sizeof(struct lpddr_private) + - numvirtchips * sizeof(struct flchip)); memcpy(retlpddr, &lpddr, sizeof(struct lpddr_private)); retlpddr->numchips = numvirtchips; diff --git a/drivers/mtd/maps/ixp2000.c b/drivers/mtd/maps/ixp2000.c index 1bdf0ee6d0b..9639d83a9d6 100644 --- a/drivers/mtd/maps/ixp2000.c +++ b/drivers/mtd/maps/ixp2000.c @@ -165,12 +165,11 @@ static int ixp2000_flash_probe(struct platform_device *dev) return -EIO; } - info = kmalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct ixp2000_flash_info), GFP_KERNEL); if(!info) { err = -ENOMEM; goto Error; } - memset(info, 0, sizeof(struct ixp2000_flash_info)); platform_set_drvdata(dev, info); diff --git a/drivers/mtd/maps/ixp4xx.c b/drivers/mtd/maps/ixp4xx.c index 7513d90fee6..e0a5e0426ea 100644 --- a/drivers/mtd/maps/ixp4xx.c +++ b/drivers/mtd/maps/ixp4xx.c @@ -196,12 +196,11 @@ static int ixp4xx_flash_probe(struct platform_device *dev) return err; } - info = kmalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct ixp4xx_flash_info), GFP_KERNEL); if(!info) { err = -ENOMEM; goto Error; } - memset(info, 0, sizeof(struct ixp4xx_flash_info)); platform_set_drvdata(dev, info); diff --git a/drivers/mtd/maps/pxa2xx-flash.c b/drivers/mtd/maps/pxa2xx-flash.c index 91dc6331053..dd90880048c 100644 --- a/drivers/mtd/maps/pxa2xx-flash.c +++ b/drivers/mtd/maps/pxa2xx-flash.c @@ -63,11 +63,10 @@ static int __init pxa2xx_flash_probe(struct platform_device *pdev) if (!res) return -ENODEV; - info = kmalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL); + info = kzalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL); if (!info) return -ENOMEM; - memset(info, 0, sizeof(struct pxa2xx_flash_info)); info->map.name = (char *) flash->name; info->map.bankwidth = flash->width; info->map.phys = res->start; diff --git a/drivers/mtd/tests/mtd_pagetest.c b/drivers/mtd/tests/mtd_pagetest.c index 921a85df919..6bc1b8276c6 100644 --- a/drivers/mtd/tests/mtd_pagetest.c +++ b/drivers/mtd/tests/mtd_pagetest.c @@ -480,12 +480,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c index 7107fccbc7d..afe71aa15c4 100644 --- a/drivers/mtd/tests/mtd_readtest.c +++ b/drivers/mtd/tests/mtd_readtest.c @@ -141,12 +141,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); /* NOR flash does not implement block_isbad */ if (mtd->block_isbad == NULL) diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c index 56ca62bb96b..161feeb7b8b 100644 --- a/drivers/mtd/tests/mtd_speedtest.c +++ b/drivers/mtd/tests/mtd_speedtest.c @@ -295,12 +295,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); /* NOR flash does not implement block_isbad */ if (mtd->block_isbad == NULL) diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c index 3854afec56d..531625fc925 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/mtd_stresstest.c @@ -221,12 +221,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); /* NOR flash does not implement block_isbad */ if (mtd->block_isbad == NULL) diff --git a/drivers/mtd/tests/mtd_subpagetest.c b/drivers/mtd/tests/mtd_subpagetest.c index 700237a3d12..11204e8aab5 100644 --- a/drivers/mtd/tests/mtd_subpagetest.c +++ b/drivers/mtd/tests/mtd_subpagetest.c @@ -354,12 +354,11 @@ static int scan_for_bad_eraseblocks(void) { int i, bad = 0; - bbt = kmalloc(ebcnt, GFP_KERNEL); + bbt = kzalloc(ebcnt, GFP_KERNEL); if (!bbt) { printk(PRINT_PREF "error: cannot allocate memory\n"); return -ENOMEM; } - memset(bbt, 0 , ebcnt); printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { -- cgit v1.2.3-70-g09d2 From 9d5da3a9b849cf42fc165e90b1d39e2fd1e199a8 Mon Sep 17 00:00:00 2001 From: Jason Gunthorpe Date: Tue, 9 Mar 2010 12:27:56 -0700 Subject: mtd: extend physmap_of to let the device tree specify the parition probe This is to support custom partitioning schemes for embedded PPC. To use define your own mtd_part_parser and then add something like: linux,part-probe = "my_probe", "cmdlinepart"; To the board's dts file. If linux,part-probe is not specified then this behaves the same as before. Signed-off-by: Jason Gunthorpe Signed-off-by: David Woodhouse --- drivers/mtd/maps/physmap_of.c | 53 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 48 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/physmap_of.c b/drivers/mtd/maps/physmap_of.c index bbdd2194190..36dbcee1ac2 100644 --- a/drivers/mtd/maps/physmap_of.c +++ b/drivers/mtd/maps/physmap_of.c @@ -173,12 +173,53 @@ static struct mtd_info * __devinit obsolete_probe(struct of_device *dev, } } +#ifdef CONFIG_MTD_PARTITIONS +/* When partitions are set we look for a linux,part-probe property which + specifies the list of partition probers to use. If none is given then the + default is use. These take precedence over other device tree + information. */ +static const char *part_probe_types_def[] = { "cmdlinepart", "RedBoot", NULL }; +static const char ** __devinit of_get_probes(struct device_node *dp) +{ + const char *cp; + int cplen; + unsigned int l; + unsigned int count; + const char **res; + + cp = of_get_property(dp, "linux,part-probe", &cplen); + if (cp == NULL) + return part_probe_types_def; + + count = 0; + for (l = 0; l != cplen; l++) + if (cp[l] == 0) + count++; + + res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL); + count = 0; + while (cplen > 0) { + res[count] = cp; + l = strlen(cp) + 1; + cp += l; + cplen -= l; + count++; + } + return res; +} + +static void __devinit of_free_probes(const char **probes) +{ + if (probes != part_probe_types_def) + kfree(probes); +} +#endif + static int __devinit of_flash_probe(struct of_device *dev, const struct of_device_id *match) { #ifdef CONFIG_MTD_PARTITIONS - static const char *part_probe_types[] - = { "cmdlinepart", "RedBoot", NULL }; + const char **part_probe_types; #endif struct device_node *dp = dev->node; struct resource res; @@ -307,12 +348,14 @@ static int __devinit of_flash_probe(struct of_device *dev, 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 */ + part_probe_types = of_get_probes(dp); err = parse_mtd_partitions(info->cmtd, part_probe_types, &info->parts, 0); - if (err < 0) + if (err < 0) { + of_free_probes(part_probe_types); return err; + } + of_free_probes(part_probe_types); #ifdef CONFIG_MTD_OF_PARTS if (err == 0) { -- cgit v1.2.3-70-g09d2 From d484018056816178abffacb84b8c16628e880c83 Mon Sep 17 00:00:00 2001 From: Ivo Clarysse Date: Thu, 8 Apr 2010 16:14:44 +0200 Subject: mtd: mxc_nand: set NFC registers after reset This patch allows the mxc_nand driver to reset the NAND flash controller. NFC registers are (re-)set after completion of the reset, as a reset will have reverted the NFC registers to their default values. Signed-off-by: Ivo Clarysse Acked-by: Sascha Hauer Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 90 ++++++++++++++++++++++++--------------------- 1 file changed, 48 insertions(+), 42 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 2ba3be1f493..b527aa2d687 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -542,6 +542,41 @@ static void mxc_do_addr_cycle(struct mtd_info *mtd, int column, int page_addr) } } +static void preset(struct mtd_info *mtd) +{ + struct nand_chip *nand_chip = mtd->priv; + struct mxc_nand_host *host = nand_chip->priv; + uint16_t tmp; + + /* disable interrupt, disable spare enable */ + tmp = readw(host->regs + NFC_CONFIG1); + tmp |= NFC_INT_MSK; + tmp &= ~NFC_SP_EN; + if (nand_chip->ecc.mode == NAND_ECC_HW) { + tmp |= NFC_ECC_EN; + } else { + tmp &= ~NFC_ECC_EN; + } + writew(tmp, host->regs + NFC_CONFIG1); + /* preset operation */ + + /* Unlock the internal RAM Buffer */ + writew(0x2, host->regs + NFC_CONFIG); + + /* Blocks to be unlocked */ + if (nfc_is_v21()) { + writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); + writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); + } else if (nfc_is_v1()) { + writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); + writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); + } else + BUG(); + + /* Unlock Block Command for given address range */ + writew(0x4, host->regs + NFC_WRPROT); +} + /* Used by the upper layer to write command to NAND Flash for * different operations to be carried out on NAND Flash */ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, @@ -559,6 +594,10 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, /* Command pre-processing step */ switch (command) { + case NAND_CMD_RESET: + send_cmd(host, command, false); + preset(mtd); + break; case NAND_CMD_STATUS: host->buf_start = 0; @@ -680,7 +719,6 @@ static int __init mxcnd_probe(struct platform_device *pdev) struct mxc_nand_platform_data *pdata = pdev->dev.platform_data; struct mxc_nand_host *host; struct resource *res; - uint16_t tmp; int err = 0, nr_parts = 0; struct nand_ecclayout *oob_smallpage, *oob_largepage; @@ -744,51 +782,17 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->spare_len = 64; oob_smallpage = &nandv2_hw_eccoob_smallpage; oob_largepage = &nandv2_hw_eccoob_largepage; + this->ecc.bytes = 9; } else if (nfc_is_v1()) { host->regs = host->base; host->spare0 = host->base + 0x800; host->spare_len = 16; oob_smallpage = &nandv1_hw_eccoob_smallpage; oob_largepage = &nandv1_hw_eccoob_largepage; - } else - BUG(); - - /* disable interrupt and spare enable */ - tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_INT_MSK; - tmp &= ~NFC_SP_EN; - writew(tmp, host->regs + NFC_CONFIG1); - - init_waitqueue_head(&host->irq_waitq); - - host->irq = platform_get_irq(pdev, 0); - - err = request_irq(host->irq, mxc_nfc_irq, 0, DRIVER_NAME, host); - if (err) - goto eirq; - - /* Reset NAND */ - this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); - - /* preset operation */ - /* Unlock the internal RAM Buffer */ - writew(0x2, host->regs + NFC_CONFIG); - - /* Blocks to be unlocked */ - if (nfc_is_v21()) { - writew(0x0, host->regs + NFC_V21_UNLOCKSTART_BLKADDR); - writew(0xffff, host->regs + NFC_V21_UNLOCKEND_BLKADDR); - this->ecc.bytes = 9; - } else if (nfc_is_v1()) { - writew(0x0, host->regs + NFC_V1_UNLOCKSTART_BLKADDR); - writew(0x4000, host->regs + NFC_V1_UNLOCKEND_BLKADDR); this->ecc.bytes = 3; } else BUG(); - /* Unlock Block Command for given address range */ - writew(0x4, host->regs + NFC_WRPROT); - this->ecc.size = 512; this->ecc.layout = oob_smallpage; @@ -797,14 +801,8 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->ecc.hwctl = mxc_nand_enable_hwecc; this->ecc.correct = mxc_nand_correct_data; this->ecc.mode = NAND_ECC_HW; - tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_ECC_EN; - writew(tmp, host->regs + NFC_CONFIG1); } else { this->ecc.mode = NAND_ECC_SOFT; - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_ECC_EN; - writew(tmp, host->regs + NFC_CONFIG1); } /* NAND bus width determines access funtions used by upper layer */ @@ -818,6 +816,14 @@ static int __init mxcnd_probe(struct platform_device *pdev) this->options |= NAND_USE_FLASH_BBT; } + init_waitqueue_head(&host->irq_waitq); + + host->irq = platform_get_irq(pdev, 0); + + err = request_irq(host->irq, mxc_nfc_irq, 0, DRIVER_NAME, host); + if (err) + goto eirq; + /* first scan to find the device and get the page size */ if (nand_scan_ident(mtd, 1, NULL)) { err = -ENXIO; -- cgit v1.2.3-70-g09d2 From a47bfd2eb66837653dc3b42541dfe4283dd41251 Mon Sep 17 00:00:00 2001 From: Ivo Clarysse Date: Thu, 8 Apr 2010 16:16:51 +0200 Subject: mtd: mxc_nand: support i.MX21 On i.MX21 SoCs, if the NFC_CONFIG1:NFC_INT_MASK bit is set, NFC_CONFIG2:NFC_INT always reads out zero, even if an operation is completed. This patch uses enable_irq and disable_irq_nosync instead of NFC_CONFIG1:NFC_INT_MASK to mask NFC interrupts. This allows NFC_CONFIG2:NFC_INT to also be used to detect operation completion on i.MX21. The i.MX21 NFC does not signal reset completion using NFC_CONFIG1:NFC_INT_MASK, so instead reset completion is tested by checking if NFC_CONFIG2 becomes 0. Signed-off-by: Ivo Clarysse Acked-by: Sascha Hauer Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 41 +++++++++++++++++++++++++---------------- 1 file changed, 25 insertions(+), 16 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index b527aa2d687..35da3dc4bd1 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -38,7 +38,7 @@ #define DRIVER_NAME "mxc_nand" #define nfc_is_v21() (cpu_is_mx25() || cpu_is_mx35()) -#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27()) +#define nfc_is_v1() (cpu_is_mx31() || cpu_is_mx27() || cpu_is_mx21()) /* Addresses for NFC registers */ #define NFC_BUF_SIZE 0xE00 @@ -168,11 +168,7 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) { struct mxc_nand_host *host = dev_id; - uint16_t tmp; - - tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_INT_MSK; /* Disable interrupt */ - writew(tmp, host->regs + NFC_CONFIG1); + disable_irq_nosync(irq); wake_up(&host->irq_waitq); @@ -184,15 +180,13 @@ static irqreturn_t mxc_nfc_irq(int irq, void *dev_id) */ static void wait_op_done(struct mxc_nand_host *host, int useirq) { - uint32_t tmp; - int max_retries = 2000; + uint16_t tmp; + int max_retries = 8000; if (useirq) { if ((readw(host->regs + NFC_CONFIG2) & NFC_INT) == 0) { - tmp = readw(host->regs + NFC_CONFIG1); - tmp &= ~NFC_INT_MSK; /* Enable interrupt */ - writew(tmp, host->regs + NFC_CONFIG1); + enable_irq(host->irq); wait_event(host->irq_waitq, readw(host->regs + NFC_CONFIG2) & NFC_INT); @@ -226,8 +220,23 @@ static void send_cmd(struct mxc_nand_host *host, uint16_t cmd, int useirq) writew(cmd, host->regs + NFC_FLASH_CMD); writew(NFC_CMD, host->regs + NFC_CONFIG2); - /* Wait for operation to complete */ - wait_op_done(host, useirq); + if (cpu_is_mx21() && (cmd == NAND_CMD_RESET)) { + int max_retries = 100; + /* Reset completion is indicated by NFC_CONFIG2 */ + /* being set to 0 */ + while (max_retries-- > 0) { + if (readw(host->regs + NFC_CONFIG2) == 0) { + break; + } + udelay(1); + } + if (max_retries < 0) + DEBUG(MTD_DEBUG_LEVEL0, "%s: RESET failed\n", + __func__); + } else { + /* Wait for operation to complete */ + wait_op_done(host, useirq); + } } /* This function sends an address (or partial address) to the @@ -548,9 +557,9 @@ static void preset(struct mtd_info *mtd) struct mxc_nand_host *host = nand_chip->priv; uint16_t tmp; - /* disable interrupt, disable spare enable */ + /* enable interrupt, disable spare enable */ tmp = readw(host->regs + NFC_CONFIG1); - tmp |= NFC_INT_MSK; + tmp &= ~NFC_INT_MSK; tmp &= ~NFC_SP_EN; if (nand_chip->ecc.mode == NAND_ECC_HW) { tmp |= NFC_ECC_EN; @@ -820,7 +829,7 @@ static int __init mxcnd_probe(struct platform_device *pdev) host->irq = platform_get_irq(pdev, 0); - err = request_irq(host->irq, mxc_nfc_irq, 0, DRIVER_NAME, host); + err = request_irq(host->irq, mxc_nfc_irq, IRQF_DISABLED, DRIVER_NAME, host); if (err) goto eirq; -- cgit v1.2.3-70-g09d2 From c3611570ddf601609f8803574ea83889ff969aa0 Mon Sep 17 00:00:00 2001 From: Maxim Levitsky Date: Mon, 19 Apr 2010 18:20:41 +0300 Subject: mtd: sm_common: split smartmedia and xD table 2GB xD card, and 4MB SmartMedia ROM card share same ID, so to make both work split xD and smartmedia ID tables. Hardware driver must be able to know which type it handles (and probably just one). Signed-off-by: Maxim Levitsky Signed-off-by: David Woodhouse --- drivers/mtd/nand/r852.c | 2 +- drivers/mtd/nand/sm_common.c | 37 +++++++++++++++++++++---------------- drivers/mtd/nand/sm_common.h | 2 +- 3 files changed, 23 insertions(+), 18 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 96bfbd8e8fd..6dfbb471316 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -666,7 +666,7 @@ int r852_register_nand_device(struct r852_device *dev) r852_engine_enable(dev); - if (sm_register_device(dev->mtd)) + if (sm_register_device(dev->mtd, dev->sm)) goto error2; if (device_create_file(&dev->mtd->dev, &dev_attr_media_type)) diff --git a/drivers/mtd/nand/sm_common.c b/drivers/mtd/nand/sm_common.c index aae0b9acd7a..ac80fb362e6 100644 --- a/drivers/mtd/nand/sm_common.c +++ b/drivers/mtd/nand/sm_common.c @@ -68,8 +68,6 @@ static int sm_block_markbad(struct mtd_info *mtd, loff_t ofs) static struct nand_flash_dev nand_smartmedia_flash_ids[] = { - - /* SmartMedia */ {"SmartMedia 1MiB 5V", 0x6e, 256, 1, 0x1000, 0}, {"SmartMedia 1MiB 3,3V", 0xe8, 256, 1, 0x1000, 0}, {"SmartMedia 1MiB 3,3V", 0xec, 256, 1, 0x1000, 0}, @@ -82,28 +80,34 @@ static struct nand_flash_dev nand_smartmedia_flash_ids[] = { {"SmartMedia 4MiB 3,3V ROM", 0xd5, 512, 4, 0x2000, NAND_ROM}, {"SmartMedia 8MiB 3,3V", 0xe6, 512, 8, 0x2000, 0}, {"SmartMedia 8MiB 3,3V ROM", 0xd6, 512, 8, 0x2000, NAND_ROM}, - -#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD) - /* xD / SmartMedia */ - {"SmartMedia/xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, + {"SmartMedia 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, {"SmartMedia 16MiB 3,3V ROM", 0x57, 512, 16, 0x4000, NAND_ROM}, - {"SmartMedia/xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, + {"SmartMedia 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, {"SmartMedia 32MiB 3,3V ROM", 0x58, 512, 32, 0x4000, NAND_ROM}, - {"SmartMedia/xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, + {"SmartMedia 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, {"SmartMedia 64MiB 3,3V ROM", 0xd9, 512, 64, 0x4000, NAND_ROM}, - {"SmartMedia/xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, + {"SmartMedia 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, {"SmartMedia 128MiB 3,3V ROM", 0xda, 512, 128, 0x4000, NAND_ROM}, - {"SmartMedia/xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM}, + {"SmartMedia 256MiB 3,3V", 0x71, 512, 256, 0x4000 }, {"SmartMedia 256MiB 3,3V ROM", 0x5b, 512, 256, 0x4000, NAND_ROM}, + {NULL,} +}; - /* xD only */ - {"xD 512MiB 3,3V", 0xDC, 512, 512, 0x4000, XD_TYPEM}, - {"xD 1GiB 3,3V", 0xD3, 512, 1024, 0x4000, XD_TYPEM}, - {"xD 2GiB 3,3V", 0xD5, 512, 2048, 0x4000, XD_TYPEM}, +#define XD_TYPEM (NAND_NO_AUTOINCR | NAND_BROKEN_XD) +static struct nand_flash_dev nand_xd_flash_ids[] = { + + {"xD 16MiB 3,3V", 0x73, 512, 16, 0x4000, 0}, + {"xD 32MiB 3,3V", 0x75, 512, 32, 0x4000, 0}, + {"xD 64MiB 3,3V", 0x76, 512, 64, 0x4000, 0}, + {"xD 128MiB 3,3V", 0x79, 512, 128, 0x4000, 0}, + {"xD 256MiB 3,3V", 0x71, 512, 256, 0x4000, XD_TYPEM}, + {"xD 512MiB 3,3V", 0xdc, 512, 512, 0x4000, XD_TYPEM}, + {"xD 1GiB 3,3V", 0xd3, 512, 1024, 0x4000, XD_TYPEM}, + {"xD 2GiB 3,3V", 0xd5, 512, 2048, 0x4000, XD_TYPEM}, {NULL,} }; -int sm_register_device(struct mtd_info *mtd) +int sm_register_device(struct mtd_info *mtd, int smartmedia) { struct nand_chip *chip = (struct nand_chip *)mtd->priv; int ret; @@ -111,7 +115,8 @@ int sm_register_device(struct mtd_info *mtd) chip->options |= NAND_SKIP_BBTSCAN; /* Scan for card properties */ - ret = nand_scan_ident(mtd, 1, nand_smartmedia_flash_ids); + ret = nand_scan_ident(mtd, 1, smartmedia ? + nand_smartmedia_flash_ids : nand_xd_flash_ids); if (ret) return ret; diff --git a/drivers/mtd/nand/sm_common.h b/drivers/mtd/nand/sm_common.h index 18284f5fae6..00f4a83359b 100644 --- a/drivers/mtd/nand/sm_common.h +++ b/drivers/mtd/nand/sm_common.h @@ -36,7 +36,7 @@ struct sm_oob { #define SM_SMALL_OOB_SIZE 8 -extern int sm_register_device(struct mtd_info *mtd); +extern int sm_register_device(struct mtd_info *mtd, int smartmedia); static inline int sm_sector_valid(struct sm_oob *oob) -- cgit v1.2.3-70-g09d2 From eedfea252690435858722a8da1109d104d639087 Mon Sep 17 00:00:00 2001 From: Ben Dooks Date: Tue, 20 Apr 2010 10:26:18 +0100 Subject: mtd: orion/kirkwood: add RnB line support to orion mtd driver Add support for a board to register a callback to get the state of the RnB line if it has it attached. Signed-off-by: Ben Dooks Signed-off-by: David Woodhouse --- arch/arm/plat-orion/include/plat/orion_nand.h | 1 + drivers/mtd/nand/orion_nand.c | 3 +++ 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/arch/arm/plat-orion/include/plat/orion_nand.h b/arch/arm/plat-orion/include/plat/orion_nand.h index d6a4cfa3778..9f3c180834d 100644 --- a/arch/arm/plat-orion/include/plat/orion_nand.h +++ b/arch/arm/plat-orion/include/plat/orion_nand.h @@ -14,6 +14,7 @@ */ struct orion_nand_data { struct mtd_partition *parts; + int (*dev_ready)(struct mtd_info *mtd); u32 nr_parts; u8 ale; /* address line number connected to ALE */ u8 cle; /* address line number connected to CLE */ diff --git a/drivers/mtd/nand/orion_nand.c b/drivers/mtd/nand/orion_nand.c index f4444fe960a..da6e7534305 100644 --- a/drivers/mtd/nand/orion_nand.c +++ b/drivers/mtd/nand/orion_nand.c @@ -126,6 +126,9 @@ static int __init orion_nand_probe(struct platform_device *pdev) if (board->width == 16) nc->options |= NAND_BUSWIDTH_16; + if (board->dev_ready) + nc->dev_ready = board->dev_ready; + platform_set_drvdata(pdev, mtd); if (nand_scan(mtd, 1)) { -- cgit v1.2.3-70-g09d2 From 8473044d644553ca3c939249490d1c5ef5f6d4e6 Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Sat, 24 Apr 2010 17:57:52 +0200 Subject: mtd: cfi_probe: enter Auto Select Mode after filling cfi->cfiq members Move the code to enter Auto Select Mode down to be able to use cfi->cfiq members to add support for chips using alternative sequence / unlock addresses. Signed-off-by: Guillaume LECERF Reviewed-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_probe.c | 47 ++++++++++++++++++++++--------------------- 1 file changed, 24 insertions(+), 23 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index e63e6749429..f657d16cb98 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -181,29 +181,6 @@ static int __xipram cfi_chip_setup(struct map_info *map, for (i=0; i<(sizeof(struct cfi_ident) + num_erase_regions * 4); i++) ((unsigned char *)cfi->cfiq)[i] = cfi_read_query(map,base + (0x10 + i)*ofs_factor); - /* Note we put the device back into Read Mode BEFORE going into Auto - * Select Mode, as some devices support nesting of modes, others - * don't. This way should always work. - * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and - * so should be treated as nops or illegal (and so put the device - * back into Read Mode, which is a nop in this case). - */ - cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi->mfr = cfi_read_query16(map, base); - cfi->id = cfi_read_query16(map, base + ofs_factor); - - /* Get AMD/Spansion extended JEDEC ID */ - if (cfi->mfr == CFI_MFR_AMD && (cfi->id & 0xff) == 0x7e) - cfi->id = cfi_read_query(map, base + 0xe * ofs_factor) << 8 | - cfi_read_query(map, base + 0xf * ofs_factor); - - /* Put it back into Read Mode */ - cfi_qry_mode_off(base, map, cfi); - xip_allowed(base, map); - /* Do any necessary byteswapping */ cfi->cfiq->P_ID = le16_to_cpu(cfi->cfiq->P_ID); @@ -228,6 +205,30 @@ static int __xipram cfi_chip_setup(struct map_info *map, #endif } + /* + * Note we put the device back into Read Mode BEFORE going into Auto + * Select Mode, as some devices support nesting of modes, others + * don't. This way should always work. + * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and + * so should be treated as nops or illegal (and so put the device + * back into Read Mode, which is a nop in this case). + */ + cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi->mfr = cfi_read_query16(map, base); + cfi->id = cfi_read_query16(map, base + ofs_factor); + + /* Get AMD/Spansion extended JEDEC ID */ + if (cfi->mfr == CFI_MFR_AMD && (cfi->id & 0xff) == 0x7e) + cfi->id = cfi_read_query(map, base + 0xe * ofs_factor) << 8 | + cfi_read_query(map, base + 0xf * ofs_factor); + + /* Put it back into Read Mode */ + cfi_qry_mode_off(base, map, cfi); + xip_allowed(base, map); + printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit bank\n", map->name, cfi->interleave, cfi->device_type*8, base, map->bankwidth*8); -- cgit v1.2.3-70-g09d2 From ad7026fef6c771fc88ecbcb111876fc050b1a4d0 Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Sat, 24 Apr 2010 17:57:57 +0200 Subject: mtd: cfi_probe: make the addresses used to enter Auto Select Mode variable Make the addresses used to enter Auto Select Mode variable to leave place for handling chips using non-standard addresses. Signed-off-by: Guillaume LECERF Reviewed-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_probe.c | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index f657d16cb98..045dc100496 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -158,6 +158,7 @@ static int __xipram cfi_chip_setup(struct map_info *map, __u32 base = 0; int num_erase_regions = cfi_read_query(map, base + (0x10 + 28)*ofs_factor); int i; + int addr_unlock1 = 0x555, addr_unlock2 = 0x2AA; xip_enable(base, map, cfi); #ifdef DEBUG_CFI @@ -214,9 +215,9 @@ static int __xipram cfi_chip_setup(struct map_info *map, * back into Read Mode, which is a nop in this case). */ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); - cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0xaa, addr_unlock1, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, addr_unlock2, base, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, addr_unlock1, base, map, cfi, cfi->device_type, NULL); cfi->mfr = cfi_read_query16(map, base); cfi->id = cfi_read_query16(map, base + ofs_factor); -- cgit v1.2.3-70-g09d2 From 54b93a49d8dd90dfb658f21a3316527fe6195106 Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Sat, 24 Apr 2010 17:58:02 +0200 Subject: mtd: cfi_probe: add support for SST 0x0701 vendorname SST 39VF160x and 39VF320x chips use vendorname id 0x0701 and alternative unlock addresses. Add support for them in cfi_probe.c. Signed-off-by: Guillaume LECERF Reviewed-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_probe.c | 8 ++++++++ include/linux/mtd/cfi.h | 1 + 2 files changed, 9 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index 045dc100496..b2acd32f4fb 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -206,6 +206,11 @@ static int __xipram cfi_chip_setup(struct map_info *map, #endif } + if (cfi->cfiq->P_ID == P_ID_SST_OLD) { + addr_unlock1 = 0x5555; + addr_unlock2 = 0x2AAA; + } + /* * Note we put the device back into Read Mode BEFORE going into Auto * Select Mode, as some devices support nesting of modes, others @@ -271,6 +276,9 @@ static char *vendorname(__u16 vendor) case P_ID_SST_PAGE: return "SST Page Write"; + case P_ID_SST_OLD: + return "SST 39VF160x/39VF320x"; + case P_ID_INTEL_PERFORMANCE: return "Intel Performance Code"; diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index cee05b1e62b..5716fc78ca8 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -253,6 +253,7 @@ struct cfi_bri_query { #define P_ID_MITSUBISHI_STD 0x0100 #define P_ID_MITSUBISHI_EXT 0x0101 #define P_ID_SST_PAGE 0x0102 +#define P_ID_SST_OLD 0x0701 #define P_ID_INTEL_PERFORMANCE 0x0200 #define P_ID_INTEL_DATA 0x0210 #define P_ID_RESERVED 0xffff -- cgit v1.2.3-70-g09d2 From 58598861227877bb481b9035d2a07283577a2274 Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Sat, 24 Apr 2010 17:58:07 +0200 Subject: mtd: cfi_probe: use P_ID_* definitions instead of hardcoded values Use P_ID_* definitions already in include/linux/mtd/cfi.h instead of the hardcoded values. Make the code more readable. Signed-off-by: Guillaume LECERF Reviewed-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/gen_probe.c | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index fcc1bc02c8a..1d56887c839 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -241,17 +241,17 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary) /* We need these for the !CONFIG_MODULES case, because symbol_get() doesn't work there */ #ifdef CONFIG_MTD_CFI_INTELEXT - case 0x0001: - case 0x0003: - case 0x0200: + case P_ID_INTEL_EXT: + case P_ID_INTEL_STD: + case P_ID_INTEL_PERFORMANCE: return cfi_cmdset_0001(map, primary); #endif #ifdef CONFIG_MTD_CFI_AMDSTD - case 0x0002: + case P_ID_AMD_STD: return cfi_cmdset_0002(map, primary); #endif #ifdef CONFIG_MTD_CFI_STAA - case 0x0020: + case P_ID_ST_ADV: return cfi_cmdset_0020(map, primary); #endif default: -- cgit v1.2.3-70-g09d2 From 564b84978df2bf83d334940f1a1190702579f79f Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Sat, 24 Apr 2010 17:58:17 +0200 Subject: mtd: cfi_cmdset_0002: do not fail on no extended query table as they are both optional After looking at AMD's CFI specification [1], both of the extended query tables are optional. Thus, it looks like relying that at least one of those tables exist is a bug in cfi_cmdset_0002. This patch inverts the logic and checks for unlock function pointers before exiting on error. This approach leaves place to add a call to a fixup function to try to handle chips compatible with the early AMD specification from 1995 [2]. [1] http://www.amd.com/us-en/assets/content_type/DownloadableAssets/cfi_r20.pdf [2] http://noel.feld.cvut.cz/hw/amd/20158a.pdf Signed-off-by: Guillaume LECERF Reviewed-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 89 +++++++++++++++++++------------------ 1 file changed, 45 insertions(+), 44 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index c16b8cecc3a..ce38d3d049e 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -357,65 +357,66 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) if (cfi->cfi_mode==CFI_MODE_CFI){ unsigned char bootloc; - /* - * It's a real CFI chip, not one for which the probe - * routine faked a CFI structure. So we read the feature - * table from it. - */ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; struct cfi_pri_amdstd *extp; extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); - if (!extp) { - kfree(mtd); - return NULL; - } - - cfi_fixup_major_minor(cfi, extp); - - if (extp->MajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { - printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " - "version %c.%c.\n", extp->MajorVersion, - extp->MinorVersion); - kfree(extp); - kfree(mtd); - return NULL; - } + if (extp) { + /* + * It's a real CFI chip, not one for which the probe + * routine faked a CFI structure. + */ + cfi_fixup_major_minor(cfi, extp); + + if (extp->MajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '4')) { + printk(KERN_ERR " Unknown Amd/Fujitsu Extended Query " + "version %c.%c.\n", extp->MajorVersion, + extp->MinorVersion); + kfree(extp); + kfree(mtd); + return NULL; + } - /* Install our own private info structure */ - cfi->cmdset_priv = extp; + /* Install our own private info structure */ + cfi->cmdset_priv = extp; - /* Apply cfi device specific fixups */ - cfi_fixup(mtd, cfi_fixup_table); + /* Apply cfi device specific fixups */ + cfi_fixup(mtd, cfi_fixup_table); #ifdef DEBUG_CFI_FEATURES - /* Tell the user about it in lots of lovely detail */ - cfi_tell_features(extp); + /* Tell the user about it in lots of lovely detail */ + cfi_tell_features(extp); #endif - bootloc = extp->TopBottom; - if ((bootloc != 2) && (bootloc != 3)) { - printk(KERN_WARNING "%s: CFI does not contain boot " - "bank location. Assuming top.\n", map->name); - bootloc = 2; - } + bootloc = extp->TopBottom; + if ((bootloc != 2) && (bootloc != 3)) { + printk(KERN_WARNING "%s: CFI does not contain boot " + "bank location. Assuming top.\n", map->name); + bootloc = 2; + } - if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { - printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); + if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { + printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); - for (i=0; icfiq->NumEraseRegions / 2; i++) { - int j = (cfi->cfiq->NumEraseRegions-1)-i; - __u32 swap; + for (i=0; icfiq->NumEraseRegions / 2; i++) { + int j = (cfi->cfiq->NumEraseRegions-1)-i; + __u32 swap; - swap = cfi->cfiq->EraseRegionInfo[i]; - cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j]; - cfi->cfiq->EraseRegionInfo[j] = swap; + swap = cfi->cfiq->EraseRegionInfo[i]; + cfi->cfiq->EraseRegionInfo[i] = cfi->cfiq->EraseRegionInfo[j]; + cfi->cfiq->EraseRegionInfo[j] = swap; + } } + /* Set the default CFI lock/unlock addresses */ + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2aa; + } + + if (!cfi->addr_unlock1 || !cfi->addr_unlock2) { + kfree(mtd); + return NULL; } - /* Set the default CFI lock/unlock addresses */ - cfi->addr_unlock1 = 0x555; - cfi->addr_unlock2 = 0x2aa; } /* CFI mode */ else if (cfi->cfi_mode == CFI_MODE_JEDEC) { -- cgit v1.2.3-70-g09d2 From 412da2f6e083eba6e4bd91ff2e78abb4735357a7 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 14 May 2010 01:35:54 +0100 Subject: mtd: cfi_cmdset_0002: Tone down warning messages about TopBottom CFI field Accept values of 2-5 for TopBottom, where the newly-added 4 and 5 values mean a uniform layout. It does indicate WP layout but we don't handle that. Also don't say "broken" when swapping erase regions in a top-boot chip. That got retrospectively documented in the spec. Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index ce38d3d049e..c27dd1c936c 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -390,14 +390,15 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) #endif bootloc = extp->TopBottom; - if ((bootloc != 2) && (bootloc != 3)) { - printk(KERN_WARNING "%s: CFI does not contain boot " - "bank location. Assuming top.\n", map->name); + if ((bootloc < 2) || (bootloc > 5)) { + printk(KERN_WARNING "%s: CFI contains unrecognised boot " + "bank location (%d). Assuming bottom.\n", + bootloc, map->name); bootloc = 2; } if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { - printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); + printk(KERN_WARNING "%s: Swapping erase regions for top-boot CFI table.\n", map->name); for (i=0; icfiq->NumEraseRegions / 2; i++) { int j = (cfi->cfiq->NumEraseRegions-1)-i; -- cgit v1.2.3-70-g09d2 From 83dcd3bb1139060fedb15235f8614d2bac82e18d Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Sat, 24 Apr 2010 17:58:22 +0200 Subject: mtd: cfi_cmdset_0002: add CFI detection for SST 39VF{16, 32}xx chips SST 39VF{16,32}xx chips use the 0x0701 command set, fully compatible with the AMD one. This patch adds support for detecting them in CFI mode. Signed-off-by: Guillaume LECERF Reviewed-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 35 +++++++++++++++++++++++++++++++++++ drivers/mtd/chips/gen_probe.c | 1 + 2 files changed, 36 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index c27dd1c936c..b7d821d6183 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -258,6 +258,31 @@ static void fixup_use_atmel_lock(struct mtd_info *mtd, void *param) mtd->flags |= MTD_POWERUP_LOCK; } +static void fixup_old_sst_eraseregion(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + /* + * These flashes report two seperate eraseblock regions based on the + * sector_erase-size and block_erase-size, although they both operate on the + * same memory. This is not allowed according to CFI, so we just pick the + * sector_erase-size. + */ + cfi->cfiq->NumEraseRegions = 1; +} + +static void fixup_sst39vf(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + fixup_old_sst_eraseregion(mtd); + + cfi->addr_unlock1 = 0x5555; + cfi->addr_unlock2 = 0x2AAA; +} + static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param) { struct map_info *map = mtd->priv; @@ -280,6 +305,15 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param) } } +/* Used to fix CFI-Tables of chips without Extended Query Tables */ +static struct cfi_fixup cfi_nopri_fixup_table[] = { + { CFI_MFR_SST, 0x234A, fixup_sst39vf, NULL, }, // SST39VF1602 + { CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, // SST39VF1601 + { CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, // SST39VF3202 + { CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, // SST39VF3201 + { 0, 0, NULL, NULL } +}; + static struct cfi_fixup cfi_fixup_table[] = { { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL }, #ifdef AMD_BOOTLOC_BUG @@ -413,6 +447,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) cfi->addr_unlock1 = 0x555; cfi->addr_unlock2 = 0x2aa; } + cfi_fixup(mtd, cfi_nopri_fixup_table); if (!cfi->addr_unlock1 || !cfi->addr_unlock2) { kfree(mtd); diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index 1d56887c839..75a8f9db8e4 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -248,6 +248,7 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary) #endif #ifdef CONFIG_MTD_CFI_AMDSTD case P_ID_AMD_STD: + case P_ID_SST_OLD: return cfi_cmdset_0002(map, primary); #endif #ifdef CONFIG_MTD_CFI_STAA -- cgit v1.2.3-70-g09d2 From 5a0563f0ad0c9864b735e9ae23e55f7fa9c73bf5 Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Sat, 24 Apr 2010 17:58:27 +0200 Subject: mtd: cfi_cmdset_0002: add CFI detection for SST 39VF{32, 64}xxB chips This patch adds support for detecting SST 39VF32xxB and 39VF64xxB chips in CFI mode. Signed-off-by: Guillaume LECERF Reviewed-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index b7d821d6183..0e21b098248 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -283,6 +283,17 @@ static void fixup_sst39vf(struct mtd_info *mtd, void *param) cfi->addr_unlock2 = 0x2AAA; } +static void fixup_sst39vf_rev_b(struct mtd_info *mtd, void *param) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + fixup_old_sst_eraseregion(mtd); + + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2AA; +} + static void fixup_s29gl064n_sectors(struct mtd_info *mtd, void *param) { struct map_info *map = mtd->priv; @@ -311,6 +322,10 @@ static struct cfi_fixup cfi_nopri_fixup_table[] = { { CFI_MFR_SST, 0x234B, fixup_sst39vf, NULL, }, // SST39VF1601 { CFI_MFR_SST, 0x235A, fixup_sst39vf, NULL, }, // SST39VF3202 { CFI_MFR_SST, 0x235B, fixup_sst39vf, NULL, }, // SST39VF3201 + { CFI_MFR_SST, 0x235C, fixup_sst39vf_rev_b, NULL, }, // SST39VF3202B + { CFI_MFR_SST, 0x235D, fixup_sst39vf_rev_b, NULL, }, // SST39VF3201B + { CFI_MFR_SST, 0x236C, fixup_sst39vf_rev_b, NULL, }, // SST39VF6402B + { CFI_MFR_SST, 0x236D, fixup_sst39vf_rev_b, NULL, }, // SST39VF6401B { 0, 0, NULL, NULL } }; -- cgit v1.2.3-70-g09d2 From 087444da61ed972b3c2bfbf7dcf317cb4475f143 Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Sat, 24 Apr 2010 17:58:32 +0200 Subject: mtd: cfi_util: do not printk if no extended query table Signed-off-by: Guillaume LECERF Reviewed-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_util.c | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_util.c b/drivers/mtd/chips/cfi_util.c index ca584d0380b..d7c2c672757 100644 --- a/drivers/mtd/chips/cfi_util.c +++ b/drivers/mtd/chips/cfi_util.c @@ -104,10 +104,11 @@ __xipram cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* n int i; struct cfi_extquery *extp = NULL; - printk(" %s Extended Query Table at 0x%4.4X\n", name, adr); if (!adr) goto out; + printk(KERN_INFO "%s Extended Query Table at 0x%4.4X\n", name, adr); + extp = kmalloc(size, GFP_KERNEL); if (!extp) { printk(KERN_ERR "Failed to allocate memory\n"); -- cgit v1.2.3-70-g09d2 From ae731822294468f213f2b56a0ddfc425148c873b Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 27 Apr 2010 04:19:34 +0200 Subject: mtd: chips: use common manufacturer codes in jedec_probe() Factor out old manufacturers and use the generic ones from cfi.h Signed-off-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 252 +++++++++++++++++++--------------------- include/linux/mtd/cfi.h | 13 ++- 2 files changed, 128 insertions(+), 137 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 8db1148dfa4..04fb45cacc3 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -22,24 +22,6 @@ #include #include -/* Manufacturers */ -#define MANUFACTURER_AMD 0x0001 -#define MANUFACTURER_ATMEL 0x001f -#define MANUFACTURER_EON 0x001c -#define MANUFACTURER_FUJITSU 0x0004 -#define MANUFACTURER_HYUNDAI 0x00AD -#define MANUFACTURER_INTEL 0x0089 -#define MANUFACTURER_MACRONIX 0x00C2 -#define MANUFACTURER_NEC 0x0010 -#define MANUFACTURER_PMC 0x009D -#define MANUFACTURER_SHARP 0x00b0 -#define MANUFACTURER_SST 0x00BF -#define MANUFACTURER_ST 0x0020 -#define MANUFACTURER_TOSHIBA 0x0098 -#define MANUFACTURER_WINBOND 0x00da -#define CONTINUATION_CODE 0x007f - - /* AMD */ #define AM29DL800BB 0x22CB #define AM29DL800BT 0x224A @@ -309,7 +291,7 @@ struct amd_flash_info { */ static const struct amd_flash_info jedec_table[] = { { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F032B, .name = "AMD AM29F032B", .uaddr = MTD_UADDR_0x0555_0x02AA, @@ -321,7 +303,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,64) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV160DT, .name = "AMD AM29LV160DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -336,7 +318,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV160DB, .name = "AMD AM29LV160DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -351,7 +333,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV400BB, .name = "AMD AM29LV400BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -366,7 +348,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV400BT, .name = "AMD AM29LV400BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -381,7 +363,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV800BB, .name = "AMD AM29LV800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -397,7 +379,7 @@ static const struct amd_flash_info jedec_table[] = { } }, { /* add DL */ - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29DL800BB, .name = "AMD AM29DL800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -414,7 +396,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,14) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29DL800BT, .name = "AMD AM29DL800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -431,7 +413,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F800BB, .name = "AMD AM29F800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -446,7 +428,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV800BT, .name = "AMD AM29LV800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -461,7 +443,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F800BT, .name = "AMD AM29F800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -476,7 +458,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F017D, .name = "AMD AM29F017D", .devtypes = CFI_DEVICETYPE_X8, @@ -488,7 +470,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F016D, .name = "AMD AM29F016D", .devtypes = CFI_DEVICETYPE_X8, @@ -500,7 +482,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F080, .name = "AMD AM29F080", .devtypes = CFI_DEVICETYPE_X8, @@ -512,7 +494,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F040, .name = "AMD AM29F040", .devtypes = CFI_DEVICETYPE_X8, @@ -524,7 +506,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29LV040B, .name = "AMD AM29LV040B", .devtypes = CFI_DEVICETYPE_X8, @@ -536,7 +518,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29F002T, .name = "AMD AM29F002T", .devtypes = CFI_DEVICETYPE_X8, @@ -551,7 +533,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29SL800DT, .name = "AMD AM29SL800DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -566,7 +548,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_AMD, + .mfr_id = CFI_MFR_AMD, .dev_id = AM29SL800DB, .name = "AMD AM29SL800DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -581,7 +563,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV512, .name = "Atmel AT49BV512", .devtypes = CFI_DEVICETYPE_X8, @@ -593,7 +575,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,1) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT29LV512, .name = "Atmel AT29LV512", .devtypes = CFI_DEVICETYPE_X8, @@ -606,7 +588,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x80,256) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV16X, .name = "Atmel AT49BV16X", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -619,7 +601,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV16XT, .name = "Atmel AT49BV16XT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -632,7 +614,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV32X, .name = "Atmel AT49BV32X", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -645,7 +627,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63) } }, { - .mfr_id = MANUFACTURER_ATMEL, + .mfr_id = CFI_MFR_ATMEL, .dev_id = AT49BV32XT, .name = "Atmel AT49BV32XT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -658,7 +640,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_EON, + .mfr_id = CFI_MFR_EON, .dev_id = EN29SL800BT, .name = "Eon EN29SL800BT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -673,7 +655,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_EON, + .mfr_id = CFI_MFR_EON, .dev_id = EN29SL800BB, .name = "Eon EN29SL800BB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -688,7 +670,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29F040C, .name = "Fujitsu MBM29F040C", .devtypes = CFI_DEVICETYPE_X8, @@ -700,7 +682,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29F800BA, .name = "Fujitsu MBM29F800BA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -715,7 +697,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV650UE, .name = "Fujitsu MBM29LV650UE", .devtypes = CFI_DEVICETYPE_X8, @@ -727,7 +709,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,128) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV320TE, .name = "Fujitsu MBM29LV320TE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -740,7 +722,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV320BE, .name = "Fujitsu MBM29LV320BE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -753,7 +735,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV160TE, .name = "Fujitsu MBM29LV160TE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -768,7 +750,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV160BE, .name = "Fujitsu MBM29LV160BE", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -783,7 +765,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV800BA, .name = "Fujitsu MBM29LV800BA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -798,7 +780,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV800TA, .name = "Fujitsu MBM29LV800TA", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -813,7 +795,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV400BC, .name = "Fujitsu MBM29LV400BC", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -828,7 +810,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7) } }, { - .mfr_id = MANUFACTURER_FUJITSU, + .mfr_id = CFI_MFR_FUJITSU, .dev_id = MBM29LV400TC, .name = "Fujitsu MBM29LV400TC", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -843,7 +825,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_HYUNDAI, + .mfr_id = CFI_MFR_HYUNDAI, .dev_id = HY29F002T, .name = "Hyundai HY29F002T", .devtypes = CFI_DEVICETYPE_X8, @@ -858,7 +840,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F004B3B, .name = "Intel 28F004B3B", .devtypes = CFI_DEVICETYPE_X8, @@ -871,7 +853,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 7), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F004B3T, .name = "Intel 28F004B3T", .devtypes = CFI_DEVICETYPE_X8, @@ -884,7 +866,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F400B3B, .name = "Intel 28F400B3B", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -897,7 +879,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 7), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F400B3T, .name = "Intel 28F400B3T", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -910,7 +892,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008B3B, .name = "Intel 28F008B3B", .devtypes = CFI_DEVICETYPE_X8, @@ -923,7 +905,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 15), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008B3T, .name = "Intel 28F008B3T", .devtypes = CFI_DEVICETYPE_X8, @@ -936,7 +918,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008S5, .name = "Intel 28F008S5", .devtypes = CFI_DEVICETYPE_X8, @@ -948,7 +930,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016S5, .name = "Intel 28F016S5", .devtypes = CFI_DEVICETYPE_X8, @@ -960,7 +942,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F008SA, .name = "Intel 28F008SA", .devtypes = CFI_DEVICETYPE_X8, @@ -972,7 +954,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 16), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F800B3B, .name = "Intel 28F800B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -985,7 +967,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 15), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F800B3T, .name = "Intel 28F800B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -998,7 +980,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016B3B, .name = "Intel 28F016B3B", .devtypes = CFI_DEVICETYPE_X8, @@ -1011,7 +993,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 31), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016S3, .name = "Intel I28F016S3", .devtypes = CFI_DEVICETYPE_X8, @@ -1023,7 +1005,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 32), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F016B3T, .name = "Intel 28F016B3T", .devtypes = CFI_DEVICETYPE_X8, @@ -1036,7 +1018,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F160B3B, .name = "Intel 28F160B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1049,7 +1031,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 31), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F160B3T, .name = "Intel 28F160B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -1062,7 +1044,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F320B3B, .name = "Intel 28F320B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1075,7 +1057,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 63), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F320B3T, .name = "Intel 28F320B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -1088,7 +1070,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F640B3B, .name = "Intel 28F640B3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1101,7 +1083,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 127), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F640B3T, .name = "Intel 28F640B3T", .devtypes = CFI_DEVICETYPE_X16, @@ -1114,7 +1096,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000, 8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I28F640C3B, .name = "Intel 28F640C3B", .devtypes = CFI_DEVICETYPE_X16, @@ -1127,7 +1109,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000, 127), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I82802AB, .name = "Intel 82802AB", .devtypes = CFI_DEVICETYPE_X8, @@ -1139,7 +1121,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_INTEL, + .mfr_id = CFI_MFR_INTEL, .dev_id = I82802AC, .name = "Intel 82802AC", .devtypes = CFI_DEVICETYPE_X8, @@ -1151,7 +1133,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29LV040C, .name = "Macronix MX29LV040C", .devtypes = CFI_DEVICETYPE_X8, @@ -1163,7 +1145,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29LV160T, .name = "MXIC MX29LV160T", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1178,7 +1160,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_NEC, + .mfr_id = CFI_MFR_NEC, .dev_id = UPD29F064115, .name = "NEC uPD29F064115", .devtypes = CFI_DEVICETYPE_X16, @@ -1192,7 +1174,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x2000,8), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29LV160B, .name = "MXIC MX29LV160B", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1207,7 +1189,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F040, .name = "Macronix MX29F040", .devtypes = CFI_DEVICETYPE_X8, @@ -1219,7 +1201,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F016, .name = "Macronix MX29F016", .devtypes = CFI_DEVICETYPE_X8, @@ -1231,7 +1213,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F004T, .name = "Macronix MX29F004T", .devtypes = CFI_DEVICETYPE_X8, @@ -1246,7 +1228,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F004B, .name = "Macronix MX29F004B", .devtypes = CFI_DEVICETYPE_X8, @@ -1261,7 +1243,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7), } }, { - .mfr_id = MANUFACTURER_MACRONIX, + .mfr_id = CFI_MFR_MACRONIX, .dev_id = MX29F002T, .name = "Macronix MX29F002T", .devtypes = CFI_DEVICETYPE_X8, @@ -1276,7 +1258,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1), } }, { - .mfr_id = MANUFACTURER_PMC, + .mfr_id = CFI_MFR_PMC, .dev_id = PM49FL002, .name = "PMC Pm49FL002", .devtypes = CFI_DEVICETYPE_X8, @@ -1288,7 +1270,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO( 0x01000, 64 ) } }, { - .mfr_id = MANUFACTURER_PMC, + .mfr_id = CFI_MFR_PMC, .dev_id = PM49FL004, .name = "PMC Pm49FL004", .devtypes = CFI_DEVICETYPE_X8, @@ -1300,7 +1282,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO( 0x01000, 128 ) } }, { - .mfr_id = MANUFACTURER_PMC, + .mfr_id = CFI_MFR_PMC, .dev_id = PM49FL008, .name = "PMC Pm49FL008", .devtypes = CFI_DEVICETYPE_X8, @@ -1312,7 +1294,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO( 0x01000, 256 ) } }, { - .mfr_id = MANUFACTURER_SHARP, + .mfr_id = CFI_MFR_SHARP, .dev_id = LH28F640BF, .name = "LH28F640BF", .devtypes = CFI_DEVICETYPE_X8, @@ -1324,7 +1306,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x40000,16), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF512, .name = "SST 39LF512", .devtypes = CFI_DEVICETYPE_X8, @@ -1336,7 +1318,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,16), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF010, .name = "SST 39LF010", .devtypes = CFI_DEVICETYPE_X8, @@ -1348,7 +1330,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,32), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST29EE020, .name = "SST 29EE020", .devtypes = CFI_DEVICETYPE_X8, @@ -1359,7 +1341,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = {ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST29LE020, .name = "SST 29LE020", .devtypes = CFI_DEVICETYPE_X8, @@ -1370,7 +1352,7 @@ static const struct amd_flash_info jedec_table[] = { .regions = {ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF020, .name = "SST 39LF020", .devtypes = CFI_DEVICETYPE_X8, @@ -1382,7 +1364,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39LF040, .name = "SST 39LF040", .devtypes = CFI_DEVICETYPE_X8, @@ -1394,7 +1376,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39SF010A, .name = "SST 39SF010A", .devtypes = CFI_DEVICETYPE_X8, @@ -1406,7 +1388,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,32), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39SF020A, .name = "SST 39SF020A", .devtypes = CFI_DEVICETYPE_X8, @@ -1418,7 +1400,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,64), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST39SF040, .name = "SST 39SF040", .devtypes = CFI_DEVICETYPE_X8, @@ -1430,7 +1412,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF040B, .name = "SST 49LF040B", .devtypes = CFI_DEVICETYPE_X8, @@ -1443,7 +1425,7 @@ static const struct amd_flash_info jedec_table[] = { } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF004B, .name = "SST 49LF004B", .devtypes = CFI_DEVICETYPE_X8, @@ -1455,7 +1437,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF008A, .name = "SST 49LF008A", .devtypes = CFI_DEVICETYPE_X8, @@ -1467,7 +1449,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,256), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF030A, .name = "SST 49LF030A", .devtypes = CFI_DEVICETYPE_X8, @@ -1479,7 +1461,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,96), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF040A, .name = "SST 49LF040A", .devtypes = CFI_DEVICETYPE_X8, @@ -1491,7 +1473,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,128), } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST49LF080A, .name = "SST 49LF080A", .devtypes = CFI_DEVICETYPE_X8, @@ -1503,7 +1485,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x01000,256), } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39LF160, .name = "SST 39LF160", .devtypes = CFI_DEVICETYPE_X16, @@ -1516,7 +1498,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39VF1601, .name = "SST 39VF1601", .devtypes = CFI_DEVICETYPE_X16, @@ -1529,7 +1511,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { - .mfr_id = MANUFACTURER_SST, /* should be CFI */ + .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39VF3201, .name = "SST 39VF3201", .devtypes = CFI_DEVICETYPE_X16, @@ -1544,7 +1526,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256) } }, { - .mfr_id = MANUFACTURER_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST36VF3203, .name = "SST 36VF3203", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1556,7 +1538,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,64), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29F800AB, .name = "ST M29F800AB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1571,7 +1553,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15), } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W800DT, .name = "ST M29W800DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1586,7 +1568,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W800DB, .name = "ST M29W800DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1601,7 +1583,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,15) } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29W400DT, .name = "ST M29W400DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1616,7 +1598,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,1) } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29W400DB, .name = "ST M29W400DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1631,7 +1613,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,7) } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W160DT, .name = "ST M29W160DT", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1646,7 +1628,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ + .mfr_id = CFI_MFR_ST, /* FIXME - CFI device? */ .dev_id = M29W160DB, .name = "ST M29W160DB", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1661,7 +1643,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M29W040B, .name = "ST M29W040B", .devtypes = CFI_DEVICETYPE_X8, @@ -1673,7 +1655,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FW040, .name = "ST M50FW040", .devtypes = CFI_DEVICETYPE_X8, @@ -1685,7 +1667,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,8), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FW080, .name = "ST M50FW080", .devtypes = CFI_DEVICETYPE_X8, @@ -1697,7 +1679,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FW016, .name = "ST M50FW016", .devtypes = CFI_DEVICETYPE_X8, @@ -1709,7 +1691,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,32), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50LPW080, .name = "ST M50LPW080", .devtypes = CFI_DEVICETYPE_X8, @@ -1721,7 +1703,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), }, }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FLW080A, .name = "ST M50FLW080A", .devtypes = CFI_DEVICETYPE_X8, @@ -1736,7 +1718,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,16), } }, { - .mfr_id = MANUFACTURER_ST, + .mfr_id = CFI_MFR_ST, .dev_id = M50FLW080B, .name = "ST M50FLW080B", .devtypes = CFI_DEVICETYPE_X8, @@ -1751,7 +1733,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,16), } }, { - .mfr_id = 0xff00 | MANUFACTURER_ST, + .mfr_id = 0xff00 | CFI_MFR_ST, .dev_id = 0xff00 | PSD4256G6V, .name = "ST PSD4256G6V", .devtypes = CFI_DEVICETYPE_X16, @@ -1763,7 +1745,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,16), } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVT160, .name = "Toshiba TC58FVT160", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1778,7 +1760,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x04000,1) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVB160, .name = "Toshiba TC58FVB160", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1793,7 +1775,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,31) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVB321, .name = "Toshiba TC58FVB321", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1806,7 +1788,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,63) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVT321, .name = "Toshiba TC58FVT321", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1819,7 +1801,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVB641, .name = "Toshiba TC58FVB641", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1832,7 +1814,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x10000,127) } }, { - .mfr_id = MANUFACTURER_TOSHIBA, + .mfr_id = CFI_MFR_TOSHIBA, .dev_id = TC58FVT641, .name = "Toshiba TC58FVT641", .devtypes = CFI_DEVICETYPE_X16|CFI_DEVICETYPE_X8, @@ -1845,7 +1827,7 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x02000,8) } }, { - .mfr_id = MANUFACTURER_WINBOND, + .mfr_id = CFI_MFR_WINBOND, .dev_id = W49V002A, .name = "Winbond W49V002A", .devtypes = CFI_DEVICETYPE_X8, @@ -1878,7 +1860,7 @@ static inline u32 jedec_read_mfr(struct map_info *map, uint32_t base, mask = (1 << (cfi->device_type * 8)) - 1; result = map_read(map, base + ofs); bank++; - } while ((result.x[0] & mask) == CONTINUATION_CODE); + } while ((result.x[0] & mask) == CFI_MFR_CONTINUATION); return result.x[0] & mask; } diff --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index 5716fc78ca8..574d9ee066f 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -516,16 +516,25 @@ struct cfi_fixup { void* param; }; -#define CFI_MFR_ANY 0xffff -#define CFI_ID_ANY 0xffff +#define CFI_MFR_ANY 0xFFFF +#define CFI_ID_ANY 0xFFFF +#define CFI_MFR_CONTINUATION 0x007F #define CFI_MFR_AMD 0x0001 #define CFI_MFR_ATMEL 0x001F +#define CFI_MFR_EON 0x001C +#define CFI_MFR_FUJITSU 0x0004 +#define CFI_MFR_HYUNDAI 0x00AD #define CFI_MFR_INTEL 0x0089 #define CFI_MFR_MACRONIX 0x00C2 +#define CFI_MFR_NEC 0x0010 +#define CFI_MFR_PMC 0x009D #define CFI_MFR_SAMSUNG 0x00EC +#define CFI_MFR_SHARP 0x00B0 #define CFI_MFR_SST 0x00BF #define CFI_MFR_ST 0x0020 /* STMicroelectronics */ +#define CFI_MFR_TOSHIBA 0x0098 +#define CFI_MFR_WINBOND 0x00DA void cfi_fixup(struct mtd_info *mtd, struct cfi_fixup* fixups); -- cgit v1.2.3-70-g09d2 From 7d84b6273c2a7805c042b398dcc01c98ad2ecf20 Mon Sep 17 00:00:00 2001 From: Artem Bityutskiy Date: Tue, 4 May 2010 16:15:11 +0300 Subject: mtd: jedec_probe: remove spaces before tabs Nothing very important, this just makes git am stop producing warnings. Signed-off-by: Artem Bityutskiy Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index 04fb45cacc3..b2e6f2f79de 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1331,7 +1331,7 @@ static const struct amd_flash_info jedec_table[] = { } }, { .mfr_id = CFI_MFR_SST, - .dev_id = SST29EE020, + .dev_id = SST29EE020, .name = "SST 29EE020", .devtypes = CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x5555_0x2AAA, @@ -1341,9 +1341,9 @@ static const struct amd_flash_info jedec_table[] = { .regions = {ERASEINFO(0x01000,64), } }, { - .mfr_id = CFI_MFR_SST, + .mfr_id = CFI_MFR_SST, .dev_id = SST29LE020, - .name = "SST 29LE020", + .name = "SST 29LE020", .devtypes = CFI_DEVICETYPE_X8, .uaddr = MTD_UADDR_0x5555_0x2AAA, .dev_size = SIZE_256KiB, @@ -1951,7 +1951,7 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 / p_cfi->device_type; p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 / p_cfi->device_type; - return 1; /* ok */ + return 1; /* ok */ } -- cgit v1.2.3-70-g09d2 From 6a88c47bd528cb0f82692986a3ca57b3695d9c60 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 28 Apr 2010 17:46:45 +0200 Subject: mtd: onenand: add support for chips with 4KiB page size This patch adds support for OneNAND chips that have 4KiB page size. Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 32 +++++++++++++++++++------------- include/linux/mtd/onenand.h | 4 ++++ 2 files changed, 23 insertions(+), 13 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 32f0ed33afe..1b26f50e159 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -397,7 +397,8 @@ 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_MLC(this) || ONENAND_IS_2PLANE(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_2PLANE(this) || + ONENAND_IS_4KB_PAGE(this)) /* It is always BufferRAM0 */ ONENAND_SET_BUFFERRAM0(this); else @@ -426,7 +427,7 @@ static int onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, size_t le case FLEXONENAND_CMD_RECOVER_LSB: case ONENAND_CMD_READ: case ONENAND_CMD_READOOB: - if (ONENAND_IS_MLC(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) /* It is always BufferRAM0 */ dataram = ONENAND_SET_BUFFERRAM0(this); else @@ -466,11 +467,11 @@ static inline int onenand_read_ecc(struct onenand_chip *this) { int ecc, i, result = 0; - if (!FLEXONENAND(this)) + if (!FLEXONENAND(this) && !ONENAND_IS_4KB_PAGE(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); + ecc = this->read_word(this->base + ONENAND_REG_ECC_STATUS + i*2); if (likely(!ecc)) continue; if (ecc & FLEXONENAND_UNCORRECTABLE_ERROR) @@ -1425,7 +1426,7 @@ static int onenand_read(struct mtd_info *mtd, loff_t from, size_t len, int ret; onenand_get_device(mtd, FL_READING); - ret = ONENAND_IS_MLC(this) ? + ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ? onenand_mlc_read_ops_nolock(mtd, from, &ops) : onenand_read_ops_nolock(mtd, from, &ops); onenand_release_device(mtd); @@ -1460,7 +1461,7 @@ static int onenand_read_oob(struct mtd_info *mtd, loff_t from, onenand_get_device(mtd, FL_READING); if (ops->datbuf) - ret = ONENAND_IS_MLC(this) ? + ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ? onenand_mlc_read_ops_nolock(mtd, from, ops) : onenand_read_ops_nolock(mtd, from, ops); else @@ -1926,7 +1927,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, * 2 PLANE, MLC, and Flex-OneNAND do not support * write-while-program feature. */ - if (!ONENAND_IS_2PLANE(this) && !first) { + if (!ONENAND_IS_2PLANE(this) && !ONENAND_IS_4KB_PAGE(this) && !first) { ONENAND_SET_PREV_BUFFERRAM(this); ret = this->wait(mtd, FL_WRITING); @@ -1957,7 +1958,7 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to, /* * 2 PLANE, MLC, and Flex-OneNAND wait here */ - if (ONENAND_IS_2PLANE(this)) { + if (ONENAND_IS_2PLANE(this) || ONENAND_IS_4KB_PAGE(this)) { ret = this->wait(mtd, FL_WRITING); /* In partial page write we don't update bufferram */ @@ -2084,7 +2085,7 @@ 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); - if (ONENAND_IS_MLC(this)) { + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) { /* Set main area of DataRAM to 0xff*/ memset(this->page_buf, 0xff, mtd->writesize); this->write_bufferram(mtd, ONENAND_DATARAM, @@ -3027,7 +3028,7 @@ 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_IS_MLC(this) ? + ret = ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this) ? onenand_mlc_read_ops_nolock(mtd, from, &ops) : onenand_read_ops_nolock(mtd, from, &ops); @@ -3372,7 +3373,10 @@ static void onenand_check_features(struct mtd_info *mtd) /* Lock scheme */ switch (density) { case ONENAND_DEVICE_DENSITY_4Gb: - this->options |= ONENAND_HAS_2PLANE; + if (ONENAND_IS_DDP(this)) + this->options |= ONENAND_HAS_2PLANE; + else + this->options |= ONENAND_HAS_4KB_PAGE; case ONENAND_DEVICE_DENSITY_2Gb: /* 2Gb DDP does not have 2 plane */ @@ -3393,7 +3397,7 @@ static void onenand_check_features(struct mtd_info *mtd) break; } - if (ONENAND_IS_MLC(this)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) this->options &= ~ONENAND_HAS_2PLANE; if (FLEXONENAND(this)) { @@ -3407,6 +3411,8 @@ static void onenand_check_features(struct mtd_info *mtd) printk(KERN_DEBUG "Chip support all block unlock\n"); if (this->options & ONENAND_HAS_2PLANE) printk(KERN_DEBUG "Chip has 2 plane\n"); + if (this->options & ONENAND_HAS_4KB_PAGE) + printk(KERN_DEBUG "Chip has 4KiB pagesize\n"); } /** @@ -3799,7 +3805,7 @@ static int onenand_probe(struct mtd_info *mtd) /* 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)) + if (ONENAND_IS_MLC(this) || ONENAND_IS_4KB_PAGE(this)) mtd->writesize <<= 1; mtd->oobsize = mtd->writesize >> 5; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 5509eb06b32..c9a3c3596b6 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -175,10 +175,14 @@ struct onenand_chip { #define ONENAND_HAS_CONT_LOCK (0x0001) #define ONENAND_HAS_UNLOCK_ALL (0x0002) #define ONENAND_HAS_2PLANE (0x0004) +#define ONENAND_HAS_4KB_PAGE (0x0008) #define ONENAND_SKIP_UNLOCK_CHECK (0x0100) #define ONENAND_PAGEBUF_ALLOC (0x1000) #define ONENAND_OOBBUF_ALLOC (0x2000) +#define ONENAND_IS_4KB_PAGE(this) \ + (this->options & ONENAND_HAS_4KB_PAGE) + /* * OneNAND Flash Manufacturer ID Codes */ -- cgit v1.2.3-70-g09d2 From 4a8ce0b030716b95004a4ace969953bc3ad7d2fe Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 28 Apr 2010 17:46:46 +0200 Subject: mtd: onenand: allocate verify buffer in the core This patch extends OneNAND core code with support for OneNAND verify write check. This is done by allocating the buffer for verify read directly from the core code. Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 13 ++++++++++++- include/linux/mtd/onenand.h | 3 +++ 2 files changed, 15 insertions(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 1b26f50e159..045811f2149 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -3932,6 +3932,13 @@ int onenand_scan(struct mtd_info *mtd, int maxchips) __func__); return -ENOMEM; } +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + this->verify_buf = kzalloc(mtd->writesize, GFP_KERNEL); + if (!this->verify_buf) { + kfree(this->page_buf); + return -ENOMEM; + } +#endif this->options |= ONENAND_PAGEBUF_ALLOC; } if (!this->oob_buf) { @@ -4059,8 +4066,12 @@ void onenand_release(struct mtd_info *mtd) kfree(this->bbm); } /* Buffers allocated by onenand_scan */ - if (this->options & ONENAND_PAGEBUF_ALLOC) + if (this->options & ONENAND_PAGEBUF_ALLOC) { kfree(this->page_buf); +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + kfree(this->verify_buf); +#endif + } if (this->options & ONENAND_OOBBUF_ALLOC) kfree(this->oob_buf); kfree(mtd->eraseregions); diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index c9a3c3596b6..9b43268224a 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -125,6 +125,9 @@ struct onenand_chip { flstate_t state; unsigned char *page_buf; unsigned char *oob_buf; +#ifdef CONFIG_MTD_ONENAND_VERIFY_WRITE + unsigned char *verify_buf; +#endif int subpagesize; struct nand_ecclayout *ecclayout; -- cgit v1.2.3-70-g09d2 From 3328dc315914aa6db486da2ceb021b6f0b36b877 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 28 Apr 2010 17:46:47 +0200 Subject: mtd: onenand: add new callback for bufferram read This patch adds a new callback for the underlying drivers, which is called instead of accessing the buffer ram directly. This callback will be used by Samsung OneNAND driver to implement DMA transfers on S5PC110 SoC. Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 6 ++---- include/linux/mtd/onenand.h | 2 ++ 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 045811f2149..9827ab779c0 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -1635,7 +1635,6 @@ static int onenand_verify_oob(struct mtd_info *mtd, const u_char *buf, loff_t to static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, size_t len) { struct onenand_chip *this = mtd->priv; - void __iomem *dataram; int ret = 0; int thislen, column; @@ -1655,10 +1654,9 @@ static int onenand_verify(struct mtd_info *mtd, const u_char *buf, loff_t addr, onenand_update_bufferram(mtd, addr, 1); - dataram = this->base + ONENAND_DATARAM; - dataram += onenand_bufferram_offset(mtd, ONENAND_DATARAM); + this->read_bufferram(mtd, ONENAND_DATARAM, this->verify_buf, 0, mtd->writesize); - if (memcmp(buf, dataram + column, thislen)) + if (memcmp(buf, this->verify_buf, thislen)) return -EBADMSG; len -= thislen; diff --git a/include/linux/mtd/onenand.h b/include/linux/mtd/onenand.h index 9b43268224a..c26ff86ad08 100644 --- a/include/linux/mtd/onenand.h +++ b/include/linux/mtd/onenand.h @@ -212,6 +212,8 @@ struct mtd_partition; struct onenand_platform_data { void (*mmcontrol)(struct mtd_info *mtd, int sync_read); + int (*read_bufferram)(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, size_t count); struct mtd_partition *parts; unsigned int nr_parts; }; -- cgit v1.2.3-70-g09d2 From c37cb56fb15d0f8e4180b19eed20f52fe8641b54 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 28 Apr 2010 17:46:48 +0200 Subject: mtd: onenand: add workaround for SYNC_WRITE mode Some chips fails to identify properly when SYNC_WRITE mode is enabled (the example is OneNAND on S5PC110 SoC). This patch adds a workaround for such chips. Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: David Woodhouse --- drivers/mtd/onenand/onenand_base.c | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/onenand/onenand_base.c b/drivers/mtd/onenand/onenand_base.c index 9827ab779c0..26caf2590da 100644 --- a/drivers/mtd/onenand/onenand_base.c +++ b/drivers/mtd/onenand/onenand_base.c @@ -3763,6 +3763,12 @@ static int onenand_probe(struct mtd_info *mtd) /* Restore system configuration 1 */ this->write_word(syscfg, this->base + ONENAND_REG_SYS_CFG1); + /* Workaround */ + if (syscfg & ONENAND_SYS_CFG1_SYNC_WRITE) { + bram_maf_id = this->read_word(this->base + ONENAND_REG_MANUFACTURER_ID); + bram_dev_id = this->read_word(this->base + ONENAND_REG_DEVICE_ID); + } + /* Check manufacturer ID */ if (onenand_check_maf(bram_maf_id)) return -ENXIO; @@ -3782,6 +3788,9 @@ static int onenand_probe(struct mtd_info *mtd) this->device_id = dev_id; this->version_id = ver_id; + /* Check OneNAND features */ + onenand_check_features(mtd); + density = onenand_get_density(dev_id); if (FLEXONENAND(this)) { this->dies = ONENAND_IS_DDP(this) ? 2 : 1; @@ -3833,9 +3842,6 @@ static int onenand_probe(struct mtd_info *mtd) else mtd->size = this->chipsize; - /* Check OneNAND features */ - onenand_check_features(mtd); - /* * We emulate the 4KiB page and 256KiB erase block size * But oobsize is still 64 bytes. -- cgit v1.2.3-70-g09d2 From 46f3e88bd9da010e76a9049d55cf9013560b5903 Mon Sep 17 00:00:00 2001 From: Kyungmin Park Date: Wed, 28 Apr 2010 17:46:49 +0200 Subject: mtd: add Samsung SoC OneNAND driver This patch adds a driver for OneNAND controller on Samsung SoCs. Following SoCs are supported: S3C6400, S3C6410, S5PC100 and S5PC110. Signed-off-by: Kyungmin Park Signed-off-by: Marek Szyprowski Signed-off-by: David Woodhouse --- drivers/mtd/onenand/Kconfig | 7 + drivers/mtd/onenand/Makefile | 1 + drivers/mtd/onenand/samsung.c | 1071 +++++++++++++++++++++++++++++++++++++++++ 3 files changed, 1079 insertions(+) create mode 100644 drivers/mtd/onenand/samsung.c (limited to 'drivers') diff --git a/drivers/mtd/onenand/Kconfig b/drivers/mtd/onenand/Kconfig index 3a9f1578460..9a49d68ba5f 100644 --- a/drivers/mtd/onenand/Kconfig +++ b/drivers/mtd/onenand/Kconfig @@ -30,6 +30,13 @@ config MTD_ONENAND_OMAP2 Support for a OneNAND flash device connected to an OMAP2/OMAP3 CPU via the GPMC memory controller. +config MTD_ONENAND_SAMSUNG + tristate "OneNAND on Samsung SOC controller support" + depends on MTD_ONENAND && (ARCH_S3C64XX || ARCH_S5PC100 || ARCH_S5PV210) + help + Support for a OneNAND flash device connected to an Samsung SOC + S3C64XX/S5PC1XX controller. + config MTD_ONENAND_OTP bool "OneNAND OTP Support" select HAVE_MTD_OTP diff --git a/drivers/mtd/onenand/Makefile b/drivers/mtd/onenand/Makefile index 64b6cc61a52..2b7884c7577 100644 --- a/drivers/mtd/onenand/Makefile +++ b/drivers/mtd/onenand/Makefile @@ -8,6 +8,7 @@ obj-$(CONFIG_MTD_ONENAND) += onenand.o # Board specific. obj-$(CONFIG_MTD_ONENAND_GENERIC) += generic.o obj-$(CONFIG_MTD_ONENAND_OMAP2) += omap2.o +obj-$(CONFIG_MTD_ONENAND_SAMSUNG) += samsung.o # Simulator obj-$(CONFIG_MTD_ONENAND_SIM) += onenand_sim.o diff --git a/drivers/mtd/onenand/samsung.c b/drivers/mtd/onenand/samsung.c new file mode 100644 index 00000000000..2750317cb58 --- /dev/null +++ b/drivers/mtd/onenand/samsung.c @@ -0,0 +1,1071 @@ +/* + * Samsung S3C64XX/S5PC1XX OneNAND driver + * + * Copyright © 2008-2010 Samsung Electronics + * Kyungmin Park + * Marek Szyprowski + * + * 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. + * + * Implementation: + * S3C64XX and S5PC100: emulate the pseudo BufferRAM + * S5PC110: use DMA + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include + +enum soc_type { + TYPE_S3C6400, + TYPE_S3C6410, + TYPE_S5PC100, + TYPE_S5PC110, +}; + +#define ONENAND_ERASE_STATUS 0x00 +#define ONENAND_MULTI_ERASE_SET 0x01 +#define ONENAND_ERASE_START 0x03 +#define ONENAND_UNLOCK_START 0x08 +#define ONENAND_UNLOCK_END 0x09 +#define ONENAND_LOCK_START 0x0A +#define ONENAND_LOCK_END 0x0B +#define ONENAND_LOCK_TIGHT_START 0x0C +#define ONENAND_LOCK_TIGHT_END 0x0D +#define ONENAND_UNLOCK_ALL 0x0E +#define ONENAND_OTP_ACCESS 0x12 +#define ONENAND_SPARE_ACCESS_ONLY 0x13 +#define ONENAND_MAIN_ACCESS_ONLY 0x14 +#define ONENAND_ERASE_VERIFY 0x15 +#define ONENAND_MAIN_SPARE_ACCESS 0x16 +#define ONENAND_PIPELINE_READ 0x4000 + +#define MAP_00 (0x0) +#define MAP_01 (0x1) +#define MAP_10 (0x2) +#define MAP_11 (0x3) + +#define S3C64XX_CMD_MAP_SHIFT 24 +#define S5PC1XX_CMD_MAP_SHIFT 26 + +#define S3C6400_FBA_SHIFT 10 +#define S3C6400_FPA_SHIFT 4 +#define S3C6400_FSA_SHIFT 2 + +#define S3C6410_FBA_SHIFT 12 +#define S3C6410_FPA_SHIFT 6 +#define S3C6410_FSA_SHIFT 4 + +#define S5PC100_FBA_SHIFT 13 +#define S5PC100_FPA_SHIFT 7 +#define S5PC100_FSA_SHIFT 5 + +/* S5PC110 specific definitions */ +#define S5PC110_DMA_SRC_ADDR 0x400 +#define S5PC110_DMA_SRC_CFG 0x404 +#define S5PC110_DMA_DST_ADDR 0x408 +#define S5PC110_DMA_DST_CFG 0x40C +#define S5PC110_DMA_TRANS_SIZE 0x414 +#define S5PC110_DMA_TRANS_CMD 0x418 +#define S5PC110_DMA_TRANS_STATUS 0x41C +#define S5PC110_DMA_TRANS_DIR 0x420 + +#define S5PC110_DMA_CFG_SINGLE (0x0 << 16) +#define S5PC110_DMA_CFG_4BURST (0x2 << 16) +#define S5PC110_DMA_CFG_8BURST (0x3 << 16) +#define S5PC110_DMA_CFG_16BURST (0x4 << 16) + +#define S5PC110_DMA_CFG_INC (0x0 << 8) +#define S5PC110_DMA_CFG_CNT (0x1 << 8) + +#define S5PC110_DMA_CFG_8BIT (0x0 << 0) +#define S5PC110_DMA_CFG_16BIT (0x1 << 0) +#define S5PC110_DMA_CFG_32BIT (0x2 << 0) + +#define S5PC110_DMA_SRC_CFG_READ (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_16BIT) +#define S5PC110_DMA_DST_CFG_READ (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_32BIT) +#define S5PC110_DMA_SRC_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_32BIT) +#define S5PC110_DMA_DST_CFG_WRITE (S5PC110_DMA_CFG_16BURST | \ + S5PC110_DMA_CFG_INC | \ + S5PC110_DMA_CFG_16BIT) + +#define S5PC110_DMA_TRANS_CMD_TDC (0x1 << 18) +#define S5PC110_DMA_TRANS_CMD_TEC (0x1 << 16) +#define S5PC110_DMA_TRANS_CMD_TR (0x1 << 0) + +#define S5PC110_DMA_TRANS_STATUS_TD (0x1 << 18) +#define S5PC110_DMA_TRANS_STATUS_TB (0x1 << 17) +#define S5PC110_DMA_TRANS_STATUS_TE (0x1 << 16) + +#define S5PC110_DMA_DIR_READ 0x0 +#define S5PC110_DMA_DIR_WRITE 0x1 + +struct s3c_onenand { + struct mtd_info *mtd; + struct platform_device *pdev; + enum soc_type type; + void __iomem *base; + struct resource *base_res; + void __iomem *ahb_addr; + struct resource *ahb_res; + int bootram_command; + void __iomem *page_buf; + void __iomem *oob_buf; + unsigned int (*mem_addr)(int fba, int fpa, int fsa); + unsigned int (*cmd_map)(unsigned int type, unsigned int val); + void __iomem *dma_addr; + struct resource *dma_res; + unsigned long phys_base; +#ifdef CONFIG_MTD_PARTITIONS + struct mtd_partition *parts; +#endif +}; + +#define CMD_MAP_00(dev, addr) (dev->cmd_map(MAP_00, ((addr) << 1))) +#define CMD_MAP_01(dev, mem_addr) (dev->cmd_map(MAP_01, (mem_addr))) +#define CMD_MAP_10(dev, mem_addr) (dev->cmd_map(MAP_10, (mem_addr))) +#define CMD_MAP_11(dev, addr) (dev->cmd_map(MAP_11, ((addr) << 2))) + +static struct s3c_onenand *onenand; + +#ifdef CONFIG_MTD_PARTITIONS +static const char *part_probes[] = { "cmdlinepart", NULL, }; +#endif + +static inline int s3c_read_reg(int offset) +{ + return readl(onenand->base + offset); +} + +static inline void s3c_write_reg(int value, int offset) +{ + writel(value, onenand->base + offset); +} + +static inline int s3c_read_cmd(unsigned int cmd) +{ + return readl(onenand->ahb_addr + cmd); +} + +static inline void s3c_write_cmd(int value, unsigned int cmd) +{ + writel(value, onenand->ahb_addr + cmd); +} + +#ifdef SAMSUNG_DEBUG +static void s3c_dump_reg(void) +{ + int i; + + for (i = 0; i < 0x400; i += 0x40) { + printk(KERN_INFO "0x%08X: 0x%08x 0x%08x 0x%08x 0x%08x\n", + (unsigned int) onenand->base + i, + s3c_read_reg(i), s3c_read_reg(i + 0x10), + s3c_read_reg(i + 0x20), s3c_read_reg(i + 0x30)); + } +} +#endif + +static unsigned int s3c64xx_cmd_map(unsigned type, unsigned val) +{ + return (type << S3C64XX_CMD_MAP_SHIFT) | val; +} + +static unsigned int s5pc1xx_cmd_map(unsigned type, unsigned val) +{ + return (type << S5PC1XX_CMD_MAP_SHIFT) | val; +} + +static unsigned int s3c6400_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S3C6400_FBA_SHIFT) | (fpa << S3C6400_FPA_SHIFT) | + (fsa << S3C6400_FSA_SHIFT); +} + +static unsigned int s3c6410_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S3C6410_FBA_SHIFT) | (fpa << S3C6410_FPA_SHIFT) | + (fsa << S3C6410_FSA_SHIFT); +} + +static unsigned int s5pc100_mem_addr(int fba, int fpa, int fsa) +{ + return (fba << S5PC100_FBA_SHIFT) | (fpa << S5PC100_FPA_SHIFT) | + (fsa << S5PC100_FSA_SHIFT); +} + +static void s3c_onenand_reset(void) +{ + unsigned long timeout = 0x10000; + int stat; + + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + while (1 && timeout--) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & RST_CMP) + break; + } + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* Clear interrupt */ + s3c_write_reg(0x0, INT_ERR_ACK_OFFSET); + /* Clear the ECC status */ + s3c_write_reg(0x0, ECC_ERR_STAT_OFFSET); +} + +static unsigned short s3c_onenand_readw(void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + struct device *dev = &onenand->pdev->dev; + int reg = addr - this->base; + int word_addr = reg >> 1; + int value; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_MANUFACTURER_ID: + return s3c_read_reg(MANUFACT_ID_OFFSET); + case ONENAND_REG_DEVICE_ID: + return s3c_read_reg(DEVICE_ID_OFFSET); + case ONENAND_REG_VERSION_ID: + return s3c_read_reg(FLASH_VER_ID_OFFSET); + case ONENAND_REG_DATA_BUFFER_SIZE: + return s3c_read_reg(DATA_BUF_SIZE_OFFSET); + case ONENAND_REG_TECHNOLOGY: + return s3c_read_reg(TECH_OFFSET); + case ONENAND_REG_SYS_CFG1: + return s3c_read_reg(MEM_CFG_OFFSET); + + /* Used at unlock all status */ + case ONENAND_REG_CTRL_STATUS: + return 0; + + case ONENAND_REG_WP_STATUS: + return ONENAND_WP_US; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int) addr < ONENAND_DATARAM && onenand->bootram_command) { + if (word_addr == 0) + return s3c_read_reg(MANUFACT_ID_OFFSET); + if (word_addr == 1) + return s3c_read_reg(DEVICE_ID_OFFSET); + if (word_addr == 2) + return s3c_read_reg(FLASH_VER_ID_OFFSET); + } + + value = s3c_read_cmd(CMD_MAP_11(onenand, word_addr)) & 0xffff; + dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, + word_addr, value); + return value; +} + +static void s3c_onenand_writew(unsigned short value, void __iomem *addr) +{ + struct onenand_chip *this = onenand->mtd->priv; + struct device *dev = &onenand->pdev->dev; + unsigned int reg = addr - this->base; + unsigned int word_addr = reg >> 1; + + /* It's used for probing time */ + switch (reg) { + case ONENAND_REG_SYS_CFG1: + s3c_write_reg(value, MEM_CFG_OFFSET); + return; + + case ONENAND_REG_START_ADDRESS1: + case ONENAND_REG_START_ADDRESS2: + return; + + /* Lock/lock-tight/unlock/unlock_all */ + case ONENAND_REG_START_BLOCK_ADDRESS: + return; + + default: + break; + } + + /* BootRAM access control */ + if ((unsigned int)addr < ONENAND_DATARAM) { + if (value == ONENAND_CMD_READID) { + onenand->bootram_command = 1; + return; + } + if (value == ONENAND_CMD_RESET) { + s3c_write_reg(ONENAND_MEM_RESET_COLD, MEM_RESET_OFFSET); + onenand->bootram_command = 0; + return; + } + } + + dev_info(dev, "%s: Illegal access at reg 0x%x, value 0x%x\n", __func__, + word_addr, value); + + s3c_write_cmd(value, CMD_MAP_11(onenand, word_addr)); +} + +static int s3c_onenand_wait(struct mtd_info *mtd, int state) +{ + struct device *dev = &onenand->pdev->dev; + unsigned int flags = INT_ACT; + unsigned int stat, ecc; + unsigned long timeout; + + switch (state) { + case FL_READING: + flags |= BLK_RW_CMP | LOAD_CMP; + break; + case FL_WRITING: + flags |= BLK_RW_CMP | PGM_CMP; + break; + case FL_ERASING: + flags |= BLK_RW_CMP | ERS_CMP; + break; + case FL_LOCKING: + flags |= BLK_RW_CMP; + break; + default: + break; + } + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + + if (state != FL_READING) + cond_resched(); + } + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + /* + * In the Spec. it checks the controller status first + * However if you get the correct information in case of + * power off recovery (POR) test, it should read ECC status first + */ + if (stat & LOAD_CMP) { + ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + dev_info(dev, "%s: ECC error = 0x%04x\n", __func__, + ecc); + mtd->ecc_stats.failed++; + return -EBADMSG; + } + } + + if (stat & (LOCKED_BLK | ERS_FAIL | PGM_FAIL | LD_FAIL_ECC_ERR)) { + dev_info(dev, "%s: controller error = 0x%04x\n", __func__, + stat); + if (stat & LOCKED_BLK) + dev_info(dev, "%s: it's locked error = 0x%04x\n", + __func__, stat); + + return -EIO; + } + + return 0; +} + +static int s3c_onenand_command(struct mtd_info *mtd, int cmd, loff_t addr, + size_t len) +{ + struct onenand_chip *this = mtd->priv; + unsigned int *m, *s; + int fba, fpa, fsa = 0; + unsigned int mem_addr, cmd_map_01, cmd_map_10; + int i, mcount, scount; + int index; + + fba = (int) (addr >> this->erase_shift); + fpa = (int) (addr >> this->page_shift); + fpa &= this->page_mask; + + mem_addr = onenand->mem_addr(fba, fpa, fsa); + cmd_map_01 = CMD_MAP_01(onenand, mem_addr); + cmd_map_10 = CMD_MAP_10(onenand, mem_addr); + + switch (cmd) { + case ONENAND_CMD_READ: + case ONENAND_CMD_READOOB: + case ONENAND_CMD_BUFFERRAM: + ONENAND_SET_NEXT_BUFFERRAM(this); + default: + break; + } + + index = ONENAND_CURRENT_BUFFERRAM(this); + + /* + * Emulate Two BufferRAMs and access with 4 bytes pointer + */ + m = (unsigned int *) onenand->page_buf; + s = (unsigned int *) onenand->oob_buf; + + if (index) { + m += (this->writesize >> 2); + s += (mtd->oobsize >> 2); + } + + mcount = mtd->writesize >> 2; + scount = mtd->oobsize >> 2; + + switch (cmd) { + case ONENAND_CMD_READ: + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(cmd_map_01); + return 0; + + case ONENAND_CMD_READOOB: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + /* Main */ + for (i = 0; i < mcount; i++) + *m++ = s3c_read_cmd(cmd_map_01); + + /* Spare */ + for (i = 0; i < scount; i++) + *s++ = s3c_read_cmd(cmd_map_01); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + return 0; + + case ONENAND_CMD_PROG: + /* Main */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(*m++, cmd_map_01); + return 0; + + case ONENAND_CMD_PROGOOB: + s3c_write_reg(TSRF, TRANS_SPARE_OFFSET); + + /* Main - dummy write */ + for (i = 0; i < mcount; i++) + s3c_write_cmd(0xffffffff, cmd_map_01); + + /* Spare */ + for (i = 0; i < scount; i++) + s3c_write_cmd(*s++, cmd_map_01); + + s3c_write_reg(0, TRANS_SPARE_OFFSET); + return 0; + + case ONENAND_CMD_UNLOCK_ALL: + s3c_write_cmd(ONENAND_UNLOCK_ALL, cmd_map_10); + return 0; + + case ONENAND_CMD_ERASE: + s3c_write_cmd(ONENAND_ERASE_START, cmd_map_10); + return 0; + + default: + break; + } + + return 0; +} + +static unsigned char *s3c_get_bufferram(struct mtd_info *mtd, int area) +{ + struct onenand_chip *this = mtd->priv; + int index = ONENAND_CURRENT_BUFFERRAM(this); + unsigned char *p; + + if (area == ONENAND_DATARAM) { + p = (unsigned char *) onenand->page_buf; + if (index == 1) + p += this->writesize; + } else { + p = (unsigned char *) onenand->oob_buf; + if (index == 1) + p += mtd->oobsize; + } + + return p; +} + +static int onenand_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(buffer, p + offset, count); + return 0; +} + +static int onenand_write_bufferram(struct mtd_info *mtd, int area, + const unsigned char *buffer, int offset, + size_t count) +{ + unsigned char *p; + + p = s3c_get_bufferram(mtd, area); + memcpy(p + offset, buffer, count); + return 0; +} + +static int s5pc110_dma_ops(void *dst, void *src, size_t count, int direction) +{ + void __iomem *base = onenand->dma_addr; + int status; + + writel(src, base + S5PC110_DMA_SRC_ADDR); + writel(dst, base + S5PC110_DMA_DST_ADDR); + + if (direction == S5PC110_DMA_DIR_READ) { + writel(S5PC110_DMA_SRC_CFG_READ, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_READ, base + S5PC110_DMA_DST_CFG); + } else { + writel(S5PC110_DMA_SRC_CFG_WRITE, base + S5PC110_DMA_SRC_CFG); + writel(S5PC110_DMA_DST_CFG_WRITE, base + S5PC110_DMA_DST_CFG); + } + + writel(count, base + S5PC110_DMA_TRANS_SIZE); + writel(direction, base + S5PC110_DMA_TRANS_DIR); + + writel(S5PC110_DMA_TRANS_CMD_TR, base + S5PC110_DMA_TRANS_CMD); + + do { + status = readl(base + S5PC110_DMA_TRANS_STATUS); + } while (!(status & S5PC110_DMA_TRANS_STATUS_TD)); + + if (status & S5PC110_DMA_TRANS_STATUS_TE) { + writel(S5PC110_DMA_TRANS_CMD_TEC, base + S5PC110_DMA_TRANS_CMD); + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); + return -EIO; + } + + writel(S5PC110_DMA_TRANS_CMD_TDC, base + S5PC110_DMA_TRANS_CMD); + + return 0; +} + +static int s5pc110_read_bufferram(struct mtd_info *mtd, int area, + unsigned char *buffer, int offset, size_t count) +{ + struct onenand_chip *this = mtd->priv; + void __iomem *bufferram; + void __iomem *p; + void *buf = (void *) buffer; + dma_addr_t dma_src, dma_dst; + int err; + + p = bufferram = this->base + area; + if (ONENAND_CURRENT_BUFFERRAM(this)) { + if (area == ONENAND_DATARAM) + p += this->writesize; + else + p += mtd->oobsize; + } + + if (offset & 3 || (size_t) buf & 3 || + !onenand->dma_addr || count != mtd->writesize) + goto normal; + + /* Handle vmalloc address */ + if (buf >= high_memory) { + struct page *page; + + if (((size_t) buf & PAGE_MASK) != + ((size_t) (buf + count - 1) & PAGE_MASK)) + goto normal; + page = vmalloc_to_page(buf); + if (!page) + goto normal; + buf = page_address(page) + ((size_t) buf & ~PAGE_MASK); + } + + /* DMA routine */ + dma_src = onenand->phys_base + (p - this->base); + dma_dst = dma_map_single(&onenand->pdev->dev, + buf, count, DMA_FROM_DEVICE); + if (dma_mapping_error(&onenand->pdev->dev, dma_dst)) { + dev_err(&onenand->pdev->dev, + "Couldn't map a %d byte buffer for DMA\n", count); + goto normal; + } + err = s5pc110_dma_ops((void *) dma_dst, (void *) dma_src, + count, S5PC110_DMA_DIR_READ); + dma_unmap_single(&onenand->pdev->dev, dma_dst, count, DMA_FROM_DEVICE); + + if (!err) + return 0; + +normal: + if (count != mtd->writesize) { + /* Copy the bufferram to memory to prevent unaligned access */ + memcpy(this->page_buf, bufferram, mtd->writesize); + p = this->page_buf + offset; + } + + memcpy(buffer, p, count); + + return 0; +} + +static int s3c_onenand_bbt_wait(struct mtd_info *mtd, int state) +{ + unsigned int flags = INT_ACT | LOAD_CMP; + unsigned int stat; + unsigned long timeout; + + /* The 20 msec is enough */ + timeout = jiffies + msecs_to_jiffies(20); + while (time_before(jiffies, timeout)) { + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + if (stat & flags) + break; + } + /* To get correct interrupt status in timeout case */ + stat = s3c_read_reg(INT_ERR_STAT_OFFSET); + s3c_write_reg(stat, INT_ERR_ACK_OFFSET); + + if (stat & LD_FAIL_ECC_ERR) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + + if (stat & LOAD_CMP) { + int ecc = s3c_read_reg(ECC_ERR_STAT_OFFSET); + if (ecc & ONENAND_ECC_4BIT_UNCORRECTABLE) { + s3c_onenand_reset(); + return ONENAND_BBT_READ_ERROR; + } + } + + return 0; +} + +static void s3c_onenand_check_lock_status(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + struct device *dev = &onenand->pdev->dev; + unsigned int block, end; + int tmp; + + end = this->chipsize >> this->erase_shift; + + for (block = 0; block < end; block++) { + unsigned int mem_addr = onenand->mem_addr(block, 0, 0); + tmp = s3c_read_cmd(CMD_MAP_01(onenand, mem_addr)); + + if (s3c_read_reg(INT_ERR_STAT_OFFSET) & LOCKED_BLK) { + dev_err(dev, "block %d is write-protected!\n", block); + s3c_write_reg(LOCKED_BLK, INT_ERR_ACK_OFFSET); + } + } +} + +static void s3c_onenand_do_lock_cmd(struct mtd_info *mtd, loff_t ofs, + size_t len, int cmd) +{ + struct onenand_chip *this = mtd->priv; + int start, end, start_mem_addr, end_mem_addr; + + start = ofs >> this->erase_shift; + start_mem_addr = onenand->mem_addr(start, 0, 0); + end = start + (len >> this->erase_shift) - 1; + end_mem_addr = onenand->mem_addr(end, 0, 0); + + if (cmd == ONENAND_CMD_LOCK) { + s3c_write_cmd(ONENAND_LOCK_START, CMD_MAP_10(onenand, + start_mem_addr)); + s3c_write_cmd(ONENAND_LOCK_END, CMD_MAP_10(onenand, + end_mem_addr)); + } else { + s3c_write_cmd(ONENAND_UNLOCK_START, CMD_MAP_10(onenand, + start_mem_addr)); + s3c_write_cmd(ONENAND_UNLOCK_END, CMD_MAP_10(onenand, + end_mem_addr)); + } + + this->wait(mtd, FL_LOCKING); +} + +static void s3c_unlock_all(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + loff_t ofs = 0; + size_t len = this->chipsize; + + if (this->options & ONENAND_HAS_UNLOCK_ALL) { + /* Write unlock command */ + this->command(mtd, ONENAND_CMD_UNLOCK_ALL, 0, 0); + + /* No need to check return value */ + this->wait(mtd, FL_LOCKING); + + /* Workaround for all block unlock in DDP */ + if (!ONENAND_IS_DDP(this)) { + s3c_onenand_check_lock_status(mtd); + return; + } + + /* All blocks on another chip */ + ofs = this->chipsize >> 1; + len = this->chipsize >> 1; + } + + s3c_onenand_do_lock_cmd(mtd, ofs, len, ONENAND_CMD_UNLOCK); + + s3c_onenand_check_lock_status(mtd); +} + +static void s3c_onenand_setup(struct mtd_info *mtd) +{ + struct onenand_chip *this = mtd->priv; + + onenand->mtd = mtd; + + if (onenand->type == TYPE_S3C6400) { + onenand->mem_addr = s3c6400_mem_addr; + onenand->cmd_map = s3c64xx_cmd_map; + } else if (onenand->type == TYPE_S3C6410) { + onenand->mem_addr = s3c6410_mem_addr; + onenand->cmd_map = s3c64xx_cmd_map; + } else if (onenand->type == TYPE_S5PC100) { + onenand->mem_addr = s5pc100_mem_addr; + onenand->cmd_map = s5pc1xx_cmd_map; + } else if (onenand->type == TYPE_S5PC110) { + /* Use generic onenand functions */ + onenand->cmd_map = s5pc1xx_cmd_map; + this->read_bufferram = s5pc110_read_bufferram; + return; + } else { + BUG(); + } + + this->read_word = s3c_onenand_readw; + this->write_word = s3c_onenand_writew; + + this->wait = s3c_onenand_wait; + this->bbt_wait = s3c_onenand_bbt_wait; + this->unlock_all = s3c_unlock_all; + this->command = s3c_onenand_command; + + this->read_bufferram = onenand_read_bufferram; + this->write_bufferram = onenand_write_bufferram; +} + +static int s3c_onenand_probe(struct platform_device *pdev) +{ + struct onenand_platform_data *pdata; + struct onenand_chip *this; + struct mtd_info *mtd; + struct resource *r; + int size, err; + unsigned long onenand_ctrl_cfg = 0; + + pdata = pdev->dev.platform_data; + /* No need to check pdata. the platform data is optional */ + + size = sizeof(struct mtd_info) + sizeof(struct onenand_chip); + mtd = kzalloc(size, GFP_KERNEL); + if (!mtd) { + dev_err(&pdev->dev, "failed to allocate memory\n"); + return -ENOMEM; + } + + onenand = kzalloc(sizeof(struct s3c_onenand), GFP_KERNEL); + if (!onenand) { + err = -ENOMEM; + goto onenand_fail; + } + + this = (struct onenand_chip *) &mtd[1]; + mtd->priv = this; + mtd->dev.parent = &pdev->dev; + mtd->owner = THIS_MODULE; + onenand->pdev = pdev; + onenand->type = platform_get_device_id(pdev)->driver_data; + + s3c_onenand_setup(mtd); + + r = platform_get_resource(pdev, IORESOURCE_MEM, 0); + if (!r) { + dev_err(&pdev->dev, "no memory resource defined\n"); + return -ENOENT; + goto ahb_resource_failed; + } + + onenand->base_res = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!onenand->base_res) { + dev_err(&pdev->dev, "failed to request memory resource\n"); + err = -EBUSY; + goto resource_failed; + } + + onenand->base = ioremap(r->start, resource_size(r)); + if (!onenand->base) { + dev_err(&pdev->dev, "failed to map memory resource\n"); + err = -EFAULT; + goto ioremap_failed; + } + /* Set onenand_chip also */ + this->base = onenand->base; + + /* Use runtime badblock check */ + this->options |= ONENAND_SKIP_UNLOCK_CHECK; + + if (onenand->type != TYPE_S5PC110) { + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r) { + dev_err(&pdev->dev, "no buffer memory resource defined\n"); + return -ENOENT; + goto ahb_resource_failed; + } + + onenand->ahb_res = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!onenand->ahb_res) { + dev_err(&pdev->dev, "failed to request buffer memory resource\n"); + err = -EBUSY; + goto ahb_resource_failed; + } + + onenand->ahb_addr = ioremap(r->start, resource_size(r)); + if (!onenand->ahb_addr) { + dev_err(&pdev->dev, "failed to map buffer memory resource\n"); + err = -EINVAL; + goto ahb_ioremap_failed; + } + + /* Allocate 4KiB BufferRAM */ + onenand->page_buf = kzalloc(SZ_4K, GFP_KERNEL); + if (!onenand->page_buf) { + err = -ENOMEM; + goto page_buf_fail; + } + + /* Allocate 128 SpareRAM */ + onenand->oob_buf = kzalloc(128, GFP_KERNEL); + if (!onenand->oob_buf) { + err = -ENOMEM; + goto oob_buf_fail; + } + + /* S3C doesn't handle subpage write */ + mtd->subpage_sft = 0; + this->subpagesize = mtd->writesize; + + } else { /* S5PC110 */ + r = platform_get_resource(pdev, IORESOURCE_MEM, 1); + if (!r) { + dev_err(&pdev->dev, "no dma memory resource defined\n"); + return -ENOENT; + goto dma_resource_failed; + } + + onenand->dma_res = request_mem_region(r->start, resource_size(r), + pdev->name); + if (!onenand->dma_res) { + dev_err(&pdev->dev, "failed to request dma memory resource\n"); + err = -EBUSY; + goto dma_resource_failed; + } + + onenand->dma_addr = ioremap(r->start, resource_size(r)); + if (!onenand->dma_addr) { + dev_err(&pdev->dev, "failed to map dma memory resource\n"); + err = -EINVAL; + goto dma_ioremap_failed; + } + + onenand->phys_base = onenand->base_res->start; + + onenand_ctrl_cfg = readl(onenand->dma_addr + 0x100); + if ((onenand_ctrl_cfg & ONENAND_SYS_CFG1_SYNC_WRITE) && + onenand->dma_addr) + writel(onenand_ctrl_cfg & ~ONENAND_SYS_CFG1_SYNC_WRITE, + onenand->dma_addr + 0x100); + else + onenand_ctrl_cfg = 0; + } + + if (onenand_scan(mtd, 1)) { + err = -EFAULT; + goto scan_failed; + } + + if (onenand->type == TYPE_S5PC110) { + if (onenand_ctrl_cfg && onenand->dma_addr) + writel(onenand_ctrl_cfg, onenand->dma_addr + 0x100); + } else { + /* S3C doesn't handle subpage write */ + mtd->subpage_sft = 0; + this->subpagesize = mtd->writesize; + } + + if (s3c_read_reg(MEM_CFG_OFFSET) & ONENAND_SYS_CFG1_SYNC_READ) + dev_info(&onenand->pdev->dev, "OneNAND Sync. Burst Read enabled\n"); + +#ifdef CONFIG_MTD_PARTITIONS + err = parse_mtd_partitions(mtd, part_probes, &onenand->parts, 0); + if (err > 0) + add_mtd_partitions(mtd, onenand->parts, err); + else if (err <= 0 && pdata && pdata->parts) + add_mtd_partitions(mtd, pdata->parts, pdata->nr_parts); + else +#endif + err = add_mtd_device(mtd); + + platform_set_drvdata(pdev, mtd); + + return 0; + +scan_failed: + if (onenand->dma_addr) + iounmap(onenand->dma_addr); +dma_ioremap_failed: + if (onenand->dma_res) + release_mem_region(onenand->dma_res->start, + resource_size(onenand->dma_res)); + kfree(onenand->oob_buf); +oob_buf_fail: + kfree(onenand->page_buf); +page_buf_fail: + if (onenand->ahb_addr) + iounmap(onenand->ahb_addr); +ahb_ioremap_failed: + if (onenand->ahb_res) + release_mem_region(onenand->ahb_res->start, + resource_size(onenand->ahb_res)); +dma_resource_failed: +ahb_resource_failed: + iounmap(onenand->base); +ioremap_failed: + if (onenand->base_res) + release_mem_region(onenand->base_res->start, + resource_size(onenand->base_res)); +resource_failed: + kfree(onenand); +onenand_fail: + kfree(mtd); + return err; +} + +static int __devexit s3c_onenand_remove(struct platform_device *pdev) +{ + struct mtd_info *mtd = platform_get_drvdata(pdev); + + onenand_release(mtd); + if (onenand->ahb_addr) + iounmap(onenand->ahb_addr); + if (onenand->ahb_res) + release_mem_region(onenand->ahb_res->start, + resource_size(onenand->ahb_res)); + if (onenand->dma_addr) + iounmap(onenand->dma_addr); + if (onenand->dma_res) + release_mem_region(onenand->dma_res->start, + resource_size(onenand->dma_res)); + + iounmap(onenand->base); + release_mem_region(onenand->base_res->start, + resource_size(onenand->base_res)); + + platform_set_drvdata(pdev, NULL); + kfree(onenand->oob_buf); + kfree(onenand->page_buf); + kfree(onenand); + kfree(mtd); + return 0; +} + +static int s3c_pm_ops_suspend(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct onenand_chip *this = mtd->priv; + + this->wait(mtd, FL_PM_SUSPENDED); + return mtd->suspend(mtd); +} + +static int s3c_pm_ops_resume(struct device *dev) +{ + struct platform_device *pdev = to_platform_device(dev); + struct mtd_info *mtd = platform_get_drvdata(pdev); + struct onenand_chip *this = mtd->priv; + + mtd->resume(mtd); + this->unlock_all(mtd); + return 0; +} + +static const struct dev_pm_ops s3c_pm_ops = { + .suspend = s3c_pm_ops_suspend, + .resume = s3c_pm_ops_resume, +}; + +static struct platform_device_id s3c_onenand_driver_ids[] = { + { + .name = "s3c6400-onenand", + .driver_data = TYPE_S3C6400, + }, { + .name = "s3c6410-onenand", + .driver_data = TYPE_S3C6410, + }, { + .name = "s5pc100-onenand", + .driver_data = TYPE_S5PC100, + }, { + .name = "s5pc110-onenand", + .driver_data = TYPE_S5PC110, + }, { }, +}; +MODULE_DEVICE_TABLE(platform, s3c_onenand_driver_ids); + +static struct platform_driver s3c_onenand_driver = { + .driver = { + .name = "samsung-onenand", + .pm = &s3c_pm_ops, + }, + .id_table = s3c_onenand_driver_ids, + .probe = s3c_onenand_probe, + .remove = __devexit_p(s3c_onenand_remove), +}; + +static int __init s3c_onenand_init(void) +{ + return platform_driver_register(&s3c_onenand_driver); +} + +static void __exit s3c_onenand_exit(void) +{ + platform_driver_unregister(&s3c_onenand_driver); +} + +module_init(s3c_onenand_init); +module_exit(s3c_onenand_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Kyungmin Park "); +MODULE_DESCRIPTION("Samsung OneNAND controller support"); -- cgit v1.2.3-70-g09d2 From 0ffe0ce36e07185c693e3ff06ab5b3b6c30780ee Mon Sep 17 00:00:00 2001 From: H Hartley Sweeten Date: Thu, 29 Apr 2010 13:34:24 -0500 Subject: mtd: sst25l: fix multi-part messages with broken spi masters Some SPI masters (ep93xx) have limitations when using the SFRMOUT signal for the spi device chip select. The SFRMOUT signal is only asserted as long as the spi transmit fifo contains data. As soon as the last bit is clocked into the receive fifo it gets deasserted. The functions sst25l_status and sst25l_match_device use the API function spi_write_then_read to write a command to the flash then read the response back. This API function creates a two part spi message for the write then read. When this message is transferred the SFRMOUT signal ends up getting deasserted after the command phase. This causes the command to get aborted by the device so the read phase returns invalid data. By changing sst25l_status and sst25l_match_device to use a single transfer synchronous message, the SFRMOUT signal stays asserted during the entire message so the correct data always gets returned. This change will have no effect on SPI masters which use a chip select mechanism (GPIO's, etc.) which does stay asserted correctly. As a bonus, the single transfer synchronous messages complete faster than multi-part messages. Signed-off-by: H Hartley Sweeten Signed-off-by: David Woodhouse --- drivers/mtd/devices/sst25l.c | 57 +++++++++++++++++++++++++------------------- 1 file changed, 33 insertions(+), 24 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/devices/sst25l.c b/drivers/mtd/devices/sst25l.c index bcf040beb83..ab5d8cd02a1 100644 --- a/drivers/mtd/devices/sst25l.c +++ b/drivers/mtd/devices/sst25l.c @@ -73,15 +73,25 @@ static struct flash_info __initdata sst25l_flash_info[] = { static int sst25l_status(struct sst25l_flash *flash, int *status) { - unsigned char command, response; + struct spi_message m; + struct spi_transfer t; + unsigned char cmd_resp[2]; int err; - command = SST25L_CMD_RDSR; - err = spi_write_then_read(flash->spi, &command, 1, &response, 1); + spi_message_init(&m); + memset(&t, 0, sizeof(struct spi_transfer)); + + cmd_resp[0] = SST25L_CMD_RDSR; + cmd_resp[1] = 0xff; + t.tx_buf = cmd_resp; + t.rx_buf = cmd_resp; + t.len = sizeof(cmd_resp); + spi_message_add_tail(&t, &m); + err = spi_sync(flash->spi, &m); if (err < 0) return err; - *status = response; + *status = cmd_resp[1]; return 0; } @@ -328,33 +338,32 @@ out: static struct flash_info *__init sst25l_match_device(struct spi_device *spi) { struct flash_info *flash_info = NULL; - unsigned char command[4], response; + struct spi_message m; + struct spi_transfer t; + unsigned char cmd_resp[6]; int i, err; uint16_t id; - command[0] = SST25L_CMD_READ_ID; - command[1] = 0; - command[2] = 0; - command[3] = 0; - err = spi_write_then_read(spi, command, sizeof(command), &response, 1); - if (err < 0) { - dev_err(&spi->dev, "error reading device id msb\n"); - return NULL; - } - - id = response << 8; - - command[0] = SST25L_CMD_READ_ID; - command[1] = 0; - command[2] = 0; - command[3] = 1; - err = spi_write_then_read(spi, command, sizeof(command), &response, 1); + spi_message_init(&m); + memset(&t, 0, sizeof(struct spi_transfer)); + + cmd_resp[0] = SST25L_CMD_READ_ID; + cmd_resp[1] = 0; + cmd_resp[2] = 0; + cmd_resp[3] = 0; + cmd_resp[4] = 0xff; + cmd_resp[5] = 0xff; + t.tx_buf = cmd_resp; + t.rx_buf = cmd_resp; + t.len = sizeof(cmd_resp); + spi_message_add_tail(&t, &m); + err = spi_sync(spi, &m); if (err < 0) { - dev_err(&spi->dev, "error reading device id lsb\n"); + dev_err(&spi->dev, "error reading device id\n"); return NULL; } - id |= response; + id = (cmd_resp[4] << 8) | cmd_resp[5]; for (i = 0; i < ARRAY_SIZE(sst25l_flash_info); i++) if (sst25l_flash_info[i].device_id == id) -- cgit v1.2.3-70-g09d2 From 426c457a3216fac74e3d44dd39729b0689f4c7ab Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 4 May 2010 20:58:03 -0700 Subject: mtd: nand: extend NAND flash detection to new MLC chips Some of the newer MLC devices have a 6-byte ID sequence in which several field definitions differ from older chips in a manner that is not backward compatible. For instance: Samsung K9GAG08U0M (5-byte sequence): ec d5 14 b6 74 4th byte, bits 1:0 encode the page size: 0=1KiB, 1=2KiB, 2=4KiB, 3=8KiB 4th byte, bits 5:4 encode the block size: 0=64KiB, 1=128KiB, ... 4th byte, bit 6 encodes the OOB size: 0=8B/512B, 1=16B/512B Samsung K9GAG08U0D (6-byte sequence): ec d5 94 29 34 41 4th byte, bits 1:0 encode the page size: 0=2KiB, 1=4KiB, 3=8KiB, 4=rsvd 4th byte, bits 7;5:4 encode the block size: 0=128KiB, 1=256KiB, ... 4th byte, bits 6;3:2 encode the OOB size: 1=128B/page, 2=218B/page This patch uses the new 6-byte scheme if the following conditions are all true: 1) The ID code wraps around after exactly 6 bytes 2) Manufacturer is Samsung 3) 6th byte is zero The patch also extends the maximum OOB size from 128B to 256B. Signed-off-by: Kevin Cernekee Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 64 ++++++++++++++++++++++++++++++-------------- include/linux/mtd/nand.h | 2 +- 2 files changed, 45 insertions(+), 21 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index b9dc65c7253..85891dcc27a 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -2774,8 +2774,8 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, int busw, int *maf_id, struct nand_flash_dev *type) { - int dev_id, maf_idx; - int tmp_id, tmp_manf; + int i, dev_id, maf_idx; + u8 id_data[8]; /* Select the device */ chip->select_chip(mtd, 0); @@ -2801,15 +2801,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, chip->cmdfunc(mtd, NAND_CMD_READID, 0x00, -1); - /* Read manufacturer and device IDs */ + /* Read entire ID string */ - tmp_manf = chip->read_byte(mtd); - tmp_id = chip->read_byte(mtd); + for (i = 0; i < 8; i++) + id_data[i] = chip->read_byte(mtd); - if (tmp_manf != *maf_id || tmp_id != dev_id) { + if (id_data[0] != *maf_id || id_data[1] != dev_id) { printk(KERN_INFO "%s: second ID read did not match " "%02x,%02x against %02x,%02x\n", __func__, - *maf_id, dev_id, tmp_manf, tmp_id); + *maf_id, dev_id, id_data[0], id_data[1]); return ERR_PTR(-ENODEV); } @@ -2832,21 +2832,45 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (!type->pagesize) { int extid; /* The 3rd id byte holds MLC / multichip data */ - chip->cellinfo = chip->read_byte(mtd); + chip->cellinfo = id_data[2]; /* The 4th id byte is the important one */ - extid = chip->read_byte(mtd); - /* Calc pagesize */ - mtd->writesize = 1024 << (extid & 0x3); - extid >>= 2; - /* Calc oobsize */ - mtd->oobsize = (8 << (extid & 0x01)) * (mtd->writesize >> 9); - extid >>= 2; - /* Calc blocksize. Blocksize is multiples of 64KiB */ - mtd->erasesize = (64 * 1024) << (extid & 0x03); - extid >>= 2; - /* Get buswidth information */ - busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + extid = id_data[3]; + /* + * Field definitions are in the following datasheets: + * Old style (4,5 byte ID): Samsung K9GAG08U0M (p.32) + * New style (6 byte ID): Samsung K9GAG08U0D (p.40) + * + * Check for wraparound + Samsung ID + nonzero 6th byte + * to decide what to do. + */ + if (id_data[0] == id_data[6] && id_data[1] == id_data[7] && + id_data[0] == NAND_MFR_SAMSUNG && + id_data[5] != 0x00) { + /* Calc pagesize */ + mtd->writesize = 2048 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (extid & 0x03) == 0x01 ? 128 : 218; + extid >>= 2; + /* Calc blocksize */ + mtd->erasesize = (128 * 1024) << + (((extid >> 1) & 0x04) | (extid & 0x03)); + busw = 0; + } else { + /* Calc pagesize */ + mtd->writesize = 1024 << (extid & 0x03); + extid >>= 2; + /* Calc oobsize */ + mtd->oobsize = (8 << (extid & 0x01)) * + (mtd->writesize >> 9); + extid >>= 2; + /* Calc blocksize. Blocksize is multiples of 64KiB */ + mtd->erasesize = (64 * 1024) << (extid & 0x03); + extid >>= 2; + /* Get buswidth information */ + busw = (extid & 0x01) ? NAND_BUSWIDTH_16 : 0; + } } else { /* * Old devices have chip data hardcoded in the device id table diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 8bdacb885f9..50f3aa00a45 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -53,7 +53,7 @@ extern int nand_unlock(struct mtd_info *mtd, loff_t ofs, uint64_t len); * is supported now. If you add a chip with bigger oobsize/page * adjust this accordingly. */ -#define NAND_MAX_OOBSIZE 128 +#define NAND_MAX_OOBSIZE 256 #define NAND_MAX_PAGESIZE 4096 /* -- cgit v1.2.3-70-g09d2 From b60b08b02ca8d9575985ae6711bd656dd67e9039 Mon Sep 17 00:00:00 2001 From: Kevin Cernekee Date: Tue, 4 May 2010 20:58:10 -0700 Subject: mtd: nand: support alternate BB marker locations on MLC This is a slightly modified version of a patch submitted last year by Reuben Dowle . His original comments follow: This patch adds support for some MLC NAND flashes that place the BB marker in the LAST page of the bad block rather than the FIRST page used for SLC NAND and other types of MLC nand. Lifted from Samsung datasheet for K9LG8G08U0A (1Gbyte MLC NAND): " Identifying Initial Invalid Block(s) All device locations are erased(FFh) except locations where the initial invalid block(s) information is written prior to shipping. The initial invalid block(s) status is defined by the 1st byte in the spare area. Samsung makes sure that the last page of every initial invalid block has non-FFh data at the column address of 2,048. ... " As far as I can tell, this is the same for all Samsung MLC nand, and in fact the samsung bsp for the processor used in our project (s3c6410) actually contained a hack similar to this patch but less portable to enable use of their NAND parts. I discovered this problem when trying to use a Micron NAND which does not used this layout - I wish samsung would put their stuff in main-line to avoid this type of problem. Currently this patch causes all MLC nand with manufacturer codes from Samsung and ST(Numonyx) to use this alternative location, since these are the manufactures that I know of that use this layout. Signed-off-by: Kevin Cernekee Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_base.c | 15 +++++++++++++++ drivers/mtd/nand/nand_bbt.c | 3 +++ include/linux/mtd/nand.h | 2 ++ 3 files changed, 20 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_base.c b/drivers/mtd/nand/nand_base.c index 85891dcc27a..4a7b86423ee 100644 --- a/drivers/mtd/nand/nand_base.c +++ b/drivers/mtd/nand/nand_base.c @@ -347,6 +347,9 @@ static int nand_block_bad(struct mtd_info *mtd, loff_t ofs, int getchip) struct nand_chip *chip = mtd->priv; u16 bad; + if (chip->options & NAND_BB_LAST_PAGE) + ofs += mtd->erasesize - mtd->writesize; + page = (int)(ofs >> chip->page_shift) & chip->pagemask; if (getchip) { @@ -396,6 +399,9 @@ static int nand_default_block_markbad(struct mtd_info *mtd, loff_t ofs) uint8_t buf[2] = { 0, 0 }; int block, ret; + if (chip->options & NAND_BB_LAST_PAGE) + ofs += mtd->erasesize - mtd->writesize; + /* Get block number */ block = (int)(ofs >> chip->bbt_erase_shift); if (chip->bbt) @@ -2933,6 +2939,15 @@ static struct nand_flash_dev *nand_get_flash_type(struct mtd_info *mtd, if (*maf_id != NAND_MFR_SAMSUNG && !type->pagesize) chip->options &= ~NAND_SAMSUNG_LP_OPTIONS; + /* + * Bad block marker is stored in the last page of each block + * on Samsung and Hynix MLC devices + */ + if ((chip->cellinfo & NAND_CI_CELLTYPE_MSK) && + (*maf_id == NAND_MFR_SAMSUNG || + *maf_id == NAND_MFR_HYNIX)) + chip->options |= NAND_BB_LAST_PAGE; + /* Check for AND chips with 4 page planes */ if (chip->options & NAND_4PAGE_ARRAY) chip->erase_cmd = multi_erase_cmd; diff --git a/drivers/mtd/nand/nand_bbt.c b/drivers/mtd/nand/nand_bbt.c index 387c45c366f..ad97c0ce73b 100644 --- a/drivers/mtd/nand/nand_bbt.c +++ b/drivers/mtd/nand/nand_bbt.c @@ -432,6 +432,9 @@ static int create_bbt(struct mtd_info *mtd, uint8_t *buf, from = (loff_t)startblock << (this->bbt_erase_shift - 1); } + if (this->options & NAND_BB_LAST_PAGE) + from += mtd->erasesize - (mtd->writesize * len); + for (i = startblock; i < numblocks;) { int ret; diff --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 50f3aa00a45..a81b185e23a 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -181,6 +181,8 @@ typedef enum { #define NAND_NO_READRDY 0x00000100 /* Chip does not allow subpage writes */ #define NAND_NO_SUBPAGE_WRITE 0x00000200 +/* Chip stores bad block marker on the last page of the eraseblock */ +#define NAND_BB_LAST_PAGE 0x00000400 /* Device is one of 'new' xD cards that expose fake nand command set */ #define NAND_BROKEN_XD 0x00000400 -- cgit v1.2.3-70-g09d2 From 087acaf1c6812d0ff4d4cb79c6f5a0e4e63815b3 Mon Sep 17 00:00:00 2001 From: Wolfram Sang Date: Tue, 27 Apr 2010 04:19:33 +0200 Subject: mtd: chips: add SST39WF160x NOR-flashes Due to a broken CFI, they have to be added to jedec_probe. Signed-off-by: Wolfram Sang Signed-off-by: David Woodhouse --- drivers/mtd/chips/jedec_probe.c | 30 ++++++++++++++++++++++++++++++ 1 file changed, 30 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index b2e6f2f79de..d72a5fb2d04 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -148,6 +148,8 @@ #define SST39LF160 0x2782 #define SST39VF1601 0x234b #define SST39VF3201 0x235b +#define SST39WF1601 0x274b +#define SST39WF1602 0x274a #define SST39LF512 0x00D4 #define SST39LF010 0x00D5 #define SST39LF020 0x00D6 @@ -1510,6 +1512,34 @@ static const struct amd_flash_info jedec_table[] = { ERASEINFO(0x1000,256), ERASEINFO(0x1000,256) } + }, { + /* CFI is broken: reports AMD_STD, but needs custom uaddr */ + .mfr_id = CFI_MFR_SST, + .dev_id = SST39WF1601, + .name = "SST 39WF1601", + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0xAAAA_0x5555, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } + }, { + /* CFI is broken: reports AMD_STD, but needs custom uaddr */ + .mfr_id = CFI_MFR_SST, + .dev_id = SST39WF1602, + .name = "SST 39WF1602", + .devtypes = CFI_DEVICETYPE_X16, + .uaddr = MTD_UADDR_0xAAAA_0x5555, + .dev_size = SIZE_2MiB, + .cmd_set = P_ID_AMD_STD, + .nr_regions = 2, + .regions = { + ERASEINFO(0x1000,256), + ERASEINFO(0x1000,256) + } }, { .mfr_id = CFI_MFR_SST, /* should be CFI */ .dev_id = SST39VF3201, -- cgit v1.2.3-70-g09d2 From f6763c98cb2175a816936f9b125d40054a27c185 Mon Sep 17 00:00:00 2001 From: Alexander Kurz Date: Thu, 13 May 2010 11:59:59 +0200 Subject: pcmciamtd: fixing obvious errors After fixing the obvious errors, the driver will now compile again on v2.6.34-rc3. First tests with two 4MB flash cards including erase- and write test with one of the cards where successful. Also, add two new PCMCIA_DEVICE_PROD_IDs. [linux@dominikbrodowski.net: clean up commit message] Signed-off-by: Alexander Kurz Signed-off-by: Dominik Brodowski Signed-off-by: David Woodhouse --- drivers/mtd/maps/Kconfig | 2 +- drivers/mtd/maps/pcmciamtd.c | 32 ++++++++++++++++---------------- 2 files changed, 17 insertions(+), 17 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index aa2807d0ce7..f22bc9f05dd 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -435,7 +435,7 @@ config MTD_PCI config MTD_PCMCIA tristate "PCMCIA MTD driver" - depends on PCMCIA && MTD_COMPLEX_MAPPINGS && BROKEN + depends on PCMCIA && MTD_COMPLEX_MAPPINGS help Map driver for accessing PCMCIA linear flash memory cards. These cards are usually around 4-16MiB in size. This does not include diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 81159d708f8..915b64e2379 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -137,7 +137,7 @@ static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs) return d; d.x[0] = readb(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx", ofs, addr, d.x[0]); return d; } @@ -152,7 +152,7 @@ static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs) return d; d.x[0] = readw(addr); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx", ofs, addr, d.x[0]); return d; } @@ -190,7 +190,7 @@ static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d.x[0]); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx", adr, addr, d.x[0]); writeb(d.x[0], addr); } @@ -201,7 +201,7 @@ static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long if(!addr) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d.x[0]); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx", adr, addr, d.x[0]); writew(d.x[0], addr); } @@ -245,7 +245,7 @@ static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) return d; d.x[0] = readb(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx", ofs, win_base + ofs, d.x[0]); return d; } @@ -259,7 +259,7 @@ static map_word pcmcia_read16(struct map_info *map, unsigned long ofs) return d; d.x[0] = readw(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx", ofs, win_base + ofs, d.x[0]); return d; } @@ -276,27 +276,27 @@ static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from, } -static void pcmcia_write8(struct map_info *map, u8 d, unsigned long adr) +static void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr) { caddr_t win_base = (caddr_t)map->map_priv_2; if(DEV_REMOVED(map)) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, win_base + adr, d); - writeb(d, win_base + adr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx", adr, win_base + adr, d.x[0]); + writeb(d.x[0], win_base + adr); } -static void pcmcia_write16(struct map_info *map, u16 d, unsigned long adr) +static void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr) { caddr_t win_base = (caddr_t)map->map_priv_2; if(DEV_REMOVED(map)) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, win_base + adr, d); - writew(d, win_base + adr); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx", adr, win_base + adr, d.x[0]); + writew(d.x[0], win_base + adr); } @@ -432,7 +432,7 @@ static int pcmciamtd_cistpl_geo(struct pcmcia_device *p_dev, } -static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *link, int *new_name) +static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev, int *new_name) { int i; @@ -490,7 +490,6 @@ static int pcmciamtd_config(struct pcmcia_device *link) { struct pcmciamtd_dev *dev = link->priv; struct mtd_info *mtd = NULL; - cs_status_t status; win_req_t req; int ret; int i; @@ -565,7 +564,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) dev->pcmcia_map.map_priv_1 = (unsigned long)dev; dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; - dev->vpp = (vpp) ? vpp : link->socket.socket.Vpp; + dev->vpp = (vpp) ? vpp : link->socket->socket.Vpp; link->conf.Attributes = 0; if(setvpp == 2) { link->conf.Vpp = dev->vpp; @@ -652,7 +651,6 @@ static int pcmciamtd_config(struct pcmcia_device *link) link->dev_node = &dev->node; return 0; - failed: err("CS Error, exiting"); pcmciamtd_release(link); return -ENODEV; @@ -737,8 +735,10 @@ static struct pcmcia_device_id pcmciamtd_ids[] = { PCMCIA_DEVICE_PROD_ID12("intel", "VALUE SERIES 100 ", 0x40ade711, 0xdf8506d8), PCMCIA_DEVICE_PROD_ID12("KINGMAX TECHNOLOGY INC.", "SRAM 256K Bytes", 0x54d0c69c, 0xad12c29c), PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0), + PCMCIA_DEVICE_PROD_ID123("M-Systems", "M-SYS Flash Memory Card", "(c) M-Systems", 0x7ed2ad87, 0x675dc3fb, 0x7aef3965), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad), + PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies", " 4MB FLASH Card", 0x96fd8277, 0x737a5b05), PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-3000", 0x05ddca47, 0xe7d67bca), PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-4100", 0x05ddca47, 0x7bc32944), /* the following was commented out in pcmcia-cs-3.2.7 */ -- cgit v1.2.3-70-g09d2 From 9bdde162ebcc0237e722e8c3d0d376e35188a98f Mon Sep 17 00:00:00 2001 From: Alexander Kurz Date: Thu, 13 May 2010 12:00:00 +0200 Subject: pcmciamtd: coding style cleanups Signed-off-by: Alexander Kurz Signed-off-by: Dominik Brodowski Signed-off-by: David Woodhouse --- drivers/mtd/maps/pcmciamtd.c | 55 ++++++++++++++++++++++++++------------------ 1 file changed, 33 insertions(+), 22 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 915b64e2379..050aa8ecb24 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -40,10 +40,7 @@ MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); static const int debug = 0; #endif -#define err(format, arg...) printk(KERN_ERR "pcmciamtd: " format "\n" , ## arg) #define info(format, arg...) printk(KERN_INFO "pcmciamtd: " format "\n" , ## arg) -#define warn(format, arg...) printk(KERN_WARNING "pcmciamtd: " format "\n" , ## arg) - #define DRIVER_DESC "PCMCIA Flash memory card driver" @@ -100,7 +97,9 @@ module_param(mem_type, int, 0); MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)"); -/* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */ +/* read/write{8,16} copy_{from,to} routines with window remapping + * to access whole card + */ static caddr_t remap_window(struct map_info *map, unsigned long to) { struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; @@ -245,7 +244,8 @@ static map_word pcmcia_read8(struct map_info *map, unsigned long ofs) return d; d.x[0] = readb(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx", ofs, win_base + ofs, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02lx", + ofs, win_base + ofs, d.x[0]); return d; } @@ -259,7 +259,8 @@ static map_word pcmcia_read16(struct map_info *map, unsigned long ofs) return d; d.x[0] = readw(win_base + ofs); - DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx", ofs, win_base + ofs, d.x[0]); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04lx", + ofs, win_base + ofs, d.x[0]); return d; } @@ -283,7 +284,8 @@ static void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr) if(DEV_REMOVED(map)) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx", adr, win_base + adr, d.x[0]); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02lx", + adr, win_base + adr, d.x[0]); writeb(d.x[0], win_base + adr); } @@ -295,7 +297,8 @@ static void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr) if(DEV_REMOVED(map)) return; - DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx", adr, win_base + adr, d.x[0]); + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04lx", + adr, win_base + adr, d.x[0]); writew(d.x[0], win_base + adr); } @@ -376,7 +379,8 @@ static int pcmciamtd_cistpl_jedec(struct pcmcia_device *p_dev, if (!pcmcia_parse_tuple(tuple, &parse)) { cistpl_jedec_t *t = &parse.jedec; for (i = 0; i < t->nid; i++) - DEBUG(2, "JEDEC: 0x%02x 0x%02x", t->id[i].mfr, t->id[i].info); + DEBUG(2, "JEDEC: 0x%02x 0x%02x", + t->id[i].mfr, t->id[i].info); } return -ENOSPC; } @@ -477,7 +481,8 @@ static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev } DEBUG(1, "Device: Size: %lu Width:%d Name: %s", - dev->pcmcia_map.size, dev->pcmcia_map.bankwidth << 3, dev->mtd_name); + dev->pcmcia_map.size, + dev->pcmcia_map.bankwidth << 3, dev->mtd_name); } @@ -513,9 +518,11 @@ static int pcmciamtd_config(struct pcmcia_device *link) if(setvpp == 1) dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp; - /* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum - that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the - whole card - otherwise we try smaller windows until we succeed */ + /* Request a memory window for PCMCIA. Some architeures can map windows + * upto the maximum that PCMCIA can support (64MiB) - this is ideal and + * we aim for a window the size of the whole card - otherwise we try + * smaller windows until we succeed + */ req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; req.Attributes |= (dev->pcmcia_map.bankwidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; @@ -543,7 +550,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) DEBUG(2, "dev->win_size = %d", dev->win_size); if(!dev->win_size) { - err("Cant allocate memory window"); + dev_err(&dev->p_dev->dev, "Cannot allocate memory window\n"); pcmciamtd_release(link); return -ENODEV; } @@ -553,7 +560,8 @@ static int pcmciamtd_config(struct pcmcia_device *link) DEBUG(2, "window handle = 0x%8.8lx", (unsigned long)link->win); dev->win_base = ioremap(req.Base, req.Size); if(!dev->win_base) { - err("ioremap(%lu, %u) failed", req.Base, req.Size); + dev_err(&dev->p_dev->dev, "ioremap(%lu, %u) failed\n", + req.Base, req.Size); pcmciamtd_release(link); return -ENODEV; } @@ -600,7 +608,7 @@ static int pcmciamtd_config(struct pcmcia_device *link) } if(!mtd) { - DEBUG(1, "Cant find an MTD"); + DEBUG(1, "Can not find an MTD"); pcmciamtd_release(link); return -ENODEV; } @@ -611,8 +619,9 @@ static int pcmciamtd_config(struct pcmcia_device *link) if(new_name) { int size = 0; char unit = ' '; - /* Since we are using a default name, make it better by adding in the - size */ + /* Since we are using a default name, make it better by adding + * in the size + */ if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */ size = mtd->size >> 10; unit = 'K'; @@ -642,16 +651,17 @@ static int pcmciamtd_config(struct pcmcia_device *link) if(add_mtd_device(mtd)) { map_destroy(mtd); dev->mtd_info = NULL; - err("Couldnt register MTD device"); + dev_err(&dev->p_dev->dev, + "Could not register the MTD device\n"); pcmciamtd_release(link); return -ENODEV; } snprintf(dev->node.dev_name, sizeof(dev->node.dev_name), "mtd%d", mtd->index); - info("mtd%d: %s", mtd->index, mtd->name); + dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name); link->dev_node = &dev->node; return 0; - err("CS Error, exiting"); + dev_err(&dev->p_dev->dev, "CS Error, exiting\n"); pcmciamtd_release(link); return -ENODEV; } @@ -690,7 +700,8 @@ static void pcmciamtd_detach(struct pcmcia_device *link) if(dev->mtd_info) { del_mtd_device(dev->mtd_info); - info("mtd%d: Removing", dev->mtd_info->index); + dev_info(&dev->p_dev->dev, "mtd%d: Removing\n", + dev->mtd_info->index); map_destroy(dev->mtd_info); } -- cgit v1.2.3-70-g09d2 From b2321ac37a16f0d6bdbcd2d20263e8b8b943c0ea Mon Sep 17 00:00:00 2001 From: Dominik Brodowski Date: Thu, 13 May 2010 12:00:01 +0200 Subject: pcmciamtd: add another ID Reported-by: Komuro Signed-off-by: Dominik Brodowski Signed-off-by: David Woodhouse --- drivers/mtd/maps/pcmciamtd.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index 050aa8ecb24..c4aacbfe288 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -747,6 +747,7 @@ static struct pcmcia_device_id pcmciamtd_ids[] = { PCMCIA_DEVICE_PROD_ID12("KINGMAX TECHNOLOGY INC.", "SRAM 256K Bytes", 0x54d0c69c, 0xad12c29c), PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0), PCMCIA_DEVICE_PROD_ID123("M-Systems", "M-SYS Flash Memory Card", "(c) M-Systems", 0x7ed2ad87, 0x675dc3fb, 0x7aef3965), + PCMCIA_DEVICE_PROD_ID12("PRETEC", " 2MB SRAM CARD", 0xebf91155, 0x805360ca), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b), PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad), PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies", " 4MB FLASH Card", 0x96fd8277, 0x737a5b05), -- cgit v1.2.3-70-g09d2 From f6b173cc9d73c00a3182ec3fdb0f03909cad4b5b Mon Sep 17 00:00:00 2001 From: Florian Fainelli Date: Fri, 7 May 2010 19:09:13 +0200 Subject: mtd: nand: add Toshiba TC58NVG0 device ID This NAND flash part advertises 0xD1 as an identifier but is still a working 128MBytes x 8bits 3.3V NAND part. Signed-off-by: Florian Fainelli Signed-off-by: David Woodhouse --- drivers/mtd/nand/nand_ids.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers') diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c index 69ee2c90eb0..89907ed9900 100644 --- a/drivers/mtd/nand/nand_ids.c +++ b/drivers/mtd/nand/nand_ids.c @@ -82,6 +82,7 @@ struct nand_flash_dev nand_flash_ids[] = { /* 1 Gigabit */ {"NAND 128MiB 1,8V 8-bit", 0xA1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 3,3V 8-bit", 0xF1, 0, 128, 0, LP_OPTIONS}, + {"NAND 128MiB 3,3V 8-bit", 0xD1, 0, 128, 0, LP_OPTIONS}, {"NAND 128MiB 1,8V 16-bit", 0xB1, 0, 128, 0, LP_OPTIONS16}, {"NAND 128MiB 3,3V 16-bit", 0xC1, 0, 128, 0, LP_OPTIONS16}, -- cgit v1.2.3-70-g09d2 From abab7ebf8cc12a6bb03d06b103a49e97276168f0 Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Fri, 14 May 2010 09:14:24 +0100 Subject: mtd: cfi_cmdset_0002: Fix argument order in bootloc warning Doh. Pointed out by Guillaume LECERF since I managed to miss it in my test builds. S'what I get for hacking at 2am, I suppose. Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 0e21b098248..87e86e93ebf 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -442,7 +442,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) if ((bootloc < 2) || (bootloc > 5)) { printk(KERN_WARNING "%s: CFI contains unrecognised boot " "bank location (%d). Assuming bottom.\n", - bootloc, map->name); + map->name, bootloc); bootloc = 2; } -- cgit v1.2.3-70-g09d2 From d80f2666b5373f195deae57c9f33a5abb8053d37 Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Sat, 15 May 2010 23:23:31 +0200 Subject: drivers/mtd: Use kmemdup Use kmemdup when some other buffer is immediately copied into the allocated region. A simplified version of the semantic patch that makes this change is as follows: (http://coccinelle.lip6.fr/) // @@ expression from,to,size,flag; statement S; @@ - to = \(kmalloc\|kzalloc\)(size,flag); + to = kmemdup(from,size,flag); if (to==NULL || ...) S - memcpy(to, from, size); // Signed-off-by: Julia Lawall Signed-off-by: David Woodhouse --- drivers/mtd/mtdconcat.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index db6de74082a..7e075621bbf 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -183,10 +183,9 @@ concat_writev(struct mtd_info *mtd, const struct kvec *vecs, } /* make a copy of vecs */ - vecs_copy = kmalloc(sizeof(struct kvec) * count, GFP_KERNEL); + vecs_copy = kmemdup(vecs, sizeof(struct kvec) * count, GFP_KERNEL); if (!vecs_copy) return -ENOMEM; - memcpy(vecs_copy, vecs, sizeof(struct kvec) * count); entry_low = 0; for (i = 0; i < concat->num_subdev; i++) { -- cgit v1.2.3-70-g09d2 From 11c93605faecfe6f9a28a6f3d14989bad077055a Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Mon, 17 May 2010 06:55:24 +0100 Subject: mtd/maps/pcmciamtd: Fix printk format for ssize_t in debug messages Signed-off-by: David Woodhouse --- drivers/mtd/maps/pcmciamtd.c | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index c4aacbfe288..a975f2a1bb9 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -161,7 +161,7 @@ static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; unsigned long win_size = dev->win_size; - DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + DEBUG(3, "to = %p from = %lu len = %zd", to, from, len); while(len) { int toread = win_size - (from & (win_size-1)); caddr_t addr; @@ -210,7 +210,7 @@ static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const v struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; unsigned long win_size = dev->win_size; - DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + DEBUG(3, "to = %lu from = %p len = %zd", to, from, len); while(len) { int towrite = win_size - (to & (win_size-1)); caddr_t addr; @@ -272,7 +272,7 @@ static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from, if(DEV_REMOVED(map)) return; - DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + DEBUG(3, "to = %p from = %lu len = %zd", to, from, len); memcpy_fromio(to, win_base + from, len); } @@ -310,7 +310,7 @@ static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *f if(DEV_REMOVED(map)) return; - DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + DEBUG(3, "to = %lu from = %p len = %zd", to, from, len); memcpy_toio(win_base + to, from, len); } -- cgit v1.2.3-70-g09d2 From cd874237d97f24f601f16a140d20803b6a79202e Mon Sep 17 00:00:00 2001 From: "Kirill A. Shutemov" Date: Mon, 17 May 2010 16:55:47 +0300 Subject: mtd: mtdchar: Do not corrupt backing device of device node inode We cannot modify file->f_mapping->backing_dev_info, because it will corrupt backing device of device node inode, since file->f_mapping is equal to inode->i_mapping (see __dentry_open() in fs/open.c). Let's introduce separate inode for MTD device with appropriate backing device. [dwmw2: Refactor to keep it all entirely within mtdchar.c; use iget_locked()] Signed-off-by: Kirill A. Shutemov Acked-by: Jan Kara Signed-off-by: David Woodhouse --- drivers/mtd/mtdchar.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 87 insertions(+), 8 deletions(-) (limited to 'drivers') diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index c355491d132..8bb5e4a6632 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -15,12 +15,15 @@ #include #include #include +#include #include #include #include +#define MTD_INODE_FS_MAGIC 0x11307854 +static struct vfsmount *mtd_inode_mnt __read_mostly; /* * Data structure to hold the pointer to the mtd device as well @@ -28,6 +31,7 @@ */ struct mtd_file_info { struct mtd_info *mtd; + struct inode *ino; enum mtd_file_modes mode; }; @@ -64,6 +68,7 @@ static int mtd_open(struct inode *inode, struct file *file) int ret = 0; struct mtd_info *mtd; struct mtd_file_info *mfi; + struct inode *mtd_ino; DEBUG(MTD_DEBUG_LEVEL0, "MTD_open\n"); @@ -85,11 +90,23 @@ static int mtd_open(struct inode *inode, struct file *file) goto out; } - if (mtd->backing_dev_info) - file->f_mapping->backing_dev_info = mtd->backing_dev_info; + mtd_ino = iget_locked(mtd_inode_mnt->mnt_sb, devnum); + if (!mtd_ino) { + put_mtd_device(mtd); + ret = -ENOMEM; + goto out; + } + if (mtd_ino->i_state & I_NEW) { + mtd_ino->i_private = mtd; + mtd_ino->i_mode = S_IFCHR; + mtd_ino->i_data.backing_dev_info = mtd->backing_dev_info; + unlock_new_inode(mtd_ino); + } + file->f_mapping = mtd_ino->i_mapping; /* You can't open it RW if it's not a writeable device */ if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) { + iput(mtd_ino); put_mtd_device(mtd); ret = -EACCES; goto out; @@ -97,10 +114,12 @@ static int mtd_open(struct inode *inode, struct file *file) mfi = kzalloc(sizeof(*mfi), GFP_KERNEL); if (!mfi) { + iput(mtd_ino); put_mtd_device(mtd); ret = -ENOMEM; goto out; } + mfi->ino = mtd_ino; mfi->mtd = mtd; file->private_data = mfi; @@ -122,6 +141,8 @@ static int mtd_close(struct inode *inode, struct file *file) if ((file->f_mode & FMODE_WRITE) && mtd->sync) mtd->sync(mtd); + iput(mfi->ino); + put_mtd_device(mtd); file->private_data = NULL; kfree(mfi); @@ -951,22 +972,80 @@ static const struct file_operations mtd_fops = { #endif }; +static int mtd_inodefs_get_sb(struct file_system_type *fs_type, int flags, + const char *dev_name, void *data, + struct vfsmount *mnt) +{ + return get_sb_pseudo(fs_type, "mtd_inode:", NULL, MTD_INODE_FS_MAGIC, + mnt); +} + +static struct file_system_type mtd_inodefs_type = { + .name = "mtd_inodefs", + .get_sb = mtd_inodefs_get_sb, + .kill_sb = kill_anon_super, +}; + +static void mtdchar_notify_add(struct mtd_info *mtd) +{ +} + +static void mtdchar_notify_remove(struct mtd_info *mtd) +{ + struct inode *mtd_ino = ilookup(mtd_inode_mnt->mnt_sb, mtd->index); + + if (mtd_ino) { + /* Destroy the inode if it exists */ + mtd_ino->i_nlink = 0; + iput(mtd_ino); + } +} + +static struct mtd_notifier mtdchar_notifier = { + .add = mtdchar_notify_add, + .remove = mtdchar_notify_remove, +}; + static int __init init_mtdchar(void) { - int status; + int ret; - status = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, + ret = __register_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd", &mtd_fops); - if (status < 0) { - printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", - MTD_CHAR_MAJOR); + if (ret < 0) { + pr_notice("Can't allocate major number %d for " + "Memory Technology Devices.\n", MTD_CHAR_MAJOR); + return ret; } - return status; + ret = register_filesystem(&mtd_inodefs_type); + if (ret) { + pr_notice("Can't register mtd_inodefs filesystem: %d\n", ret); + goto err_unregister_chdev; + } + + mtd_inode_mnt = kern_mount(&mtd_inodefs_type); + if (IS_ERR(mtd_inode_mnt)) { + ret = PTR_ERR(mtd_inode_mnt); + pr_notice("Error mounting mtd_inodefs filesystem: %d\n", ret); + goto err_unregister_filesystem; + } + register_mtd_user(&mtdchar_notifier); + + return ret; + +err_unregister_filesystem: + unregister_filesystem(&mtd_inodefs_type); +err_unregister_chdev: + __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); + return ret; } static void __exit cleanup_mtdchar(void) { + unregister_mtd_user(&mtdchar_notifier); + mntput(mtd_inode_mnt); + unregister_filesystem(&mtd_inodefs_type); __unregister_chrdev(MTD_CHAR_MAJOR, 0, 1 << MINORBITS, "mtd"); } -- cgit v1.2.3-70-g09d2 From 05d71b4625bdadd7c7ae80455e7038e221c76e4d Mon Sep 17 00:00:00 2001 From: Tejun Heo Date: Tue, 30 Mar 2010 02:52:39 +0900 Subject: mtd: update gfp/slab.h includes Implicit slab.h inclusion via percpu.h is about to go away. Make sure gfp.h or slab.h is included as necessary. Signed-off-by: Tejun Heo Signed-off-by: David Woodhouse --- drivers/mtd/mtdcore.c | 1 + drivers/mtd/nand/mpc5121_nfc.c | 1 + drivers/mtd/nand/r852.c | 1 + 3 files changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 3ae06c8935b..a1b8b70d2d0 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -21,6 +21,7 @@ #include #include #include +#include #include diff --git a/drivers/mtd/nand/mpc5121_nfc.c b/drivers/mtd/nand/mpc5121_nfc.c index f713b15fa45..3d0867d829c 100644 --- a/drivers/mtd/nand/mpc5121_nfc.c +++ b/drivers/mtd/nand/mpc5121_nfc.c @@ -27,6 +27,7 @@ #include #include +#include #include #include #include diff --git a/drivers/mtd/nand/r852.c b/drivers/mtd/nand/r852.c index 6dfbb471316..78a42329547 100644 --- a/drivers/mtd/nand/r852.c +++ b/drivers/mtd/nand/r852.c @@ -15,6 +15,7 @@ #include #include #include +#include #include #include #include "sm_common.h" -- cgit v1.2.3-70-g09d2 From 73850975e9a070abeed5912a24975d531fd5074c Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 20 May 2010 10:04:23 +0100 Subject: mtd: mxc_nand: Remove duplicate NAND_CMD_RESET case value This reverts commit 66803762 ("mtd: mxc_nand: add RESET command support"). Support for NAND_CMD_RESET was added separately in commit d4840180 ("mtd: mxc_nand: set NFC registers after reset"), causing a build error: drivers/mtd/nand/mxc_nand.c: In function 'mxc_nand_command': drivers/mtd/nand/mxc_nand.c:689: error: duplicate case value drivers/mtd/nand/mxc_nand.c:606: error: previously used here Signed-off-by: David Woodhouse --- drivers/mtd/nand/mxc_nand.c | 1 - 1 file changed, 1 deletion(-) (limited to 'drivers') diff --git a/drivers/mtd/nand/mxc_nand.c b/drivers/mtd/nand/mxc_nand.c index 35da3dc4bd1..82e94389824 100644 --- a/drivers/mtd/nand/mxc_nand.c +++ b/drivers/mtd/nand/mxc_nand.c @@ -686,7 +686,6 @@ static void mxc_nand_command(struct mtd_info *mtd, unsigned command, case NAND_CMD_ERASE1: case NAND_CMD_ERASE2: - case NAND_CMD_RESET: send_cmd(host, command, false); mxc_do_addr_cycle(mtd, column, page_addr); -- cgit v1.2.3-70-g09d2 From 1e804cec7a05e9bc26a523b1b4d0322bd894d2bc Mon Sep 17 00:00:00 2001 From: David Woodhouse Date: Thu, 20 May 2010 16:54:05 +0200 Subject: mtd: cfi_cmdset_0002: Fix MODULE_ALIAS and linkage for new 0701 commandset ID Signed-off-by: Guillaume LECERF Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 3 +++ 1 file changed, 3 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 87e86e93ebf..cd905a1c0d2 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -489,7 +489,9 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) return cfi_amdstd_setup(mtd); } +struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); EXPORT_SYMBOL_GPL(cfi_cmdset_0002); +EXPORT_SYMBOL_GPL(cfi_cmdset_0701); static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) { @@ -2040,3 +2042,4 @@ static void cfi_amdstd_destroy(struct mtd_info *mtd) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Crossnet Co. et al."); MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); +MODULE_ALIAS("cfi_cmdset_0701"); -- cgit v1.2.3-70-g09d2 From 8046112818b70329e930b1d4557ef0876c1ad2bb Mon Sep 17 00:00:00 2001 From: Guillaume LECERF Date: Thu, 20 May 2010 16:54:10 +0200 Subject: mtd: cfi_cmdset_0002: use AMD standard command-set with Winbond flash chips Tested with W19L320SBT9C [1]. [1] http://www.datasheetarchive.com/pdf-datasheets/Datasheets-40/DSA-795343.pdf [dwmw2: Fix MODULE_ALIAS and linkage] Signed-off-by: Obinou Signed-off-by: Guillaume LECERF Acked-by: Florian Fainelli Signed-off-by: David Woodhouse --- drivers/mtd/chips/cfi_cmdset_0002.c | 3 +++ drivers/mtd/chips/gen_probe.c | 1 + 2 files changed, 4 insertions(+) (limited to 'drivers') diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index cd905a1c0d2..d81079ef91a 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -489,8 +489,10 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) return cfi_amdstd_setup(mtd); } +struct mtd_info *cfi_cmdset_0006(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); struct mtd_info *cfi_cmdset_0701(struct map_info *map, int primary) __attribute__((alias("cfi_cmdset_0002"))); EXPORT_SYMBOL_GPL(cfi_cmdset_0002); +EXPORT_SYMBOL_GPL(cfi_cmdset_0006); EXPORT_SYMBOL_GPL(cfi_cmdset_0701); static struct mtd_info *cfi_amdstd_setup(struct mtd_info *mtd) @@ -2042,4 +2044,5 @@ static void cfi_amdstd_destroy(struct mtd_info *mtd) MODULE_LICENSE("GPL"); MODULE_AUTHOR("Crossnet Co. et al."); MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); +MODULE_ALIAS("cfi_cmdset_0006"); MODULE_ALIAS("cfi_cmdset_0701"); diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index 75a8f9db8e4..3b9a2843c5f 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -249,6 +249,7 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary) #ifdef CONFIG_MTD_CFI_AMDSTD case P_ID_AMD_STD: case P_ID_SST_OLD: + case P_ID_WINBOND: return cfi_cmdset_0002(map, primary); #endif #ifdef CONFIG_MTD_CFI_STAA -- cgit v1.2.3-70-g09d2