diff options
Diffstat (limited to 'drivers/mtd')
-rw-r--r-- | drivers/mtd/maps/Kconfig | 32 | ||||
-rw-r--r-- | drivers/mtd/maps/Makefile | 2 | ||||
-rw-r--r-- | drivers/mtd/maps/alchemy-flash.c | 166 | ||||
-rw-r--r-- | drivers/mtd/maps/omap_nor.c | 188 | ||||
-rw-r--r-- | drivers/mtd/maps/pismo.c | 320 | ||||
-rw-r--r-- | drivers/mtd/mtdoops.c | 2 | ||||
-rw-r--r-- | drivers/mtd/nand/Kconfig | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/au1550nd.c | 4 | ||||
-rw-r--r-- | drivers/mtd/nand/omap2.c | 35 | ||||
-rw-r--r-- | drivers/mtd/nand/sh_flctl.c | 69 | ||||
-rw-r--r-- | drivers/mtd/tests/mtd_readtest.c | 6 | ||||
-rw-r--r-- | drivers/mtd/tests/mtd_speedtest.c | 7 | ||||
-rw-r--r-- | drivers/mtd/tests/mtd_stresstest.c | 6 | ||||
-rw-r--r-- | drivers/mtd/ubi/build.c | 136 | ||||
-rw-r--r-- | drivers/mtd/ubi/cdev.c | 1 | ||||
-rw-r--r-- | drivers/mtd/ubi/debug.h | 4 | ||||
-rw-r--r-- | drivers/mtd/ubi/io.c | 120 | ||||
-rw-r--r-- | drivers/mtd/ubi/kapi.c | 15 | ||||
-rw-r--r-- | drivers/mtd/ubi/scan.c | 11 | ||||
-rw-r--r-- | drivers/mtd/ubi/upd.c | 1 | ||||
-rw-r--r-- | drivers/mtd/ubi/vtbl.c | 1 | ||||
-rw-r--r-- | drivers/mtd/ubi/wl.c | 17 |
22 files changed, 621 insertions, 526 deletions
diff --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 4c364d44ad5..aa2807d0ce7 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -251,12 +251,6 @@ config MTD_NETtel help Support for flash chips on NETtel/SecureEdge/SnapGear boards. -config MTD_ALCHEMY - tristate "AMD Alchemy Pb1xxx/Db1xxx/RDK MTD support" - depends on SOC_AU1X00 && MTD_PARTITIONS && MTD_CFI - help - Flash memory access on AMD Alchemy Pb/Db/RDK Reference Boards - config MTD_DILNETPC tristate "CFI Flash device mapped on DIL/Net PC" depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT && BROKEN @@ -428,15 +422,6 @@ config MTD_H720X This enables access to the flash chips on the Hynix evaluation boards. If you have such a board, say 'Y'. -config MTD_OMAP_NOR - tristate "TI OMAP board mappings" - depends on MTD_CFI && ARCH_OMAP - help - This enables access to the NOR flash chips on TI OMAP-based - boards defining flash platform devices and flash platform data. - These boards include the Innovator, H2, H3, OSK, Perseus2, and - more. If you have such a board, say 'Y'. - # This needs CFI or JEDEC, depending on the cards found. config MTD_PCI tristate "PCI MTD driver" @@ -549,4 +534,21 @@ config MTD_VMU To build this as a module select M here, the module will be called vmu-flash. +config MTD_PISMO + tristate "MTD discovery driver for PISMO modules" + depends on I2C + depends on ARCH_VERSATILE + help + This driver allows for discovery of PISMO modules - see + <http://www.pismoworld.org/>. These are small modules containing + up to five memory devices (eg, SRAM, flash, DOC) described by an + I2C EEPROM. + + This driver does not create any MTD maps itself; instead it + creates MTD physmap and MTD SRAM platform devices. If you + enable this option, you should consider enabling MTD_PHYSMAP + and/or MTD_PLATRAM according to the devices on your module. + + When built as a module, it will be called pismo.ko + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index ce315214ff2..bb035cd54c7 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -40,7 +40,6 @@ obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o -obj-$(CONFIG_MTD_ALCHEMY) += alchemy-flash.o obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o obj-$(CONFIG_MTD_EDB7312) += edb7312.o obj-$(CONFIG_MTD_IMPA7) += impa7.o @@ -55,7 +54,6 @@ obj-$(CONFIG_MTD_IXP2000) += ixp2000.o obj-$(CONFIG_MTD_WRSBC8260) += wr_sbc82xx_flash.o obj-$(CONFIG_MTD_DMV182) += dmv182.o obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o -obj-$(CONFIG_MTD_OMAP_NOR) += omap_nor.o obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o diff --git a/drivers/mtd/maps/alchemy-flash.c b/drivers/mtd/maps/alchemy-flash.c deleted file mode 100644 index 845ad4f2a54..00000000000 --- a/drivers/mtd/maps/alchemy-flash.c +++ /dev/null @@ -1,166 +0,0 @@ -/* - * Flash memory access on AMD Alchemy evaluation boards - * - * (C) 2003, 2004 Pete Popov <ppopov@embeddedalley.com> - */ - -#include <linux/init.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> - -#ifdef CONFIG_MIPS_PB1000 -#define BOARD_MAP_NAME "Pb1000 Flash" -#define BOARD_FLASH_SIZE 0x00800000 /* 8MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_PB1500 -#define BOARD_MAP_NAME "Pb1500 Flash" -#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_PB1100 -#define BOARD_MAP_NAME "Pb1100 Flash" -#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_PB1550 -#define BOARD_MAP_NAME "Pb1550 Flash" -#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_PB1200 -#define BOARD_MAP_NAME "Pb1200 Flash" -#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ -#define BOARD_FLASH_WIDTH 2 /* 16-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1000 -#define BOARD_MAP_NAME "Db1000 Flash" -#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1500 -#define BOARD_MAP_NAME "Db1500 Flash" -#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1100 -#define BOARD_MAP_NAME "Db1100 Flash" -#define BOARD_FLASH_SIZE 0x02000000 /* 32MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1550 -#define BOARD_MAP_NAME "Db1550 Flash" -#define BOARD_FLASH_SIZE 0x08000000 /* 128MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#endif - -#ifdef CONFIG_MIPS_DB1200 -#define BOARD_MAP_NAME "Db1200 Flash" -#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ -#define BOARD_FLASH_WIDTH 2 /* 16-bits */ -#endif - -#ifdef CONFIG_MIPS_BOSPORUS -#define BOARD_MAP_NAME "Bosporus Flash" -#define BOARD_FLASH_SIZE 0x01000000 /* 16MB */ -#define BOARD_FLASH_WIDTH 2 /* 16-bits */ -#endif - -#ifdef CONFIG_MIPS_MIRAGE -#define BOARD_MAP_NAME "Mirage Flash" -#define BOARD_FLASH_SIZE 0x04000000 /* 64MB */ -#define BOARD_FLASH_WIDTH 4 /* 32-bits */ -#define USE_LOCAL_ACCESSORS /* why? */ -#endif - -static struct map_info alchemy_map = { - .name = BOARD_MAP_NAME, -}; - -static struct mtd_partition alchemy_partitions[] = { - { - .name = "User FS", - .size = BOARD_FLASH_SIZE - 0x00400000, - .offset = 0x0000000 - },{ - .name = "YAMON", - .size = 0x0100000, - .offset = MTDPART_OFS_APPEND, - .mask_flags = MTD_WRITEABLE - },{ - .name = "raw kernel", - .size = (0x300000 - 0x40000), /* last 256KB is yamon env */ - .offset = MTDPART_OFS_APPEND, - } -}; - -static struct mtd_info *mymtd; - -static int __init alchemy_mtd_init(void) -{ - struct mtd_partition *parts; - int nb_parts = 0; - unsigned long window_addr; - unsigned long window_size; - - /* Default flash buswidth */ - alchemy_map.bankwidth = BOARD_FLASH_WIDTH; - - window_addr = 0x20000000 - BOARD_FLASH_SIZE; - window_size = BOARD_FLASH_SIZE; - - /* - * Static partition definition selection - */ - parts = alchemy_partitions; - nb_parts = ARRAY_SIZE(alchemy_partitions); - alchemy_map.size = window_size; - - /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. - */ - printk(KERN_NOTICE BOARD_MAP_NAME ": probing %d-bit flash bus\n", - alchemy_map.bankwidth*8); - alchemy_map.virt = ioremap(window_addr, window_size); - mymtd = do_map_probe("cfi_probe", &alchemy_map); - if (!mymtd) { - iounmap(alchemy_map.virt); - return -ENXIO; - } - mymtd->owner = THIS_MODULE; - - add_mtd_partitions(mymtd, parts, nb_parts); - return 0; -} - -static void __exit alchemy_mtd_cleanup(void) -{ - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - iounmap(alchemy_map.virt); - } -} - -module_init(alchemy_mtd_init); -module_exit(alchemy_mtd_cleanup); - -MODULE_AUTHOR("Embedded Alley Solutions, Inc"); -MODULE_DESCRIPTION(BOARD_MAP_NAME " MTD driver"); -MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/omap_nor.c b/drivers/mtd/maps/omap_nor.c index ead0b2fab67..e69de29bb2d 100644 --- a/drivers/mtd/maps/omap_nor.c +++ b/drivers/mtd/maps/omap_nor.c @@ -1,188 +0,0 @@ -/* - * Flash memory support for various TI OMAP boards - * - * Copyright (C) 2001-2002 MontaVista Software Inc. - * Copyright (C) 2003-2004 Texas Instruments - * Copyright (C) 2004 Nokia Corporation - * - * Assembled using driver code copyright the companies above - * and written by David Brownell, Jian Zhang <jzhang@ti.com>, - * Tony Lindgren <tony@atomide.com> and others. - * - * This program is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License as published by the - * Free Software Foundation; either version 2 of the License, or (at your - * option) any later version. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN - * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, - * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT - * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF - * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON - * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT - * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF - * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * You should have received a copy of the GNU General Public License along - * with this program; if not, write to the Free Software Foundation, Inc., - * 675 Mass Ave, Cambridge, MA 02139, USA. - */ - -#include <linux/platform_device.h> -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <linux/init.h> -#include <linux/ioport.h> -#include <linux/slab.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - -#include <asm/io.h> -#include <mach/hardware.h> -#include <asm/mach/flash.h> -#include <plat/tc.h> - -#ifdef CONFIG_MTD_PARTITIONS -static const char *part_probes[] = { /* "RedBoot", */ "cmdlinepart", NULL }; -#endif - -struct omapflash_info { - struct mtd_partition *parts; - struct mtd_info *mtd; - struct map_info map; -}; - -static void omap_set_vpp(struct map_info *map, int enable) -{ - static int count; - u32 l; - - if (cpu_class_is_omap1()) { - if (enable) { - if (count++ == 0) { - l = omap_readl(EMIFS_CONFIG); - l |= OMAP_EMIFS_CONFIG_WP; - omap_writel(l, EMIFS_CONFIG); - } - } else { - if (count && (--count == 0)) { - l = omap_readl(EMIFS_CONFIG); - l &= ~OMAP_EMIFS_CONFIG_WP; - omap_writel(l, EMIFS_CONFIG); - } - } - } -} - -static int __init omapflash_probe(struct platform_device *pdev) -{ - int err; - struct omapflash_info *info; - struct flash_platform_data *pdata = pdev->dev.platform_data; - struct resource *res = pdev->resource; - unsigned long size = res->end - res->start + 1; - - info = kzalloc(sizeof(struct omapflash_info), GFP_KERNEL); - if (!info) - return -ENOMEM; - - if (!request_mem_region(res->start, size, "flash")) { - err = -EBUSY; - goto out_free_info; - } - - info->map.virt = ioremap(res->start, size); - if (!info->map.virt) { - err = -ENOMEM; - goto out_release_mem_region; - } - info->map.name = dev_name(&pdev->dev); - info->map.phys = res->start; - info->map.size = size; - info->map.bankwidth = pdata->width; - info->map.set_vpp = omap_set_vpp; - - simple_map_init(&info->map); - info->mtd = do_map_probe(pdata->map_name, &info->map); - if (!info->mtd) { - err = -EIO; - goto out_iounmap; - } - info->mtd->owner = THIS_MODULE; - - info->mtd->dev.parent = &pdev->dev; - -#ifdef CONFIG_MTD_PARTITIONS - err = parse_mtd_partitions(info->mtd, part_probes, &info->parts, 0); - if (err > 0) - add_mtd_partitions(info->mtd, info->parts, err); - else if (err <= 0 && pdata->parts) - add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts); - else -#endif - add_mtd_device(info->mtd); - - platform_set_drvdata(pdev, info); - - return 0; - -out_iounmap: - iounmap(info->map.virt); -out_release_mem_region: - release_mem_region(res->start, size); -out_free_info: - kfree(info); - - return err; -} - -static int __exit omapflash_remove(struct platform_device *pdev) -{ - struct omapflash_info *info = platform_get_drvdata(pdev); - - platform_set_drvdata(pdev, NULL); - - if (info) { - if (info->parts) { - del_mtd_partitions(info->mtd); - kfree(info->parts); - } else - del_mtd_device(info->mtd); - map_destroy(info->mtd); - release_mem_region(info->map.phys, info->map.size); - iounmap((void __iomem *) info->map.virt); - kfree(info); - } - - return 0; -} - -static struct platform_driver omapflash_driver = { - .remove = __exit_p(omapflash_remove), - .driver = { - .name = "omapflash", - .owner = THIS_MODULE, - }, -}; - -static int __init omapflash_init(void) -{ - return platform_driver_probe(&omapflash_driver, omapflash_probe); -} - -static void __exit omapflash_exit(void) -{ - platform_driver_unregister(&omapflash_driver); -} - -module_init(omapflash_init); -module_exit(omapflash_exit); - -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("MTD NOR map driver for TI OMAP boards"); -MODULE_ALIAS("platform:omapflash"); diff --git a/drivers/mtd/maps/pismo.c b/drivers/mtd/maps/pismo.c new file mode 100644 index 00000000000..c48cad271f5 --- /dev/null +++ b/drivers/mtd/maps/pismo.c @@ -0,0 +1,320 @@ +/* + * PISMO memory driver - http://www.pismoworld.org/ + * + * For ARM Realview and Versatile platforms + * + * 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. + */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/i2c.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/mutex.h> +#include <linux/mtd/physmap.h> +#include <linux/mtd/plat-ram.h> +#include <linux/mtd/pismo.h> + +#define PISMO_NUM_CS 5 + +struct pismo_cs_block { + u8 type; + u8 width; + __le16 access; + __le32 size; + u32 reserved[2]; + char device[32]; +} __packed; + +struct pismo_eeprom { + struct pismo_cs_block cs[PISMO_NUM_CS]; + char board[15]; + u8 sum; +} __packed; + +struct pismo_mem { + phys_addr_t base; + u32 size; + u16 access; + u8 width; + u8 type; +}; + +struct pismo_data { + struct i2c_client *client; + void (*vpp)(void *, int); + void *vpp_data; + struct platform_device *dev[PISMO_NUM_CS]; +}; + +/* FIXME: set_vpp could do with a better calling convention */ +static struct pismo_data *vpp_pismo; +static DEFINE_MUTEX(pismo_mutex); + +static int pismo_setvpp_probe_fix(struct pismo_data *pismo) +{ + mutex_lock(&pismo_mutex); + if (vpp_pismo) { + mutex_unlock(&pismo_mutex); + kfree(pismo); + return -EBUSY; + } + vpp_pismo = pismo; + mutex_unlock(&pismo_mutex); + return 0; +} + +static void pismo_setvpp_remove_fix(struct pismo_data *pismo) +{ + mutex_lock(&pismo_mutex); + if (vpp_pismo == pismo) + vpp_pismo = NULL; + mutex_unlock(&pismo_mutex); +} + +static void pismo_set_vpp(struct map_info *map, int on) +{ + struct pismo_data *pismo = vpp_pismo; + + pismo->vpp(pismo->vpp_data, on); +} +/* end of hack */ + + +static unsigned int __devinit pismo_width_to_bytes(unsigned int width) +{ + width &= 15; + if (width > 2) + return 0; + return 1 << width; +} + +static int __devinit pismo_eeprom_read(struct i2c_client *client, void *buf, + u8 addr, size_t size) +{ + int ret; + struct i2c_msg msg[] = { + { + .addr = client->addr, + .len = sizeof(addr), + .buf = &addr, + }, { + .addr = client->addr, + .flags = I2C_M_RD, + .len = size, + .buf = buf, + }, + }; + + ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg)); + + return ret == ARRAY_SIZE(msg) ? size : -EIO; +} + +static int __devinit pismo_add_device(struct pismo_data *pismo, int i, + struct pismo_mem *region, const char *name, void *pdata, size_t psize) +{ + struct platform_device *dev; + struct resource res = { }; + phys_addr_t base = region.base; + int ret; + + if (base == ~0) + return -ENXIO; + + res.start = base; + res.end = base + region->size - 1; + res.flags = IORESOURCE_MEM; + + dev = platform_device_alloc(name, i); + if (!dev) + return -ENOMEM; + dev->dev.parent = &pismo->client->dev; + + do { + ret = platform_device_add_resources(dev, &res, 1); + if (ret) + break; + + ret = platform_device_add_data(dev, pdata, psize); + if (ret) + break; + + ret = platform_device_add(dev); + if (ret) + break; + + pismo->dev[i] = dev; + return 0; + } while (0); + + platform_device_put(dev); + return ret; +} + +static int __devinit pismo_add_nor(struct pismo_data *pismo, int i, + struct pismo_mem *region) +{ + struct physmap_flash_data data = { + .width = region->width, + }; + + if (pismo->vpp) + data.set_vpp = pismo_set_vpp; + + return pismo_add_device(pismo, i, region, "physmap-flash", + &data, sizeof(data)); +} + +static int __devinit pismo_add_sram(struct pismo_data *pismo, int i, + struct pismo_mem *region) +{ + struct platdata_mtd_ram data = { + .bankwidth = region->width, + }; + + return pismo_add_device(pismo, i, region, "mtd-ram", + &data, sizeof(data)); +} + +static void __devinit pismo_add_one(struct pismo_data *pismo, int i, + const struct pismo_cs_block *cs, phys_addr_t base) +{ + struct device *dev = &pismo->client->dev; + struct pismo_mem region; + + region.base = base; + region.type = cs->type; + region.width = pismo_width_to_bytes(cs->width); + region.access = le16_to_cpu(cs->access); + region.size = le32_to_cpu(cs->size); + + if (region.width == 0) { + dev_err(dev, "cs%u: bad width: %02x, ignoring\n", i, cs->width); + return; + } + + /* + * FIXME: may need to the platforms memory controller here, but at + * the moment we assume that it has already been correctly setup. + * The memory controller can also tell us the base address as well. + */ + + dev_info(dev, "cs%u: %.32s: type %02x access %u00ps size %uK\n", + i, cs->device, region.type, region.access, region.size / 1024); + + switch (region.type) { + case 0: + break; + case 1: + /* static DOC */ + break; + case 2: + /* static NOR */ + pismo_add_nor(pismo, i, ®ion); + break; + case 3: + /* static RAM */ + pismo_add_sram(pismo, i, ®ion); + break; + } +} + +static int __devexit pismo_remove(struct i2c_client *client) +{ + struct pismo_data *pismo = i2c_get_clientdata(client); + int i; + + for (i = 0; i < ARRAY_SIZE(pismo->dev); i++) + platform_device_unregister(pismo->dev[i]); + + /* FIXME: set_vpp needs saner arguments */ + pismo_setvpp_remove_fix(pismo); + + kfree(pismo); + + return 0; +} + +static int __devinit pismo_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct pismo_pdata *pdata = client->dev.platform_data; + struct pismo_eeprom eeprom; + struct pismo_data *pismo; + int ret, i; + + if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) { + dev_err(&client->dev, "functionality mismatch\n"); + return -EIO; + } + + pismo = kzalloc(sizeof(*pismo), GFP_KERNEL); + if (!pismo) + return -ENOMEM; + + /* FIXME: set_vpp needs saner arguments */ + ret = pismo_setvpp_probe_fix(pismo); + if (ret) + return ret; + + pismo->client = client; + if (pdata) { + pismo->vpp = pdata->set_vpp; + pismo->vpp_data = pdata->vpp_data; + } + i2c_set_clientdata(client, pismo); + + ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom)); + if (ret < 0) { + dev_err(&client->dev, "error reading EEPROM: %d\n", ret); + return ret; + } + + dev_info(&client->dev, "%.15s board found\n", eeprom.board); + + for (i = 0; i < ARRAY_SIZE(eeprom.cs); i++) + if (eeprom.cs[i].type != 0xff) + pismo_add_one(pismo, i, &eeprom.cs[i], + pdata->cs_addrs[i]); + + return 0; +} + +static const struct i2c_device_id pismo_id[] = { + { "pismo" }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, pismo_id); + +static struct i2c_driver pismo_driver = { + .driver = { + .name = "pismo", + .owner = THIS_MODULE, + }, + .probe = pismo_probe, + .remove = __devexit_p(pismo_remove), + .id_table = pismo_id, +}; + +static int __init pismo_init(void) +{ + BUILD_BUG_ON(sizeof(struct pismo_cs_block) != 48); + BUILD_BUG_ON(sizeof(struct pismo_eeprom) != 256); + + return i2c_add_driver(&pismo_driver); +} +module_init(pismo_init); + +static void __exit pismo_exit(void) +{ + i2c_del_driver(&pismo_driver); +} +module_exit(pismo_exit); + +MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>"); +MODULE_DESCRIPTION("PISMO memory driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/mtdoops.c b/drivers/mtd/mtdoops.c index a714ec48276..92e12df0917 100644 --- a/drivers/mtd/mtdoops.c +++ b/drivers/mtd/mtdoops.c @@ -322,7 +322,7 @@ static void mtdoops_do_dump(struct kmsg_dumper *dumper, memcpy(dst + l1_cpy, s2 + s2_start, l2_cpy); /* Panics must be written immediately */ - if (reason == KMSG_DUMP_PANIC) { + if (reason != KMSG_DUMP_OOPS) { if (!cxt->mtd->panic_write) printk(KERN_ERR "mtdoops: Cannot write from panic without panic_write\n"); else diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index 677cd53f18c..bb646560423 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -457,10 +457,10 @@ config MTD_NAND_NOMADIK config MTD_NAND_SH_FLCTL tristate "Support for NAND on Renesas SuperH FLCTL" - depends on MTD_NAND && SUPERH && CPU_SUBTYPE_SH7723 + depends on MTD_NAND && SUPERH help Several Renesas SuperH CPU has FLCTL. This option enables support - for NAND Flash using FLCTL. This driver support SH7723. + for NAND Flash using FLCTL. config MTD_NAND_DAVINCI tristate "Support NAND on DaVinci SoC" diff --git a/drivers/mtd/nand/au1550nd.c b/drivers/mtd/nand/au1550nd.c index 92c334ff450..43d46e42404 100644 --- a/drivers/mtd/nand/au1550nd.c +++ b/drivers/mtd/nand/au1550nd.c @@ -19,6 +19,7 @@ #include <asm/io.h> #include <asm/mach-au1x00/au1xxx.h> +#include <asm/mach-db1x00/bcsr.h> /* * MTD structure for NAND controller @@ -475,7 +476,8 @@ static int __init au1xxx_nand_init(void) /* set gpio206 high */ au_writel(au_readl(GPIO2_DIR) & ~(1 << 6), GPIO2_DIR); - boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr->status >> 6) & 0x1); + boot_swapboot = (au_readl(MEM_STSTAT) & (0x7 << 1)) | ((bcsr_read(BCSR_STATUS) >> 6) & 0x1); + switch (boot_swapboot) { case 0: case 2: diff --git a/drivers/mtd/nand/omap2.c b/drivers/mtd/nand/omap2.c index 1bb799f0125..26aec008018 100644 --- a/drivers/mtd/nand/omap2.c +++ b/drivers/mtd/nand/omap2.c @@ -30,12 +30,8 @@ #define DRIVER_NAME "omap2-nand" -/* size (4 KiB) for IO mapping */ -#define NAND_IO_SIZE SZ_4K - #define NAND_WP_OFF 0 #define NAND_WP_BIT 0x00000010 -#define WR_RD_PIN_MONITORING 0x00600000 #define GPMC_BUF_FULL 0x00000001 #define GPMC_BUF_EMPTY 0x00000000 @@ -882,8 +878,6 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) struct omap_nand_info *info; struct omap_nand_platform_data *pdata; int err; - unsigned long val; - pdata = pdev->dev.platform_data; if (pdata == NULL) { @@ -905,28 +899,14 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->gpmc_cs = pdata->cs; info->gpmc_baseaddr = pdata->gpmc_baseaddr; info->gpmc_cs_baseaddr = pdata->gpmc_cs_baseaddr; + info->phys_base = pdata->phys_base; info->mtd.priv = &info->nand; info->mtd.name = dev_name(&pdev->dev); info->mtd.owner = THIS_MODULE; - err = gpmc_cs_request(info->gpmc_cs, NAND_IO_SIZE, &info->phys_base); - if (err < 0) { - dev_err(&pdev->dev, "Cannot request GPMC CS\n"); - goto out_free_info; - } - - /* Enable RD PIN Monitoring Reg */ - if (pdata->dev_ready) { - val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1); - val |= WR_RD_PIN_MONITORING; - gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG1, val); - } - - val = gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG7); - val &= ~(0xf << 8); - val |= (0xc & 0xf) << 8; - gpmc_cs_write_reg(info->gpmc_cs, GPMC_CS_CONFIG7, val); + info->nand.options |= pdata->devsize ? NAND_BUSWIDTH_16 : 0; + info->nand.options |= NAND_SKIP_BBTSCAN; /* NAND write protect off */ omap_nand_wp(&info->mtd, NAND_WP_OFF); @@ -934,7 +914,7 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) if (!request_mem_region(info->phys_base, NAND_IO_SIZE, pdev->dev.driver->name)) { err = -EBUSY; - goto out_free_cs; + goto out_free_info; } info->nand.IO_ADDR_R = ioremap(info->phys_base, NAND_IO_SIZE); @@ -963,11 +943,6 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) info->nand.chip_delay = 50; } - info->nand.options |= NAND_SKIP_BBTSCAN; - if ((gpmc_cs_read_reg(info->gpmc_cs, GPMC_CS_CONFIG1) & 0x3000) - == 0x1000) - info->nand.options |= NAND_BUSWIDTH_16; - if (use_prefetch) { /* copy the virtual address of nand base for fifo access */ info->nand_pref_fifo_add = info->nand.IO_ADDR_R; @@ -1043,8 +1018,6 @@ static int __devinit omap_nand_probe(struct platform_device *pdev) out_release_mem_region: release_mem_region(info->phys_base, NAND_IO_SIZE); -out_free_cs: - gpmc_cs_free(info->gpmc_cs); out_free_info: kfree(info); diff --git a/drivers/mtd/nand/sh_flctl.c b/drivers/mtd/nand/sh_flctl.c index 02bef21f2e4..1842df8bdd9 100644 --- a/drivers/mtd/nand/sh_flctl.c +++ b/drivers/mtd/nand/sh_flctl.c @@ -1,10 +1,10 @@ /* * SuperH FLCTL nand controller * - * Copyright © 2008 Renesas Solutions Corp. - * Copyright © 2008 Atom Create Engineering Co., Ltd. + * Copyright (c) 2008 Renesas Solutions Corp. + * Copyright (c) 2008 Atom Create Engineering Co., Ltd. * - * Based on fsl_elbc_nand.c, Copyright © 2006-2007 Freescale Semiconductor + * Based on fsl_elbc_nand.c, Copyright (c) 2006-2007 Freescale Semiconductor * * 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 @@ -75,6 +75,11 @@ static void start_translation(struct sh_flctl *flctl) writeb(TRSTRT, FLTRCR(flctl)); } +static void timeout_error(struct sh_flctl *flctl, const char *str) +{ + dev_err(&flctl->pdev->dev, "Timeout occured in %s\n", str); +} + static void wait_completion(struct sh_flctl *flctl) { uint32_t timeout = LOOP_TIMEOUT_MAX; @@ -87,7 +92,7 @@ static void wait_completion(struct sh_flctl *flctl) udelay(1); } - printk(KERN_ERR "wait_completion(): Timeout occured \n"); + timeout_error(flctl, __func__); writeb(0x0, FLTRCR(flctl)); } @@ -100,6 +105,8 @@ static void set_addr(struct mtd_info *mtd, int column, int page_addr) addr = page_addr; /* ERASE1 */ } else if (page_addr != -1) { /* SEQIN, READ0, etc.. */ + if (flctl->chip.options & NAND_BUSWIDTH_16) + column >>= 1; if (flctl->page_size) { addr = column & 0x0FFF; addr |= (page_addr & 0xff) << 16; @@ -132,7 +139,7 @@ static void wait_rfifo_ready(struct sh_flctl *flctl) return; udelay(1); } - printk(KERN_ERR "wait_rfifo_ready(): Timeout occured \n"); + timeout_error(flctl, __func__); } static void wait_wfifo_ready(struct sh_flctl *flctl) @@ -146,7 +153,7 @@ static void wait_wfifo_ready(struct sh_flctl *flctl) return; udelay(1); } - printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n"); + timeout_error(flctl, __func__); } static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number) @@ -198,7 +205,7 @@ static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number) writel(0, FL4ECCCR(flctl)); } - printk(KERN_ERR "wait_recfifo_ready(): Timeout occured \n"); + timeout_error(flctl, __func__); return 1; /* timeout */ } @@ -214,7 +221,7 @@ static void wait_wecfifo_ready(struct sh_flctl *flctl) return; udelay(1); } - printk(KERN_ERR "wait_wecfifo_ready(): Timeout occured \n"); + timeout_error(flctl, __func__); } static void read_datareg(struct sh_flctl *flctl, int offset) @@ -275,7 +282,7 @@ static void write_fiforeg(struct sh_flctl *flctl, int rlen, int offset) static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_val) { struct sh_flctl *flctl = mtd_to_flctl(mtd); - uint32_t flcmncr_val = readl(FLCMNCR(flctl)); + uint32_t flcmncr_val = readl(FLCMNCR(flctl)) & ~SEL_16BIT; uint32_t flcmdcr_val, addr_len_bytes = 0; /* Set SNAND bit if page size is 2048byte */ @@ -297,6 +304,8 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va case NAND_CMD_READOOB: addr_len_bytes = flctl->rw_ADRCNT; flcmdcr_val |= CDSRC_E; + if (flctl->chip.options & NAND_BUSWIDTH_16) + flcmncr_val |= SEL_16BIT; break; case NAND_CMD_SEQIN: /* This case is that cmd is READ0 or READ1 or READ00 */ @@ -305,6 +314,8 @@ static void set_cmd_regs(struct mtd_info *mtd, uint32_t cmd, uint32_t flcmcdr_va case NAND_CMD_PAGEPROG: addr_len_bytes = flctl->rw_ADRCNT; flcmdcr_val |= DOCMD2_E | CDSRC_E | SELRW; + if (flctl->chip.options & NAND_BUSWIDTH_16) + flcmncr_val |= SEL_16BIT; break; case NAND_CMD_READID: flcmncr_val &= ~SNAND_E; @@ -523,6 +534,8 @@ static void flctl_cmdfunc(struct mtd_info *mtd, unsigned int command, set_addr(mtd, 0, page_addr); flctl->read_bytes = mtd->writesize + mtd->oobsize; + if (flctl->chip.options & NAND_BUSWIDTH_16) + column >>= 1; flctl->index += column; goto read_normal_exit; @@ -686,6 +699,18 @@ static uint8_t flctl_read_byte(struct mtd_info *mtd) return data; } +static uint16_t flctl_read_word(struct mtd_info *mtd) +{ + struct sh_flctl *flctl = mtd_to_flctl(mtd); + int index = flctl->index; + uint16_t data; + uint16_t *buf = (uint16_t *)&flctl->done_buff[index]; + + data = *buf; + flctl->index += 2; + return data; +} + static void flctl_read_buf(struct mtd_info *mtd, uint8_t *buf, int len) { int i; @@ -769,38 +794,36 @@ static int flctl_chip_init_tail(struct mtd_info *mtd) return 0; } -static int __init flctl_probe(struct platform_device *pdev) +static int __devinit flctl_probe(struct platform_device *pdev) { struct resource *res; struct sh_flctl *flctl; struct mtd_info *flctl_mtd; struct nand_chip *nand; struct sh_flctl_platform_data *pdata; - int ret; + int ret = -ENXIO; pdata = pdev->dev.platform_data; if (pdata == NULL) { - printk(KERN_ERR "sh_flctl platform_data not found.\n"); - return -ENODEV; + dev_err(&pdev->dev, "no platform data defined\n"); + return -EINVAL; } flctl = kzalloc(sizeof(struct sh_flctl), GFP_KERNEL); if (!flctl) { - printk(KERN_ERR "Unable to allocate NAND MTD dev structure.\n"); + dev_err(&pdev->dev, "failed to allocate driver data\n"); return -ENOMEM; } res = platform_get_resource(pdev, IORESOURCE_MEM, 0); if (!res) { - printk(KERN_ERR "%s: resource not found.\n", __func__); - ret = -ENODEV; + dev_err(&pdev->dev, "failed to get I/O memory\n"); 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; + dev_err(&pdev->dev, "failed to remap I/O memory\n"); goto err; } @@ -808,6 +831,7 @@ static int __init flctl_probe(struct platform_device *pdev) flctl_mtd = &flctl->mtd; nand = &flctl->chip; flctl_mtd->priv = nand; + flctl->pdev = pdev; flctl->hwecc = pdata->has_hwecc; flctl_register_init(flctl, pdata->flcmncr_val); @@ -825,6 +849,11 @@ static int __init flctl_probe(struct platform_device *pdev) nand->select_chip = flctl_select_chip; nand->cmdfunc = flctl_cmdfunc; + if (pdata->flcmncr_val & SEL_16BIT) { + nand->options |= NAND_BUSWIDTH_16; + nand->read_word = flctl_read_word; + } + ret = nand_scan_ident(flctl_mtd, 1); if (ret) goto err; @@ -846,7 +875,7 @@ err: return ret; } -static int __exit flctl_remove(struct platform_device *pdev) +static int __devexit flctl_remove(struct platform_device *pdev) { struct sh_flctl *flctl = platform_get_drvdata(pdev); diff --git a/drivers/mtd/tests/mtd_readtest.c b/drivers/mtd/tests/mtd_readtest.c index 79fc4530987..25c5dd03a83 100644 --- a/drivers/mtd/tests/mtd_readtest.c +++ b/drivers/mtd/tests/mtd_readtest.c @@ -147,6 +147,10 @@ static int scan_for_bad_eraseblocks(void) } memset(bbt, 0 , ebcnt); + /* NOR flash does not implement block_isbad */ + if (mtd->block_isbad == NULL) + return 0; + printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { bbt[i] = is_block_bad(i) ? 1 : 0; @@ -184,7 +188,7 @@ static int __init mtd_readtest_init(void) tmp = mtd->size; do_div(tmp, mtd->erasesize); ebcnt = tmp; - pgcnt = mtd->erasesize / mtd->writesize; + pgcnt = mtd->erasesize / pgsize; printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " diff --git a/drivers/mtd/tests/mtd_speedtest.c b/drivers/mtd/tests/mtd_speedtest.c index 141363a7e80..7fbb51d4eab 100644 --- a/drivers/mtd/tests/mtd_speedtest.c +++ b/drivers/mtd/tests/mtd_speedtest.c @@ -301,6 +301,10 @@ static int scan_for_bad_eraseblocks(void) } memset(bbt, 0 , ebcnt); + /* NOR flash does not implement block_isbad */ + if (mtd->block_isbad == NULL) + goto out; + printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { bbt[i] = is_block_bad(i) ? 1 : 0; @@ -309,6 +313,7 @@ static int scan_for_bad_eraseblocks(void) cond_resched(); } printk(PRINT_PREF "scanned %d eraseblocks, %d are bad\n", i, bad); +out: goodebcnt = ebcnt - bad; return 0; } @@ -340,7 +345,7 @@ static int __init mtd_speedtest_init(void) tmp = mtd->size; do_div(tmp, mtd->erasesize); ebcnt = tmp; - pgcnt = mtd->erasesize / mtd->writesize; + pgcnt = mtd->erasesize / pgsize; printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " diff --git a/drivers/mtd/tests/mtd_stresstest.c b/drivers/mtd/tests/mtd_stresstest.c index 63920476b57..a99d3cd737d 100644 --- a/drivers/mtd/tests/mtd_stresstest.c +++ b/drivers/mtd/tests/mtd_stresstest.c @@ -227,6 +227,10 @@ static int scan_for_bad_eraseblocks(void) } memset(bbt, 0 , ebcnt); + /* NOR flash does not implement block_isbad */ + if (mtd->block_isbad == NULL) + return 0; + printk(PRINT_PREF "scanning for bad eraseblocks\n"); for (i = 0; i < ebcnt; ++i) { bbt[i] = is_block_bad(i) ? 1 : 0; @@ -265,7 +269,7 @@ static int __init mtd_stresstest_init(void) tmp = mtd->size; do_div(tmp, mtd->erasesize); ebcnt = tmp; - pgcnt = mtd->erasesize / mtd->writesize; + pgcnt = mtd->erasesize / pgsize; printk(PRINT_PREF "MTD device size %llu, eraseblock size %u, " "page size %u, count of eraseblocks %u, pages per " diff --git a/drivers/mtd/ubi/build.c b/drivers/mtd/ubi/build.c index 14cec04c34f..fad40aa6f09 100644 --- a/drivers/mtd/ubi/build.c +++ b/drivers/mtd/ubi/build.c @@ -37,6 +37,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/stringify.h> +#include <linux/namei.h> #include <linux/stat.h> #include <linux/miscdevice.h> #include <linux/log2.h> @@ -50,7 +51,8 @@ /** * struct mtd_dev_param - MTD device parameter description data structure. - * @name: MTD device name or number string + * @name: MTD character device node path, MTD device name, or MTD device number + * string * @vid_hdr_offs: VID header offset */ struct mtd_dev_param { @@ -59,10 +61,10 @@ struct mtd_dev_param { }; /* Numbers of elements set in the @mtd_dev_param array */ -static int mtd_devs; +static int __initdata mtd_devs; /* MTD devices specification parameters */ -static struct mtd_dev_param mtd_dev_param[UBI_MAX_DEVICES]; +static struct mtd_dev_param __initdata mtd_dev_param[UBI_MAX_DEVICES]; /* Root UBI "class" object (corresponds to '/<sysfs>/class/ubi/') */ struct class *ubi_class; @@ -87,7 +89,8 @@ DEFINE_MUTEX(ubi_devices_mutex); static DEFINE_SPINLOCK(ubi_devices_lock); /* "Show" method for files in '/<sysfs>/class/ubi/' */ -static ssize_t ubi_version_show(struct class *class, char *buf) +static ssize_t ubi_version_show(struct class *class, struct class_attribute *attr, + char *buf) { return sprintf(buf, "%d\n", UBI_VERSION); } @@ -363,11 +366,13 @@ static void dev_release(struct device *dev) /** * ubi_sysfs_init - initialize sysfs for an UBI device. * @ubi: UBI device description object + * @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was + * taken * * This function returns zero in case of success and a negative error code in * case of failure. */ -static int ubi_sysfs_init(struct ubi_device *ubi) +static int ubi_sysfs_init(struct ubi_device *ubi, int *ref) { int err; @@ -379,6 +384,7 @@ static int ubi_sysfs_init(struct ubi_device *ubi) if (err) return err; + *ref = 1; err = device_create_file(&ubi->dev, &dev_eraseblock_size); if (err) return err; @@ -434,7 +440,7 @@ static void ubi_sysfs_close(struct ubi_device *ubi) } /** - * kill_volumes - destroy all volumes. + * kill_volumes - destroy all user volumes. * @ubi: UBI device description object */ static void kill_volumes(struct ubi_device *ubi) @@ -447,36 +453,29 @@ static void kill_volumes(struct ubi_device *ubi) } /** - * free_user_volumes - free all user volumes. - * @ubi: UBI device description object - * - * Normally the volumes are freed at the release function of the volume device - * objects. However, on error paths the volumes have to be freed before the - * device objects have been initialized. - */ -static void free_user_volumes(struct ubi_device *ubi) -{ - int i; - - for (i = 0; i < ubi->vtbl_slots; i++) - if (ubi->volumes[i]) { - kfree(ubi->volumes[i]->eba_tbl); - kfree(ubi->volumes[i]); - } -} - -/** * uif_init - initialize user interfaces for an UBI device. * @ubi: UBI device description object + * @ref: set to %1 on exit in case of failure if a reference to @ubi->dev was + * taken, otherwise set to %0 + * + * This function initializes various user interfaces for an UBI device. If the + * initialization fails at an early stage, this function frees all the + * resources it allocated, returns an error, and @ref is set to %0. However, + * if the initialization fails after the UBI device was registered in the + * driver core subsystem, this function takes a reference to @ubi->dev, because + * otherwise the release function ('dev_release()') would free whole @ubi + * object. The @ref argument is set to %1 in this case. The caller has to put + * this reference. * * This function returns zero in case of success and a negative error code in - * case of failure. Note, this function destroys all volumes if it fails. + * case of failure. */ -static int uif_init(struct ubi_device *ubi) +static int uif_init(struct ubi_device *ubi, int *ref) { int i, err; dev_t dev; + *ref = 0; sprintf(ubi->ubi_name, UBI_NAME_STR "%d", ubi->ubi_num); /* @@ -504,7 +503,7 @@ static int uif_init(struct ubi_device *ubi) goto out_unreg; } - err = ubi_sysfs_init(ubi); + err = ubi_sysfs_init(ubi, ref); if (err) goto out_sysfs; @@ -522,6 +521,8 @@ static int uif_init(struct ubi_device *ubi) out_volumes: kill_volumes(ubi); out_sysfs: + if (*ref) + get_device(&ubi->dev); ubi_sysfs_close(ubi); cdev_del(&ubi->cdev); out_unreg: @@ -875,7 +876,7 @@ static int ubi_reboot_notifier(struct notifier_block *n, unsigned long state, int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) { struct ubi_device *ubi; - int i, err, do_free = 1; + int i, err, ref = 0; /* * Check if we already have the same MTD device attached. @@ -975,9 +976,9 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) goto out_detach; } - err = uif_init(ubi); + err = uif_init(ubi, &ref); if (err) - goto out_nofree; + goto out_detach; ubi->bgt_thread = kthread_create(ubi_thread, ubi, ubi->bgt_name); if (IS_ERR(ubi->bgt_thread)) { @@ -1025,12 +1026,8 @@ int ubi_attach_mtd_dev(struct mtd_info *mtd, int ubi_num, int vid_hdr_offset) out_uif: uif_close(ubi); -out_nofree: - do_free = 0; out_detach: ubi_wl_close(ubi); - if (do_free) - free_user_volumes(ubi); free_internal_volumes(ubi); vfree(ubi->vtbl); out_free: @@ -1039,7 +1036,10 @@ out_free: #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID vfree(ubi->dbg_peb_buf); #endif - kfree(ubi); + if (ref) + put_device(&ubi->dev); + else + kfree(ubi); return err; } @@ -1096,7 +1096,7 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) /* * Get a reference to the device in order to prevent 'dev_release()' - * from freeing @ubi object. + * from freeing the @ubi object. */ get_device(&ubi->dev); @@ -1116,13 +1116,50 @@ int ubi_detach_mtd_dev(int ubi_num, int anyway) } /** - * find_mtd_device - open an MTD device by its name or number. - * @mtd_dev: name or number of the device + * open_mtd_by_chdev - open an MTD device by its character device node path. + * @mtd_dev: MTD character device node path + * + * This helper function opens an MTD device by its character node device path. + * Returns MTD device description object in case of success and a negative + * error code in case of failure. + */ +static struct mtd_info * __init open_mtd_by_chdev(const char *mtd_dev) +{ + int err, major, minor, mode; + struct path path; + + /* Probably this is an MTD character device node path */ + err = kern_path(mtd_dev, LOOKUP_FOLLOW, &path); + if (err) + return ERR_PTR(err); + + /* MTD device number is defined by the major / minor numbers */ + major = imajor(path.dentry->d_inode); + minor = iminor(path.dentry->d_inode); + mode = path.dentry->d_inode->i_mode; + path_put(&path); + if (major != MTD_CHAR_MAJOR || !S_ISCHR(mode)) + return ERR_PTR(-EINVAL); + + if (minor & 1) + /* + * Just do not think the "/dev/mtdrX" devices support is need, + * so do not support them to avoid doing extra work. + */ + return ERR_PTR(-EINVAL); + + return get_mtd_device(NULL, minor / 2); +} + +/** + * open_mtd_device - open MTD device by name, character device path, or number. + * @mtd_dev: name, character device node path, or MTD device device number * * This function tries to open and MTD device described by @mtd_dev string, - * which is first treated as an ASCII number, and if it is not true, it is - * treated as MTD device name. Returns MTD device description object in case of - * success and a negative error code in case of failure. + * which is first treated as ASCII MTD device number, and if it is not true, it + * is treated as MTD device name, and if that is also not true, it is treated + * as MTD character device node path. Returns MTD device description object in + * case of success and a negative error code in case of failure. */ static struct mtd_info * __init open_mtd_device(const char *mtd_dev) { @@ -1137,6 +1174,9 @@ static struct mtd_info * __init open_mtd_device(const char *mtd_dev) * MTD device name. */ mtd = get_mtd_device_nm(mtd_dev); + if (IS_ERR(mtd) && PTR_ERR(mtd) == -ENODEV) + /* Probably this is an MTD character device node path */ + mtd = open_mtd_by_chdev(mtd_dev); } else mtd = get_mtd_device(NULL, mtd_num); @@ -1352,13 +1392,15 @@ static int __init ubi_mtd_param_parse(const char *val, struct kernel_param *kp) module_param_call(mtd, ubi_mtd_param_parse, NULL, NULL, 000); MODULE_PARM_DESC(mtd, "MTD devices to attach. Parameter format: " - "mtd=<name|num>[,<vid_hdr_offs>].\n" + "mtd=<name|num|path>[,<vid_hdr_offs>].\n" "Multiple \"mtd\" parameters may be specified.\n" - "MTD devices may be specified by their number or name.\n" + "MTD devices may be specified by their number, name, or " + "path to the MTD character device node.\n" "Optional \"vid_hdr_offs\" parameter specifies UBI VID " - "header position and data starting position to be used " - "by UBI.\n" - "Example: mtd=content,1984 mtd=4 - attach MTD device" + "header position to be used by UBI.\n" + "Example 1: mtd=/dev/mtd0 - attach MTD device " + "/dev/mtd0.\n" + "Example 2: mtd=content,1984 mtd=4 - attach MTD device " "with name \"content\" using VID header offset 1984, and " "MTD device number 4 with default VID header offset."); diff --git a/drivers/mtd/ubi/cdev.c b/drivers/mtd/ubi/cdev.c index f237ddbb271..111ea41c4ec 100644 --- a/drivers/mtd/ubi/cdev.c +++ b/drivers/mtd/ubi/cdev.c @@ -853,7 +853,6 @@ static long ubi_cdev_ioctl(struct file *file, unsigned int cmd, break; } - req.name[req.name_len] = '\0'; err = verify_mkvol_req(ubi, &req); if (err) break; diff --git a/drivers/mtd/ubi/debug.h b/drivers/mtd/ubi/debug.h index f30bcb372c0..17a10712972 100644 --- a/drivers/mtd/ubi/debug.h +++ b/drivers/mtd/ubi/debug.h @@ -96,8 +96,11 @@ void ubi_dbg_dump_flash(struct ubi_device *ubi, int pnum, int offset, int len); #ifdef CONFIG_MTD_UBI_DEBUG_PARANOID int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len); +int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum, + int offset, int len); #else #define ubi_dbg_check_all_ff(ubi, pnum, offset, len) 0 +#define ubi_dbg_check_write(ubi, buf, pnum, offset, len) 0 #endif #ifdef CONFIG_MTD_UBI_DEBUG_DISABLE_BGT @@ -176,6 +179,7 @@ static inline int ubi_dbg_is_erase_failure(void) #define ubi_dbg_is_write_failure() 0 #define ubi_dbg_is_erase_failure() 0 #define ubi_dbg_check_all_ff(ubi, pnum, offset, len) 0 +#define ubi_dbg_check_write(ubi, buf, pnum, offset, len) 0 #endif /* !CONFIG_MTD_UBI_DEBUG */ #endif /* !__UBI_DEBUG_H__ */ diff --git a/drivers/mtd/ubi/io.c b/drivers/mtd/ubi/io.c index 8aa51e7a6a7..b4ecc84c754 100644 --- a/drivers/mtd/ubi/io.c +++ b/drivers/mtd/ubi/io.c @@ -143,7 +143,7 @@ int ubi_io_read(const struct ubi_device *ubi, void *buf, int pnum, int offset, err = paranoid_check_not_bad(ubi, pnum); if (err) - return err > 0 ? -EINVAL : err; + return err; addr = (loff_t)pnum * ubi->peb_size + offset; retry: @@ -236,12 +236,12 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, err = paranoid_check_not_bad(ubi, pnum); if (err) - return err > 0 ? -EINVAL : err; + return err; /* The area we are writing to has to contain all 0xFF bytes */ err = ubi_dbg_check_all_ff(ubi, pnum, offset, len); if (err) - return err > 0 ? -EINVAL : err; + return err; if (offset >= ubi->leb_start) { /* @@ -250,10 +250,10 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, */ err = paranoid_check_peb_ec_hdr(ubi, pnum); if (err) - return err > 0 ? -EINVAL : err; + return err; err = paranoid_check_peb_vid_hdr(ubi, pnum); if (err) - return err > 0 ? -EINVAL : err; + return err; } if (ubi_dbg_is_write_failure()) { @@ -273,6 +273,21 @@ int ubi_io_write(struct ubi_device *ubi, const void *buf, int pnum, int offset, } else ubi_assert(written == len); + if (!err) { + err = ubi_dbg_check_write(ubi, buf, pnum, offset, len); + if (err) + return err; + + /* + * Since we always write sequentially, the rest of the PEB has + * to contain only 0xFF bytes. + */ + offset += len; + len = ubi->peb_size - offset; + if (len) + err = ubi_dbg_check_all_ff(ubi, pnum, offset, len); + } + return err; } @@ -348,7 +363,7 @@ retry: err = ubi_dbg_check_all_ff(ubi, pnum, 0, ubi->peb_size); if (err) - return err > 0 ? -EINVAL : err; + return err; if (ubi_dbg_is_erase_failure() && !err) { dbg_err("cannot erase PEB %d (emulated)", pnum); @@ -542,7 +557,7 @@ int ubi_io_sync_erase(struct ubi_device *ubi, int pnum, int torture) err = paranoid_check_not_bad(ubi, pnum); if (err != 0) - return err > 0 ? -EINVAL : err; + return err; if (ubi->ro_mode) { ubi_err("read-only mode"); @@ -819,7 +834,7 @@ int ubi_io_write_ec_hdr(struct ubi_device *ubi, int pnum, err = paranoid_check_ec_hdr(ubi, pnum, ec_hdr); if (err) - return -EINVAL; + return err; err = ubi_io_write(ubi, ec_hdr, pnum, 0, ubi->ec_hdr_alsize); return err; @@ -1083,7 +1098,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, err = paranoid_check_peb_ec_hdr(ubi, pnum); if (err) - return err > 0 ? -EINVAL : err; + return err; vid_hdr->magic = cpu_to_be32(UBI_VID_HDR_MAGIC); vid_hdr->version = UBI_VERSION; @@ -1092,7 +1107,7 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, err = paranoid_check_vid_hdr(ubi, pnum, vid_hdr); if (err) - return -EINVAL; + return err; p = (char *)vid_hdr - ubi->vid_hdr_shift; err = ubi_io_write(ubi, p, pnum, ubi->vid_hdr_aloffset, @@ -1107,8 +1122,8 @@ int ubi_io_write_vid_hdr(struct ubi_device *ubi, int pnum, * @ubi: UBI device description object * @pnum: physical eraseblock number to check * - * This function returns zero if the physical eraseblock is good, a positive - * number if it is bad and a negative error code if an error occurred. + * This function returns zero if the physical eraseblock is good, %-EINVAL if + * it is bad and a negative error code if an error occurred. */ static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum) { @@ -1120,7 +1135,7 @@ static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum) ubi_err("paranoid check failed for PEB %d", pnum); ubi_dbg_dump_stack(); - return err; + return err > 0 ? -EINVAL : err; } /** @@ -1130,7 +1145,7 @@ static int paranoid_check_not_bad(const struct ubi_device *ubi, int pnum) * @ec_hdr: the erase counter header to check * * This function returns zero if the erase counter header contains valid - * values, and %1 if not. + * values, and %-EINVAL if not. */ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, const struct ubi_ec_hdr *ec_hdr) @@ -1156,7 +1171,7 @@ static int paranoid_check_ec_hdr(const struct ubi_device *ubi, int pnum, fail: ubi_dbg_dump_ec_hdr(ec_hdr); ubi_dbg_dump_stack(); - return 1; + return -EINVAL; } /** @@ -1164,8 +1179,8 @@ fail: * @ubi: UBI device description object * @pnum: the physical eraseblock number to check * - * This function returns zero if the erase counter header is all right, %1 if - * not, and a negative error code if an error occurred. + * This function returns zero if the erase counter header is all right and and + * a negative error code if not or if an error occurred. */ static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum) { @@ -1188,7 +1203,7 @@ static int paranoid_check_peb_ec_hdr(const struct ubi_device *ubi, int pnum) ubi_err("paranoid check failed for PEB %d", pnum); ubi_dbg_dump_ec_hdr(ec_hdr); ubi_dbg_dump_stack(); - err = 1; + err = -EINVAL; goto exit; } @@ -1206,7 +1221,7 @@ exit: * @vid_hdr: the volume identifier header to check * * This function returns zero if the volume identifier header is all right, and - * %1 if not. + * %-EINVAL if not. */ static int paranoid_check_vid_hdr(const struct ubi_device *ubi, int pnum, const struct ubi_vid_hdr *vid_hdr) @@ -1233,7 +1248,7 @@ fail: ubi_err("paranoid check failed for PEB %d", pnum); ubi_dbg_dump_vid_hdr(vid_hdr); ubi_dbg_dump_stack(); - return 1; + return -EINVAL; } @@ -1243,7 +1258,7 @@ fail: * @pnum: the physical eraseblock number to check * * This function returns zero if the volume identifier header is all right, - * %1 if not, and a negative error code if an error occurred. + * and a negative error code if not or if an error occurred. */ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) { @@ -1270,7 +1285,7 @@ static int paranoid_check_peb_vid_hdr(const struct ubi_device *ubi, int pnum) ubi_err("paranoid check failed for PEB %d", pnum); ubi_dbg_dump_vid_hdr(vid_hdr); ubi_dbg_dump_stack(); - err = 1; + err = -EINVAL; goto exit; } @@ -1282,6 +1297,61 @@ exit: } /** + * ubi_dbg_check_write - make sure write succeeded. + * @ubi: UBI device description object + * @buf: buffer with data which were written + * @pnum: physical eraseblock number the data were written to + * @offset: offset within the physical eraseblock the data were written to + * @len: how many bytes were written + * + * This functions reads data which were recently written and compares it with + * the original data buffer - the data have to match. Returns zero if the data + * match and a negative error code if not or in case of failure. + */ +int ubi_dbg_check_write(struct ubi_device *ubi, const void *buf, int pnum, + int offset, int len) +{ + int err, i; + + mutex_lock(&ubi->dbg_buf_mutex); + err = ubi_io_read(ubi, ubi->dbg_peb_buf, pnum, offset, len); + if (err) + goto out_unlock; + + for (i = 0; i < len; i++) { + uint8_t c = ((uint8_t *)buf)[i]; + uint8_t c1 = ((uint8_t *)ubi->dbg_peb_buf)[i]; + int dump_len; + + if (c == c1) + continue; + + ubi_err("paranoid check failed for PEB %d:%d, len %d", + pnum, offset, len); + ubi_msg("data differ at position %d", i); + dump_len = max_t(int, 128, len - i); + ubi_msg("hex dump of the original buffer from %d to %d", + i, i + dump_len); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + buf + i, dump_len, 1); + ubi_msg("hex dump of the read buffer from %d to %d", + i, i + dump_len); + print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, + ubi->dbg_peb_buf + i, dump_len, 1); + ubi_dbg_dump_stack(); + err = -EINVAL; + goto out_unlock; + } + mutex_unlock(&ubi->dbg_buf_mutex); + + return 0; + +out_unlock: + mutex_unlock(&ubi->dbg_buf_mutex); + return err; +} + +/** * ubi_dbg_check_all_ff - check that a region of flash is empty. * @ubi: UBI device description object * @pnum: the physical eraseblock number to check @@ -1289,8 +1359,8 @@ exit: * @len: the length of the region to check * * This function returns zero if only 0xFF bytes are present at offset - * @offset of the physical eraseblock @pnum, %1 if not, and a negative error - * code if an error occurred. + * @offset of the physical eraseblock @pnum, and a negative error code if not + * or if an error occurred. */ int ubi_dbg_check_all_ff(struct ubi_device *ubi, int pnum, int offset, int len) { @@ -1321,7 +1391,7 @@ fail: ubi_msg("hex dump of the %d-%d region", offset, offset + len); print_hex_dump(KERN_DEBUG, "", DUMP_PREFIX_OFFSET, 32, 1, ubi->dbg_peb_buf, len, 1); - err = 1; + err = -EINVAL; error: ubi_dbg_dump_stack(); mutex_unlock(&ubi->dbg_buf_mutex); diff --git a/drivers/mtd/ubi/kapi.c b/drivers/mtd/ubi/kapi.c index 277786ebaa2..1361574e2b0 100644 --- a/drivers/mtd/ubi/kapi.c +++ b/drivers/mtd/ubi/kapi.c @@ -291,8 +291,7 @@ EXPORT_SYMBOL_GPL(ubi_open_volume_nm); */ struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode) { - int error, ubi_num, vol_id; - struct ubi_volume_desc *ret; + int error, ubi_num, vol_id, mod; struct inode *inode; struct path path; @@ -306,16 +305,16 @@ struct ubi_volume_desc *ubi_open_volume_path(const char *pathname, int mode) return ERR_PTR(error); inode = path.dentry->d_inode; + mod = inode->i_mode; ubi_num = ubi_major2num(imajor(inode)); vol_id = iminor(inode) - 1; + path_put(&path); + if (!S_ISCHR(mod)) + return ERR_PTR(-EINVAL); if (vol_id >= 0 && ubi_num >= 0) - ret = ubi_open_volume(ubi_num, vol_id, mode); - else - ret = ERR_PTR(-ENODEV); - - path_put(&path); - return ret; + return ubi_open_volume(ubi_num, vol_id, mode); + return ERR_PTR(-ENODEV); } EXPORT_SYMBOL_GPL(ubi_open_volume_path); diff --git a/drivers/mtd/ubi/scan.c b/drivers/mtd/ubi/scan.c index 90af61a2c3e..594184bbd56 100644 --- a/drivers/mtd/ubi/scan.c +++ b/drivers/mtd/ubi/scan.c @@ -974,11 +974,8 @@ struct ubi_scan_info *ubi_scan(struct ubi_device *ubi) seb->ec = si->mean_ec; err = paranoid_check_si(ubi, si); - if (err) { - if (err > 0) - err = -EINVAL; + if (err) goto out_vidh; - } ubi_free_vid_hdr(ubi, vidh); kfree(ech); @@ -1086,8 +1083,8 @@ void ubi_scan_destroy_si(struct ubi_scan_info *si) * @ubi: UBI device description object * @si: scanning information * - * This function returns zero if the scanning information is all right, %1 if - * not and a negative error code if an error occurred. + * This function returns zero if the scanning information is all right, and a + * negative error code if not or if an error occurred. */ static int paranoid_check_si(struct ubi_device *ubi, struct ubi_scan_info *si) { @@ -1346,7 +1343,7 @@ bad_vid_hdr: out: ubi_dbg_dump_stack(); - return 1; + return -EINVAL; } #endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ diff --git a/drivers/mtd/ubi/upd.c b/drivers/mtd/ubi/upd.c index c1d7b880c79..425bf5a3edd 100644 --- a/drivers/mtd/ubi/upd.c +++ b/drivers/mtd/ubi/upd.c @@ -155,6 +155,7 @@ int ubi_start_update(struct ubi_device *ubi, struct ubi_volume *vol, if (err) return err; vol->updating = 0; + return 0; } vol->upd_buf = vmalloc(ubi->leb_size); diff --git a/drivers/mtd/ubi/vtbl.c b/drivers/mtd/ubi/vtbl.c index 1afc61e7455..40044028d68 100644 --- a/drivers/mtd/ubi/vtbl.c +++ b/drivers/mtd/ubi/vtbl.c @@ -566,6 +566,7 @@ static int init_volumes(struct ubi_device *ubi, const struct ubi_scan_info *si, vol->reserved_pebs = be32_to_cpu(vtbl[i].reserved_pebs); vol->alignment = be32_to_cpu(vtbl[i].alignment); vol->data_pad = be32_to_cpu(vtbl[i].data_pad); + vol->upd_marker = vtbl[i].upd_marker; vol->vol_type = vtbl[i].vol_type == UBI_VID_DYNAMIC ? UBI_DYNAMIC_VOLUME : UBI_STATIC_VOLUME; vol->name_len = be16_to_cpu(vtbl[i].name_len); diff --git a/drivers/mtd/ubi/wl.c b/drivers/mtd/ubi/wl.c index 600c7229d5c..f64ddabd4ac 100644 --- a/drivers/mtd/ubi/wl.c +++ b/drivers/mtd/ubi/wl.c @@ -464,7 +464,7 @@ retry: ubi->peb_size - ubi->vid_hdr_aloffset); if (err) { ubi_err("new PEB %d does not contain all 0xFF bytes", e->pnum); - return err > 0 ? -EINVAL : err; + return err; } return e->pnum; @@ -513,7 +513,7 @@ static int sync_erase(struct ubi_device *ubi, struct ubi_wl_entry *e, dbg_wl("erase PEB %d, old EC %llu", e->pnum, ec); err = paranoid_check_ec(ubi, e->pnum, e->ec); - if (err > 0) + if (err) return -EINVAL; ec_hdr = kzalloc(ubi->ec_hdr_alsize, GFP_NOFS); @@ -1572,8 +1572,7 @@ void ubi_wl_close(struct ubi_device *ubi) * @ec: the erase counter to check * * This function returns zero if the erase counter of physical eraseblock @pnum - * is equivalent to @ec, %1 if not, and a negative error code if an error - * occurred. + * is equivalent to @ec, and a negative error code if not or if an error occurred. */ static int paranoid_check_ec(struct ubi_device *ubi, int pnum, int ec) { @@ -1611,8 +1610,8 @@ out_free: * @e: the wear-leveling entry to check * @root: the root of the tree * - * This function returns zero if @e is in the @root RB-tree and %1 if it is - * not. + * This function returns zero if @e is in the @root RB-tree and %-EINVAL if it + * is not. */ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, struct rb_root *root) @@ -1623,7 +1622,7 @@ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, ubi_err("paranoid check failed for PEB %d, EC %d, RB-tree %p ", e->pnum, e->ec, root); ubi_dbg_dump_stack(); - return 1; + return -EINVAL; } /** @@ -1632,7 +1631,7 @@ static int paranoid_check_in_wl_tree(struct ubi_wl_entry *e, * @ubi: UBI device description object * @e: the wear-leveling entry to check * - * This function returns zero if @e is in @ubi->pq and %1 if it is not. + * This function returns zero if @e is in @ubi->pq and %-EINVAL if it is not. */ static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e) { @@ -1647,6 +1646,6 @@ static int paranoid_check_in_pq(struct ubi_device *ubi, struct ubi_wl_entry *e) ubi_err("paranoid check failed for PEB %d, EC %d, Protect queue", e->pnum, e->ec); ubi_dbg_dump_stack(); - return 1; + return -EINVAL; } #endif /* CONFIG_MTD_UBI_DEBUG_PARANOID */ |