]> www.pilppa.org Git - linux-2.6-omap-h63xx.git/commitdiff
Merge git://git.infradead.org/mtd-2.6
authorLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 Apr 2009 21:56:26 +0000 (14:56 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Mon, 6 Apr 2009 21:56:26 +0000 (14:56 -0700)
* git://git.infradead.org/mtd-2.6: (53 commits)
  [MTD] struct device - replace bus_id with dev_name(), dev_set_name()
  [MTD] [NOR] Fixup for Numonyx M29W128 chips
  [MTD] mtdpart: Make ecc_stats more realistic.
  powerpc/85xx: TQM8548: Update DTS file for multi-chip support
  powerpc: NAND: FSL UPM: document new bindings
  [MTD] [NAND] FSL-UPM: Add wait flags to support board/chip specific delays
  [MTD] [NAND] FSL-UPM: add multi chip support
  [MTD] [NOR] Add device parent info to physmap_of
  [MTD] [NAND] Add support for NAND on the Socrates board
  [MTD] [NAND] Add support for 4KiB pages.
  [MTD] sysfs support should not depend on CONFIG_PROC_FS
  [MTD] [NAND] Add parent info for CAFÉ controller
  [MTD] support driver model updates
  [MTD] driver model updates (part 2)
  [MTD] driver model updates
  [MTD] [NAND] move gen_nand's probe function to .devinit.text
  [MTD] [MAPS] move sa1100 flash's probe function to .devinit.text
  [MTD] fix use after free in register_mtd_blktrans
  [MTD] [MAPS] Drop now unused sharpsl-flash map
  [MTD] ofpart: Check name property to determine partition nodes.
  ...

Manually fix trivial conflict in drivers/mtd/maps/Makefile

78 files changed:
Documentation/powerpc/dts-bindings/fsl/upm-nand.txt
arch/arm/mach-davinci/include/mach/nand.h [new file with mode: 0644]
arch/arm/mach-pxa/include/mach/pxa3xx_nand.h
arch/blackfin/kernel/process.c
arch/mips/include/asm/txx9/ndfmc.h [new file with mode: 0644]
arch/mips/include/asm/txx9/rbtx4939.h
arch/mips/include/asm/txx9/tx4938.h
arch/mips/include/asm/txx9/tx4939.h
arch/mips/txx9/generic/setup.c
arch/mips/txx9/generic/setup_tx4938.c
arch/mips/txx9/generic/setup_tx4939.c
arch/mips/txx9/rbtx4938/setup.c
arch/mips/txx9/rbtx4939/setup.c
arch/powerpc/boot/dts/tqm8548-bigflash.dts
arch/powerpc/boot/dts/tqm8548.dts
arch/powerpc/sysdev/fsl_lbc.c
drivers/mtd/Makefile
drivers/mtd/ar7part.c
drivers/mtd/chips/cfi_cmdset_0001.c
drivers/mtd/chips/cfi_cmdset_0002.c
drivers/mtd/chips/jedec_probe.c
drivers/mtd/chips/map_ram.c
drivers/mtd/chips/map_rom.c
drivers/mtd/cmdlinepart.c
drivers/mtd/devices/doc2000.c
drivers/mtd/devices/doc2001.c
drivers/mtd/devices/doc2001plus.c
drivers/mtd/devices/docecc.c
drivers/mtd/devices/m25p80.c
drivers/mtd/devices/mtd_dataflash.c
drivers/mtd/devices/mtdram.c
drivers/mtd/inftlmount.c
drivers/mtd/internal.h [new file with mode: 0644]
drivers/mtd/maps/Kconfig
drivers/mtd/maps/Makefile
drivers/mtd/maps/omap_nor.c
drivers/mtd/maps/physmap.c
drivers/mtd/maps/physmap_of.c
drivers/mtd/maps/plat-ram.c
drivers/mtd/maps/rbtx4939-flash.c [new file with mode: 0644]
drivers/mtd/maps/sa1100-flash.c
drivers/mtd/maps/sharpsl-flash.c [deleted file]
drivers/mtd/mtd_blkdevs.c
drivers/mtd/mtdbdi.c [new file with mode: 0644]
drivers/mtd/mtdchar.c
drivers/mtd/mtdconcat.c
drivers/mtd/mtdcore.c
drivers/mtd/mtdoops.c
drivers/mtd/mtdpart.c
drivers/mtd/nand/Kconfig
drivers/mtd/nand/Makefile
drivers/mtd/nand/bf5xx_nand.c
drivers/mtd/nand/cafe_nand.c
drivers/mtd/nand/davinci_nand.c [new file with mode: 0644]
drivers/mtd/nand/fsl_upm.c
drivers/mtd/nand/mxc_nand.c
drivers/mtd/nand/nand_base.c
drivers/mtd/nand/plat_nand.c
drivers/mtd/nand/pxa3xx_nand.c
drivers/mtd/nand/sh_flctl.c
drivers/mtd/nand/socrates_nand.c [new file with mode: 0644]
drivers/mtd/nand/txx9ndfmc.c [new file with mode: 0644]
drivers/mtd/nftlcore.c
drivers/mtd/ofpart.c
drivers/mtd/onenand/omap2.c
drivers/mtd/onenand/onenand_base.c
fs/jffs2/acl.c
fs/jffs2/malloc.c
fs/romfs/Kconfig
fs/romfs/Makefile
fs/romfs/inode.c [deleted file]
fs/romfs/internal.h [new file with mode: 0644]
fs/romfs/mmap-nommu.c [new file with mode: 0644]
fs/romfs/storage.c [new file with mode: 0644]
fs/romfs/super.c [new file with mode: 0644]
include/linux/mtd/mtd.h
include/linux/mtd/nand.h
include/linux/mtd/partitions.h

index 84a04d5eb8e6d76c188e44378b705c5d19604568..a48b2cadc7f08fd4203bbf67f12f3afed3f447ed 100644 (file)
@@ -5,9 +5,21 @@ Required properties:
 - reg : should specify localbus chip select and size used for the chip.
 - fsl,upm-addr-offset : UPM pattern offset for the address latch.
 - fsl,upm-cmd-offset : UPM pattern offset for the command latch.
-- gpios : may specify optional GPIO connected to the Ready-Not-Busy pin.
 
-Example:
+Optional properties:
+- fsl,upm-wait-flags : add chip-dependent short delays after running the
+       UPM pattern (0x1), after writing a data byte (0x2) or after
+       writing out a buffer (0x4).
+- fsl,upm-addr-line-cs-offsets : address offsets for multi-chip support.
+       The corresponding address lines are used to select the chip.
+- gpios : may specify optional GPIOs connected to the Ready-Not-Busy pins
+       (R/B#). For multi-chip devices, "n" GPIO definitions are required
+       according to the number of chips.
+- chip-delay : chip dependent delay for transfering data from array to
+       read registers (tR). Required if property "gpios" is not used
+       (R/B# pins not connected).
+
+Examples:
 
 upm@1,0 {
        compatible = "fsl,upm-nand";
@@ -26,3 +38,26 @@ upm@1,0 {
                };
        };
 };
+
+upm@3,0 {
+       #address-cells = <0>;
+       #size-cells = <0>;
+       compatible = "tqc,tqm8548-upm-nand", "fsl,upm-nand";
+       reg = <3 0x0 0x800>;
+       fsl,upm-addr-offset = <0x10>;
+       fsl,upm-cmd-offset = <0x08>;
+       /* Multi-chip NAND device */
+       fsl,upm-addr-line-cs-offsets = <0x0 0x200>;
+       fsl,upm-wait-flags = <0x5>;
+       chip-delay = <25>; // in micro-seconds
+
+       nand@0 {
+               #address-cells = <1>;
+               #size-cells = <1>;
+
+               partition@0 {
+                           label = "fs";
+                           reg = <0x00000000 0x10000000>;
+               };
+       };
+};
diff --git a/arch/arm/mach-davinci/include/mach/nand.h b/arch/arm/mach-davinci/include/mach/nand.h
new file mode 100644 (file)
index 0000000..aa48284
--- /dev/null
@@ -0,0 +1,80 @@
+/*
+ * mach-davinci/nand.h
+ *
+ * Copyright Â© 2006 Texas Instruments.
+ *
+ * Ported to 2.6.23 Copyright Â© 2008 by
+ *   Sander Huijsen <Shuijsen@optelecom-nkf.com>
+ *   Troy Kisky <troy.kisky@boundarydevices.com>
+ *   Dirk Behme <Dirk.Behme@gmail.com>
+ *
+ * --------------------------------------------------------------------------
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#ifndef __ARCH_ARM_DAVINCI_NAND_H
+#define __ARCH_ARM_DAVINCI_NAND_H
+
+#include <linux/mtd/nand.h>
+
+#define NRCSR_OFFSET           0x00
+#define AWCCR_OFFSET           0x04
+#define A1CR_OFFSET            0x10
+#define NANDFCR_OFFSET         0x60
+#define NANDFSR_OFFSET         0x64
+#define NANDF1ECC_OFFSET       0x70
+
+/* 4-bit ECC syndrome registers */
+#define NAND_4BIT_ECC_LOAD_OFFSET      0xbc
+#define NAND_4BIT_ECC1_OFFSET          0xc0
+#define NAND_4BIT_ECC2_OFFSET          0xc4
+#define NAND_4BIT_ECC3_OFFSET          0xc8
+#define NAND_4BIT_ECC4_OFFSET          0xcc
+#define NAND_ERR_ADD1_OFFSET           0xd0
+#define NAND_ERR_ADD2_OFFSET           0xd4
+#define NAND_ERR_ERRVAL1_OFFSET                0xd8
+#define NAND_ERR_ERRVAL2_OFFSET                0xdc
+
+/* NOTE:  boards don't need to use these address bits
+ * for ALE/CLE unless they support booting from NAND.
+ * They're used unless platform data overrides them.
+ */
+#define        MASK_ALE                0x08
+#define        MASK_CLE                0x10
+
+struct davinci_nand_pdata {            /* platform_data */
+       uint32_t                mask_ale;
+       uint32_t                mask_cle;
+
+       /* for packages using two chipselects */
+       uint32_t                mask_chipsel;
+
+       /* board's default static partition info */
+       struct mtd_partition    *parts;
+       unsigned                nr_parts;
+
+       /* none  == NAND_ECC_NONE (strongly *not* advised!!)
+        * soft  == NAND_ECC_SOFT
+        * 1-bit == NAND_ECC_HW
+        * 4-bit == NAND_ECC_HW_SYNDROME (not on all chips)
+        */
+       nand_ecc_modes_t        ecc_mode;
+
+       /* e.g. NAND_BUSWIDTH_16 or NAND_USE_FLASH_BBT */
+       unsigned                options;
+};
+
+#endif /* __ARCH_ARM_DAVINCI_NAND_H */
index eb35fca9aea527bde9992f80917fd69cc174c346..3478eae32d8a8b8fe130e4e5c5f2c8f8a4694476 100644 (file)
@@ -49,6 +49,9 @@ struct pxa3xx_nand_platform_data {
         */
        int     enable_arbiter;
 
+       /* allow platform code to keep OBM/bootloader defined NFC config */
+       int     keep_config;
+
        const struct mtd_partition              *parts;
        unsigned int                            nr_parts;
 
index f49427293ca1c48e688e1f95a92504d2d8fa0b0a..e040e03335ea3c54792e44651424bbad51db5728 100644 (file)
@@ -337,7 +337,7 @@ int _access_ok(unsigned long addr, unsigned long size)
        if (addr >= memory_mtd_end && (addr + size) <= physical_mem_end)
                return 1;
 
-#ifdef CONFIG_ROMFS_MTD_FS
+#ifdef CONFIG_ROMFS_ON_MTD
        /* For XIP, allow user space to use pointers within the ROMFS.  */
        if (addr >= memory_mtd_start && (addr + size) <= memory_mtd_end)
                return 1;
diff --git a/arch/mips/include/asm/txx9/ndfmc.h b/arch/mips/include/asm/txx9/ndfmc.h
new file mode 100644 (file)
index 0000000..fa67f3d
--- /dev/null
@@ -0,0 +1,30 @@
+/*
+ * 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.
+ *
+ * (C) Copyright TOSHIBA CORPORATION 2007
+ */
+#ifndef __ASM_TXX9_NDFMC_H
+#define __ASM_TXX9_NDFMC_H
+
+#define NDFMC_PLAT_FLAG_USE_BSPRT      0x01
+#define NDFMC_PLAT_FLAG_NO_RSTR                0x02
+#define NDFMC_PLAT_FLAG_HOLDADD                0x04
+#define NDFMC_PLAT_FLAG_DUMMYWRITE     0x08
+
+struct txx9ndfmc_platform_data {
+       unsigned int shift;
+       unsigned int gbus_clock;
+       unsigned int hold;              /* hold time in nanosecond */
+       unsigned int spw;               /* strobe pulse width in nanosecond */
+       unsigned int flags;
+       unsigned char ch_mask;          /* available channel bitmask */
+       unsigned char wp_mask;          /* write-protect bitmask */
+       unsigned char wide_mask;        /* 16bit-nand bitmask */
+};
+
+void txx9_ndfmc_init(unsigned long baseaddr,
+                    const struct txx9ndfmc_platform_data *plat_data);
+
+#endif /* __ASM_TXX9_NDFMC_H */
index 1acf428c0b4fef83ef04f0aa5ab6af3227850fba..e517899794a8c6780b866d6b60ce24ee2e7e8669 100644 (file)
 void rbtx4939_prom_init(void);
 void rbtx4939_irq_setup(void);
 
+struct mtd_partition;
+struct map_info;
+struct rbtx4939_flash_data {
+       unsigned int width;
+       unsigned int nr_parts;
+       struct mtd_partition *parts;
+       void (*map_init)(struct map_info *map);
+};
+
 #endif /* __ASM_TXX9_RBTX4939_H */
index 0b068154054cb897c628651b74b7d58c84b0e31a..cd8bc2021755e8be084c2170371f3eaedf9d9fff 100644 (file)
@@ -291,6 +291,7 @@ int tx4938_pcic1_map_irq(const struct pci_dev *dev, u8 slot);
 void tx4938_setup_pcierr_irq(void);
 void tx4938_irq_init(void);
 void tx4938_mtd_init(int ch);
+void tx4938_ndfmc_init(unsigned int hold, unsigned int spw);
 
 struct tx4938ide_platform_info {
        /*
index 964ef7ede2684eaff7d8f8c1ad1e5e83a7b6c651..f02c50b3abfbd205f29dbd51aa828c7f5996b165 100644 (file)
@@ -542,5 +542,7 @@ int tx4939_irq(void);
 void tx4939_mtd_init(int ch);
 void tx4939_ata_init(void);
 void tx4939_rtc_init(void);
+void tx4939_ndfmc_init(unsigned int hold, unsigned int spw,
+                      unsigned char ch_mask, unsigned char wide_mask);
 
 #endif /* __ASM_TXX9_TX4939_H */
index a13a08b8c9ec1c24ba9292fe377083f2f70c2e43..8a266c6a3f58eb68151d9b5139f4572d378ee95d 100644 (file)
@@ -32,6 +32,7 @@
 #include <asm/txx9/generic.h>
 #include <asm/txx9/pci.h>
 #include <asm/txx9tmr.h>
+#include <asm/txx9/ndfmc.h>
 #ifdef CONFIG_CPU_TX49XX
 #include <asm/txx9/tx4938.h>
 #endif
@@ -691,6 +692,26 @@ void __init txx9_physmap_flash_init(int no, unsigned long addr,
 #endif
 }
 
+void __init txx9_ndfmc_init(unsigned long baseaddr,
+                           const struct txx9ndfmc_platform_data *pdata)
+{
+#if defined(CONFIG_MTD_NAND_TXX9NDFMC) || \
+       defined(CONFIG_MTD_NAND_TXX9NDFMC_MODULE)
+       struct resource res = {
+               .start = baseaddr,
+               .end = baseaddr + 0x1000 - 1,
+               .flags = IORESOURCE_MEM,
+       };
+       struct platform_device *pdev = platform_device_alloc("txx9ndfmc", -1);
+
+       if (!pdev ||
+           platform_device_add_resources(pdev, &res, 1) ||
+           platform_device_add_data(pdev, pdata, sizeof(*pdata)) ||
+           platform_device_add(pdev))
+               platform_device_put(pdev);
+#endif
+}
+
 #if defined(CONFIG_LEDS_GPIO) || defined(CONFIG_LEDS_GPIO_MODULE)
 static DEFINE_SPINLOCK(txx9_iocled_lock);
 
index 25819ff1c3507310b0edad65559afbe14afddf63..f0844f891f0bf11ca2ea9869f96ba590417eff71 100644 (file)
@@ -23,6 +23,7 @@
 #include <asm/txx9tmr.h>
 #include <asm/txx9pio.h>
 #include <asm/txx9/generic.h>
+#include <asm/txx9/ndfmc.h>
 #include <asm/txx9/tx4938.h>
 
 static void __init tx4938_wdr_init(void)
@@ -382,6 +383,26 @@ void __init tx4938_ata_init(unsigned int irq, unsigned int shift, int tune)
                platform_device_put(pdev);
 }
 
+void __init tx4938_ndfmc_init(unsigned int hold, unsigned int spw)
+{
+       struct txx9ndfmc_platform_data plat_data = {
+               .shift = 1,
+               .gbus_clock = txx9_gbus_clock,
+               .hold = hold,
+               .spw = spw,
+               .ch_mask = 1,
+       };
+       unsigned long baseaddr = TX4938_NDFMC_REG & 0xfffffffffULL;
+
+#ifdef __BIG_ENDIAN
+       baseaddr += 4;
+#endif
+       if ((__raw_readq(&tx4938_ccfgptr->pcfg) &
+            (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL)) ==
+           TX4938_PCFG_NDF_SEL)
+               txx9_ndfmc_init(baseaddr, &plat_data);
+}
+
 static void __init tx4938_stop_unused_modules(void)
 {
        __u64 pcfg, rst = 0, ckd = 0;
index 55440967b3a88ddaef9d07cef1e79263f5648d7b..7a25b573e9b07ff45bde162e5e7326e8d0ee528c 100644 (file)
@@ -27,6 +27,7 @@
 #include <asm/txx9irq.h>
 #include <asm/txx9tmr.h>
 #include <asm/txx9/generic.h>
+#include <asm/txx9/ndfmc.h>
 #include <asm/txx9/tx4939.h>
 
 static void __init tx4939_wdr_init(void)
@@ -457,6 +458,22 @@ void __init tx4939_rtc_init(void)
        platform_device_register(&rtc_dev);
 }
 
+void __init tx4939_ndfmc_init(unsigned int hold, unsigned int spw,
+                             unsigned char ch_mask, unsigned char wide_mask)
+{
+       struct txx9ndfmc_platform_data plat_data = {
+               .shift = 1,
+               .gbus_clock = txx9_gbus_clock,
+               .hold = hold,
+               .spw = spw,
+               .flags = NDFMC_PLAT_FLAG_NO_RSTR | NDFMC_PLAT_FLAG_HOLDADD |
+                        NDFMC_PLAT_FLAG_DUMMYWRITE,
+               .ch_mask = ch_mask,
+               .wide_mask = wide_mask,
+       };
+       txx9_ndfmc_init(TX4939_NDFMC_REG & 0xfffffffffULL, &plat_data);
+}
+
 static void __init tx4939_stop_unused_modules(void)
 {
        __u64 pcfg, rst = 0, ckd = 0;
index 547ff2920bf084a20aab6dcdba001840a6ad6556..65d13df8878aabaafdb260dc87ef7d34f0a87108 100644 (file)
@@ -352,6 +352,8 @@ static void __init rbtx4938_device_init(void)
        rbtx4938_ne_init();
        tx4938_wdt_init();
        rbtx4938_mtd_init();
+       /* TC58DVM82A1FT: tDH=10ns, tWP=tRP=tREADID=35ns */
+       tx4938_ndfmc_init(10, 35);
        tx4938_ata_init(RBTX4938_IRQ_IOC_ATA, 0, 1);
        txx9_iocled_init(RBTX4938_LED_ADDR - IO_BASE, -1, 8, 1, "green", NULL);
 }
index 656603b85b711964db905fa41bc8b454306a60af..011e1e332f4797a26066105bb29ccc6315816dc5 100644 (file)
@@ -16,6 +16,9 @@
 #include <linux/leds.h>
 #include <linux/interrupt.h>
 #include <linux/smc91x.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/partitions.h>
+#include <linux/mtd/map.h>
 #include <asm/reboot.h>
 #include <asm/txx9/generic.h>
 #include <asm/txx9/pci.h>
@@ -282,6 +285,159 @@ static void rbtx4939_7segled_putc(unsigned int pos, unsigned char val)
        __rbtx4939_7segled_putc(pos, val);
 }
 
+#if defined(CONFIG_MTD_RBTX4939) || defined(CONFIG_MTD_RBTX4939_MODULE)
+/* special mapping for boot rom */
+static unsigned long rbtx4939_flash_fixup_ofs(unsigned long ofs)
+{
+       u8 bdipsw = readb(rbtx4939_bdipsw_addr) & 0x0f;
+       unsigned char shift;
+
+       if (bdipsw & 8) {
+               /* BOOT Mode: USER ROM1 / USER ROM2 */
+               shift = bdipsw & 3;
+               /* rotate A[23:22] */
+               return (ofs & ~0xc00000) | ((((ofs >> 22) + shift) & 3) << 22);
+       }
+#ifdef __BIG_ENDIAN
+       if (bdipsw == 0)
+               /* BOOT Mode: Monitor ROM */
+               ofs ^= 0x400000;        /* swap A[22] */
+#endif
+       return ofs;
+}
+
+static map_word rbtx4939_flash_read16(struct map_info *map, unsigned long ofs)
+{
+       map_word r;
+
+       ofs = rbtx4939_flash_fixup_ofs(ofs);
+       r.x[0] = __raw_readw(map->virt + ofs);
+       return r;
+}
+
+static void rbtx4939_flash_write16(struct map_info *map, const map_word datum,
+                                  unsigned long ofs)
+{
+       ofs = rbtx4939_flash_fixup_ofs(ofs);
+       __raw_writew(datum.x[0], map->virt + ofs);
+       mb();   /* see inline_map_write() in mtd/map.h */
+}
+
+static void rbtx4939_flash_copy_from(struct map_info *map, void *to,
+                                    unsigned long from, ssize_t len)
+{
+       u8 bdipsw = readb(rbtx4939_bdipsw_addr) & 0x0f;
+       unsigned char shift;
+       ssize_t curlen;
+
+       from += (unsigned long)map->virt;
+       if (bdipsw & 8) {
+               /* BOOT Mode: USER ROM1 / USER ROM2 */
+               shift = bdipsw & 3;
+               while (len) {
+                       curlen = min_t(unsigned long, len,
+                                    0x400000 - (from & (0x400000 - 1)));
+                       memcpy(to,
+                              (void *)((from & ~0xc00000) |
+                                       ((((from >> 22) + shift) & 3) << 22)),
+                              curlen);
+                       len -= curlen;
+                       from += curlen;
+                       to += curlen;
+               }
+               return;
+       }
+#ifdef __BIG_ENDIAN
+       if (bdipsw == 0) {
+               /* BOOT Mode: Monitor ROM */
+               while (len) {
+                       curlen = min_t(unsigned long, len,
+                                    0x400000 - (from & (0x400000 - 1)));
+                       memcpy(to, (void *)(from ^ 0x400000), curlen);
+                       len -= curlen;
+                       from += curlen;
+                       to += curlen;
+               }
+               return;
+       }
+#endif
+       memcpy(to, (void *)from, len);
+}
+
+static void rbtx4939_flash_map_init(struct map_info *map)
+{
+       map->read = rbtx4939_flash_read16;
+       map->write = rbtx4939_flash_write16;
+       map->copy_from = rbtx4939_flash_copy_from;
+}
+
+static void __init rbtx4939_mtd_init(void)
+{
+       static struct {
+               struct platform_device dev;
+               struct resource res;
+               struct rbtx4939_flash_data data;
+       } pdevs[4];
+       int i;
+       static char names[4][8];
+       static struct mtd_partition parts[4];
+       struct rbtx4939_flash_data *boot_pdata = &pdevs[0].data;
+       u8 bdipsw = readb(rbtx4939_bdipsw_addr) & 0x0f;
+
+       if (bdipsw & 8) {
+               /* BOOT Mode: USER ROM1 / USER ROM2 */
+               boot_pdata->nr_parts = 4;
+               for (i = 0; i < boot_pdata->nr_parts; i++) {
+                       sprintf(names[i], "img%d", 4 - i);
+                       parts[i].name = names[i];
+                       parts[i].size = 0x400000;
+                       parts[i].offset = MTDPART_OFS_NXTBLK;
+               }
+       } else if (bdipsw == 0) {
+               /* BOOT Mode: Monitor ROM */
+               boot_pdata->nr_parts = 2;
+               strcpy(names[0], "big");
+               strcpy(names[1], "little");
+               for (i = 0; i < boot_pdata->nr_parts; i++) {
+                       parts[i].name = names[i];
+                       parts[i].size = 0x400000;
+                       parts[i].offset = MTDPART_OFS_NXTBLK;
+               }
+       } else {
+               /* BOOT Mode: ROM Emulator */
+               boot_pdata->nr_parts = 2;
+               parts[0].name = "boot";
+               parts[0].offset = 0xc00000;
+               parts[0].size = 0x400000;
+               parts[1].name = "user";
+               parts[1].offset = 0;
+               parts[1].size = 0xc00000;
+       }
+       boot_pdata->parts = parts;
+       boot_pdata->map_init = rbtx4939_flash_map_init;
+
+       for (i = 0; i < ARRAY_SIZE(pdevs); i++) {
+               struct resource *r = &pdevs[i].res;
+               struct platform_device *dev = &pdevs[i].dev;
+
+               r->start = 0x1f000000 - i * 0x1000000;
+               r->end = r->start + 0x1000000 - 1;
+               r->flags = IORESOURCE_MEM;
+               pdevs[i].data.width = 2;
+               dev->num_resources = 1;
+               dev->resource = r;
+               dev->id = i;
+               dev->name = "rbtx4939-flash";
+               dev->dev.platform_data = &pdevs[i].data;
+               platform_device_register(dev);
+       }
+}
+#else
+static void __init rbtx4939_mtd_init(void)
+{
+}
+#endif
+
 static void __init rbtx4939_arch_init(void)
 {
        rbtx4939_pci_setup();
@@ -333,6 +489,11 @@ static void __init rbtx4939_device_init(void)
            platform_device_add_data(pdev, &smc_pdata, sizeof(smc_pdata)) ||
            platform_device_add(pdev))
                platform_device_put(pdev);
+       rbtx4939_mtd_init();
+       /* TC58DVM82A1FT: tDH=10ns, tWP=tRP=tREADID=35ns */
+       tx4939_ndfmc_init(10, 35,
+                         (1 << 1) | (1 << 2),
+                         (1 << 2)); /* ch1:8bit, ch2:16bit */
        rbtx4939_led_setup();
        tx4939_wdt_init();
        tx4939_ata_init();
index 28b1a95257cdfd8d5986f971b8cdf318d2808537..19aa72301c837348b7d04c91c0b167faa232569c 100644 (file)
                upm@3,0 {
                        #address-cells = <0>;
                        #size-cells = <0>;
-                       compatible = "fsl,upm-nand";
+                       compatible = "tqc,tqm8548-upm-nand", "fsl,upm-nand";
                        reg = <3 0x0 0x800>;
                        fsl,upm-addr-offset = <0x10>;
                        fsl,upm-cmd-offset = <0x08>;
+                       /* Micron MT29F8G08FAB multi-chip device */
+                       fsl,upm-addr-line-cs-offsets = <0x0 0x200>;
+                       fsl,upm-wait-flags = <0x5>;
                        chip-delay = <25>; // in micro-seconds
 
                        nand@0 {
 
                                partition@0 {
                                            label = "fs";
-                                           reg = <0x00000000 0x01000000>;
+                                           reg = <0x00000000 0x10000000>;
                                };
                        };
                };
index 826fb622cd3c4525f3f897401302016a97b8d53d..49145a04fc6c2b892535036ec2b1de89a76e38b7 100644 (file)
                upm@3,0 {
                        #address-cells = <0>;
                        #size-cells = <0>;
-                       compatible = "fsl,upm-nand";
+                       compatible = "tqc,tqm8548-upm-nand", "fsl,upm-nand";
                        reg = <3 0x0 0x800>;
                        fsl,upm-addr-offset = <0x10>;
                        fsl,upm-cmd-offset = <0x08>;
+                       /* Micron MT29F8G08FAB multi-chip device */
+                       fsl,upm-addr-line-cs-offsets = <0x0 0x200>;
+                       fsl,upm-wait-flags = <0x5>;
                        chip-delay = <25>; // in micro-seconds
 
                        nand@0 {
 
                                partition@0 {
                                            label = "fs";
-                                           reg = <0x00000000 0x01000000>;
+                                           reg = <0x00000000 0x10000000>;
                                };
                        };
                };
index 0494ee55920f7c55f6aac6c2447a0cd7c2ad76c0..dceb8d1a843d7ccd63f46fc768c6352f2fb92b9c 100644 (file)
@@ -150,7 +150,7 @@ int fsl_upm_run_pattern(struct fsl_upm *upm, void __iomem *io_base, u32 mar)
 
        spin_lock_irqsave(&fsl_lbc_lock, flags);
 
-       out_be32(&fsl_lbc_regs->mar, mar << (32 - upm->width));
+       out_be32(&fsl_lbc_regs->mar, mar);
 
        switch (upm->width) {
        case 8:
index 4521b1ecce452607987b3d32c6251264de02d2b8..82d1e4de475bba283870d8a590416f9fc55161a8 100644 (file)
@@ -4,7 +4,7 @@
 
 # Core functionality.
 obj-$(CONFIG_MTD)              += mtd.o
-mtd-y                          := mtdcore.o mtdsuper.o
+mtd-y                          := mtdcore.o mtdsuper.o mtdbdi.o
 mtd-$(CONFIG_MTD_PARTITIONS)   += mtdpart.o
 
 obj-$(CONFIG_MTD_CONCAT)       += mtdconcat.o
index ecf170b55c3245650c220e1cf7bb75606ade0e84..6697a1ec72d067ec07a42c15986740ea33169095 100644 (file)
@@ -44,8 +44,6 @@ struct ar7_bin_rec {
        unsigned int address;
 };
 
-static struct mtd_partition ar7_parts[AR7_PARTS];
-
 static int create_mtd_partitions(struct mtd_info *master,
                                 struct mtd_partition **pparts,
                                 unsigned long origin)
@@ -57,7 +55,11 @@ static int create_mtd_partitions(struct mtd_info *master,
        unsigned int root_offset = ROOT_OFFSET;
 
        int retries = 10;
+       struct mtd_partition *ar7_parts;
 
+       ar7_parts = kzalloc(sizeof(*ar7_parts) * AR7_PARTS, GFP_KERNEL);
+       if (!ar7_parts)
+               return -ENOMEM;
        ar7_parts[0].name = "loader";
        ar7_parts[0].offset = 0;
        ar7_parts[0].size = master->erasesize;
index f5ab6fa1057bdc422816c8f5e317de466a65107f..c240454fd113d757794141a160eaaac60015c036 100644 (file)
@@ -1236,10 +1236,14 @@ static int inval_cache_and_wait_for_operation(
                        remove_wait_queue(&chip->wq, &wait);
                        spin_lock(chip->mutex);
                }
-               if (chip->erase_suspended || chip->write_suspended)  {
-                       /* Suspend has occured while sleep: reset timeout */
+               if (chip->erase_suspended && chip_state == FL_ERASING)  {
+                       /* Erase suspend occured while sleep: reset timeout */
                        timeo = reset_timeo;
                        chip->erase_suspended = 0;
+               }
+               if (chip->write_suspended && chip_state == FL_WRITING)  {
+                       /* Write suspend occured while sleep: reset timeout */
+                       timeo = reset_timeo;
                        chip->write_suspended = 0;
                }
        }
index 94bb61e19047764ae06dcf91408946219869fa06..61ea833e09086e631024f298575678ff3f123ea3 100644 (file)
@@ -282,6 +282,16 @@ static void fixup_s29gl032n_sectors(struct mtd_info *mtd, void *param)
        }
 }
 
+static void fixup_M29W128G_write_buffer(struct mtd_info *mtd, void *param)
+{
+       struct map_info *map = mtd->priv;
+       struct cfi_private *cfi = map->fldrv_priv;
+       if (cfi->cfiq->BufWriteTimeoutTyp) {
+               pr_warning("Don't use write buffer on ST flash M29W128G\n");
+               cfi->cfiq->BufWriteTimeoutTyp = 0;
+       }
+}
+
 static struct cfi_fixup cfi_fixup_table[] = {
        { CFI_MFR_ATMEL, CFI_ID_ANY, fixup_convert_atmel_pri, NULL },
 #ifdef AMD_BOOTLOC_BUG
@@ -298,6 +308,7 @@ static struct cfi_fixup cfi_fixup_table[] = {
        { CFI_MFR_AMD, 0x1301, fixup_s29gl064n_sectors, NULL, },
        { CFI_MFR_AMD, 0x1a00, fixup_s29gl032n_sectors, NULL, },
        { CFI_MFR_AMD, 0x1a01, fixup_s29gl032n_sectors, NULL, },
+       { CFI_MFR_ST,  0x227E, fixup_M29W128G_write_buffer, NULL, },
 #if !FORCE_WORD_WRITE
        { CFI_MFR_ANY, CFI_ID_ANY, fixup_use_write_buffers, NULL, },
 #endif
index 2f3f2f719ba497026a7a6f304fdcab4d03bbe5bd..e824b9b9b05619b18a9348ffa74c8586b865842a 100644 (file)
 #define SST39LF800     0x2781
 #define SST39LF160     0x2782
 #define SST39VF1601    0x234b
+#define SST39VF3201    0x235b
 #define SST39LF512     0x00D4
 #define SST39LF010     0x00D5
 #define SST39LF020     0x00D6
@@ -1489,6 +1490,21 @@ static const struct amd_flash_info jedec_table[] = {
                        ERASEINFO(0x1000,256),
                        ERASEINFO(0x1000,256)
                }
+       }, {
+               .mfr_id         = MANUFACTURER_SST,     /* should be CFI */
+               .dev_id         = SST39VF3201,
+               .name           = "SST 39VF3201",
+               .devtypes       = CFI_DEVICETYPE_X16,
+               .uaddr          = MTD_UADDR_0xAAAA_0x5555,
+               .dev_size       = SIZE_4MiB,
+               .cmd_set        = P_ID_AMD_STD,
+               .nr_regions     = 4,
+               .regions        = {
+                       ERASEINFO(0x1000,256),
+                       ERASEINFO(0x1000,256),
+                       ERASEINFO(0x1000,256),
+                       ERASEINFO(0x1000,256)
+               }
        }, {
                .mfr_id         = MANUFACTURER_SST,
                .dev_id         = SST36VF3203,
index 072dd8abf33a726620bd0f9299c3005632ce4ee4..6bdc50c727e7b1aad9bea45c81e18959fc7f79f6 100644 (file)
@@ -21,6 +21,8 @@ static int mapram_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch
 static int mapram_erase (struct mtd_info *, struct erase_info *);
 static void mapram_nop (struct mtd_info *);
 static struct mtd_info *map_ram_probe(struct map_info *map);
+static unsigned long mapram_unmapped_area(struct mtd_info *, unsigned long,
+                                         unsigned long, unsigned long);
 
 
 static struct mtd_chip_driver mapram_chipdrv = {
@@ -64,6 +66,7 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
        mtd->type = MTD_RAM;
        mtd->size = map->size;
        mtd->erase = mapram_erase;
+       mtd->get_unmapped_area = mapram_unmapped_area;
        mtd->read = mapram_read;
        mtd->write = mapram_write;
        mtd->sync = mapram_nop;
@@ -79,6 +82,20 @@ static struct mtd_info *map_ram_probe(struct map_info *map)
 }
 
 
+/*
+ * Allow NOMMU mmap() to directly map the device (if not NULL)
+ * - return the address to which the offset maps
+ * - return -ENOSYS to indicate refusal to do the mapping
+ */
+static unsigned long mapram_unmapped_area(struct mtd_info *mtd,
+                                         unsigned long len,
+                                         unsigned long offset,
+                                         unsigned long flags)
+{
+       struct map_info *map = mtd->priv;
+       return (unsigned long) map->virt + offset;
+}
+
 static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
 {
        struct map_info *map = mtd->priv;
index c76d6e5f47ee305868da2bc6b382c205107b2247..076090a67b9088319b676ce58fc7cf82263d6a85 100644 (file)
@@ -20,6 +20,8 @@ static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_ch
 static void maprom_nop (struct mtd_info *);
 static struct mtd_info *map_rom_probe(struct map_info *map);
 static int maprom_erase (struct mtd_info *mtd, struct erase_info *info);
+static unsigned long maprom_unmapped_area(struct mtd_info *, unsigned long,
+                                         unsigned long, unsigned long);
 
 static struct mtd_chip_driver maprom_chipdrv = {
        .probe  = map_rom_probe,
@@ -40,6 +42,7 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
        mtd->name = map->name;
        mtd->type = MTD_ROM;
        mtd->size = map->size;
+       mtd->get_unmapped_area = maprom_unmapped_area;
        mtd->read = maprom_read;
        mtd->write = maprom_write;
        mtd->sync = maprom_nop;
@@ -53,6 +56,20 @@ static struct mtd_info *map_rom_probe(struct map_info *map)
 }
 
 
+/*
+ * Allow NOMMU mmap() to directly map the device (if not NULL)
+ * - return the address to which the offset maps
+ * - return -ENOSYS to indicate refusal to do the mapping
+ */
+static unsigned long maprom_unmapped_area(struct mtd_info *mtd,
+                                         unsigned long len,
+                                         unsigned long offset,
+                                         unsigned long flags)
+{
+       struct map_info *map = mtd->priv;
+       return (unsigned long) map->virt + offset;
+}
+
 static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf)
 {
        struct map_info *map = mtd->priv;
index 50a340388e742024945b6011490c760d0de4dcad..5011fa73f91886302135ea08904908c2c6f89461 100644 (file)
@@ -335,7 +335,11 @@ static int parse_cmdline_partitions(struct mtd_info *master,
                                }
                                offset += part->parts[i].size;
                        }
-                       *pparts = part->parts;
+                       *pparts = kmemdup(part->parts,
+                                       sizeof(*part->parts) * part->num_parts,
+                                       GFP_KERNEL);
+                       if (!*pparts)
+                               return -ENOMEM;
                        return part->num_parts;
                }
        }
index 50de839c77a9edf410747ba963ae4c3c93d49156..5bf5f460e1327828c203710ffee7041044fb14b2 100644 (file)
@@ -10,7 +10,6 @@
 #include <asm/errno.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
-#include <linux/miscdevice.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/sched.h>
index e32c568c11450e1c8041f4e28604ed1b72654d00..0990f7803628605fba21b083c0e426ae2697628c 100644 (file)
@@ -10,7 +10,6 @@
 #include <asm/errno.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
-#include <linux/miscdevice.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
index d853f891b586d65e82254d44716f3cd78de47005..719b2915dc3ae4134fc5ffc4588012956c0d70a8 100644 (file)
@@ -14,7 +14,6 @@
 #include <asm/errno.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
-#include <linux/miscdevice.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
index 874e51b110a2d4cfbdadef073e146aab68040bdc..a19cda52da5c15804cdf50aa594f41dc4f27b433 100644 (file)
@@ -26,7 +26,6 @@
 #include <asm/errno.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
-#include <linux/miscdevice.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
index 7c3fc766dcf17b4b6cfcd2dec41d7220a75a03be..8185b1f3e5e632d9dcae772b25e1f83104f39b5d 100644 (file)
 #define FAST_READ_DUMMY_BYTE 0
 #endif
 
-#ifdef CONFIG_MTD_PARTITIONS
-#define        mtd_has_partitions()    (1)
-#else
-#define        mtd_has_partitions()    (0)
-#endif
-
 /****************************************************************************/
 
 struct m25p {
@@ -678,6 +672,8 @@ static int __devinit m25p_probe(struct spi_device *spi)
                flash->mtd.erasesize = info->sector_size;
        }
 
+       flash->mtd.dev.parent = &spi->dev;
+
        dev_info(&spi->dev, "%s (%lld Kbytes)\n", info->name,
                        (long long)flash->mtd.size >> 10);
 
@@ -708,12 +704,13 @@ static int __devinit m25p_probe(struct spi_device *spi)
                struct mtd_partition    *parts = NULL;
                int                     nr_parts = 0;
 
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-               static const char *part_probes[] = { "cmdlinepart", NULL, };
+               if (mtd_has_cmdlinepart()) {
+                       static const char *part_probes[]
+                                       = { "cmdlinepart", NULL, };
 
-               nr_parts = parse_mtd_partitions(&flash->mtd,
-                               part_probes, &parts, 0);
-#endif
+                       nr_parts = parse_mtd_partitions(&flash->mtd,
+                                       part_probes, &parts, 0);
+               }
 
                if (nr_parts <= 0 && data && data->parts) {
                        parts = data->parts;
index 6d9f810565c84c9b8a6c86ed51dc1e1bd07d7717..62dee54af0a58bfc4f1efe8cf8d8f6096ac95c0c 100644 (file)
@@ -98,12 +98,6 @@ struct dataflash {
        struct mtd_info         mtd;
 };
 
-#ifdef CONFIG_MTD_PARTITIONS
-#define        mtd_has_partitions()    (1)
-#else
-#define        mtd_has_partitions()    (0)
-#endif
-
 /* ......................................................................... */
 
 /*
@@ -670,6 +664,8 @@ add_dataflash_otp(struct spi_device *spi, char *name,
        device->write = dataflash_write;
        device->priv = priv;
 
+       device->dev.parent = &spi->dev;
+
        if (revision >= 'c')
                otp_tag = otp_setup(device, revision);
 
@@ -682,11 +678,13 @@ add_dataflash_otp(struct spi_device *spi, char *name,
                struct mtd_partition    *parts;
                int                     nr_parts = 0;
 
-#ifdef CONFIG_MTD_CMDLINE_PARTS
-               static const char *part_probes[] = { "cmdlinepart", NULL, };
+               if (mtd_has_cmdlinepart()) {
+                       static const char *part_probes[]
+                                       = { "cmdlinepart", NULL, };
 
-               nr_parts = parse_mtd_partitions(device, part_probes, &parts, 0);
-#endif
+                       nr_parts = parse_mtd_partitions(device,
+                                       part_probes, &parts, 0);
+               }
 
                if (nr_parts <= 0 && pdata && pdata->parts) {
                        parts = pdata->parts;
index 3aaca88847d39cec5421a072b13609bd25796cda..fce5ff7589aa7e0591601041d82238878eca38b7 100644 (file)
@@ -65,6 +65,19 @@ static void ram_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
 {
 }
 
+/*
+ * Allow NOMMU mmap() to directly map the device (if not NULL)
+ * - return the address to which the offset maps
+ * - return -ENOSYS to indicate refusal to do the mapping
+ */
+static unsigned long ram_get_unmapped_area(struct mtd_info *mtd,
+                                          unsigned long len,
+                                          unsigned long offset,
+                                          unsigned long flags)
+{
+       return (unsigned long) mtd->priv + offset;
+}
+
 static int ram_read(struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, u_char *buf)
 {
@@ -116,6 +129,7 @@ int mtdram_init_device(struct mtd_info *mtd, void *mapped_address,
        mtd->erase = ram_erase;
        mtd->point = ram_point;
        mtd->unpoint = ram_unpoint;
+       mtd->get_unmapped_area = ram_get_unmapped_area;
        mtd->read = ram_read;
        mtd->write = ram_write;
 
index f751dd97c5493a1a8e732e8745c759fad56f6cfa..32e82aef3e5392550012b694e5dd58ffd71d6d1d 100644 (file)
@@ -28,7 +28,6 @@
 #include <asm/errno.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
-#include <linux/miscdevice.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
diff --git a/drivers/mtd/internal.h b/drivers/mtd/internal.h
new file mode 100644 (file)
index 0000000..c658fe7
--- /dev/null
@@ -0,0 +1,17 @@
+/* Internal MTD definitions
+ *
+ * Copyright Â© 2006 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+/*
+ * mtdbdi.c
+ */
+extern struct backing_dev_info mtd_bdi_unmappable;
+extern struct backing_dev_info mtd_bdi_ro_mappable;
+extern struct backing_dev_info mtd_bdi_rw_mappable;
index 729f899a5cd5d71725ab137715d2c5b1c8a45789..82923bd2d9c5444166c53552a409f27ebb646e50 100644 (file)
@@ -529,12 +529,6 @@ config MTD_DMV182
         help
           Map driver for Dy-4 SVME/DMV-182 board.
 
-config MTD_SHARP_SL
-       tristate "ROM mapped on Sharp SL Series"
-       depends on ARCH_PXA
-       help
-         This enables access to the flash chip on the Sharp SL Series of PDAs.
-
 config MTD_INTEL_VR_NOR
        tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0"
        depends on PCI
@@ -542,6 +536,12 @@ config MTD_INTEL_VR_NOR
          Map driver for a NOR flash bank located on the Expansion Bus of the
          Intel Vermilion Range chipset.
 
+config MTD_RBTX4939
+       tristate "Map driver for RBTX4939 board"
+       depends on TOSHIBA_RBTX4939 && MTD_CFI && MTD_COMPLEX_MAPPINGS
+       help
+         Map driver for NOR flash chips on RBTX4939 board.
+
 config MTD_PLATRAM
        tristate "Map driver for platform device RAM (mtd-ram)"
        select MTD_RAM
index 26b28a7a90b5e19a57baf21502d2dd94fdc349a7..2dbc1bec848808559f4bc52995eda5ee06fb8dad 100644 (file)
@@ -56,9 +56,9 @@ obj-$(CONFIG_MTD_IXP4XX)      += ixp4xx.o
 obj-$(CONFIG_MTD_IXP2000)      += ixp2000.o
 obj-$(CONFIG_MTD_WRSBC8260)    += wr_sbc82xx_flash.o
 obj-$(CONFIG_MTD_DMV182)       += dmv182.o
-obj-$(CONFIG_MTD_SHARP_SL)     += sharpsl-flash.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
 obj-$(CONFIG_MTD_VMU)          += vmu-flash.o
index 7e50e9b1b781fa964893d2a44b017c6a84facfde..a24478102b118920e1dfd423fa082fa432576345 100644 (file)
@@ -115,6 +115,8 @@ static int __init omapflash_probe(struct platform_device *pdev)
        }
        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)
index 229718222db710e27fc980fb68ca287d9553e99d..29a9011573522abbe7d8b021ddec46182dcfb2d2 100644 (file)
@@ -147,6 +147,7 @@ static int physmap_flash_probe(struct platform_device *dev)
                        devices_found++;
                }
                info->mtd[i]->owner = THIS_MODULE;
+               info->mtd[i]->dev.parent = &dev->dev;
        }
 
        if (devices_found == 1) {
index fbf0ca939d723ba7121f2ca14c4d85a2a603a0ef..c83a60fada530e1a73f812bef5c44d18e7432259 100644 (file)
@@ -219,6 +219,7 @@ static int __devinit of_flash_probe(struct of_device *dev,
                goto err_out;
        }
        info->mtd->owner = THIS_MODULE;
+       info->mtd->dev.parent = &dev->dev;
 
 #ifdef CONFIG_MTD_PARTITIONS
        /* First look for RedBoot table or partitions on the command
index e7dd9c8a965e2fc86d32b98eaa060657d0819724..49c9ece764770e6f8cfddb9d46fd26b94d22bb47 100644 (file)
@@ -224,6 +224,7 @@ static int platram_probe(struct platform_device *pdev)
        }
 
        info->mtd->owner = THIS_MODULE;
+       info->mtd->dev.parent = &pdev->dev;
 
        platram_setrw(info, PLATRAM_RW);
 
diff --git a/drivers/mtd/maps/rbtx4939-flash.c b/drivers/mtd/maps/rbtx4939-flash.c
new file mode 100644 (file)
index 0000000..d39f0ad
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * rbtx4939-flash (based on physmap.c)
+ *
+ * This is a simplified physmap driver with map_init callback function.
+ *
+ * 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.
+ *
+ * Copyright (C) 2009 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
+ */
+
+#include <linux/module.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/device.h>
+#include <linux/platform_device.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/map.h>
+#include <linux/mtd/partitions.h>
+#include <asm/txx9/rbtx4939.h>
+
+struct rbtx4939_flash_info {
+       struct mtd_info *mtd;
+       struct map_info map;
+#ifdef CONFIG_MTD_PARTITIONS
+       int nr_parts;
+       struct mtd_partition *parts;
+#endif
+};
+
+static int rbtx4939_flash_remove(struct platform_device *dev)
+{
+       struct rbtx4939_flash_info *info;
+
+       info = platform_get_drvdata(dev);
+       if (!info)
+               return 0;
+       platform_set_drvdata(dev, NULL);
+
+       if (info->mtd) {
+#ifdef CONFIG_MTD_PARTITIONS
+               struct rbtx4939_flash_data *pdata = dev->dev.platform_data;
+
+               if (info->nr_parts) {
+                       del_mtd_partitions(info->mtd);
+                       kfree(info->parts);
+               } else if (pdata->nr_parts)
+                       del_mtd_partitions(info->mtd);
+               else
+                       del_mtd_device(info->mtd);
+#else
+               del_mtd_device(info->mtd);
+#endif
+               map_destroy(info->mtd);
+       }
+       return 0;
+}
+
+static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probe_types[] = { "cmdlinepart", NULL };
+#endif
+
+static int rbtx4939_flash_probe(struct platform_device *dev)
+{
+       struct rbtx4939_flash_data *pdata;
+       struct rbtx4939_flash_info *info;
+       struct resource *res;
+       const char **probe_type;
+       int err = 0;
+       unsigned long size;
+
+       pdata = dev->dev.platform_data;
+       if (!pdata)
+               return -ENODEV;
+
+       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENODEV;
+       info = devm_kzalloc(&dev->dev, sizeof(struct rbtx4939_flash_info),
+                           GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+
+       platform_set_drvdata(dev, info);
+
+       size = resource_size(res);
+       pr_notice("rbtx4939 platform flash device: %pR\n", res);
+
+       if (!devm_request_mem_region(&dev->dev, res->start, size,
+                                    dev_name(&dev->dev)))
+               return -EBUSY;
+
+       info->map.name = dev_name(&dev->dev);
+       info->map.phys = res->start;
+       info->map.size = size;
+       info->map.bankwidth = pdata->width;
+
+       info->map.virt = devm_ioremap(&dev->dev, info->map.phys, size);
+       if (!info->map.virt)
+               return -EBUSY;
+
+       if (pdata->map_init)
+               (*pdata->map_init)(&info->map);
+       else
+               simple_map_init(&info->map);
+
+       probe_type = rom_probe_types;
+       for (; !info->mtd && *probe_type; probe_type++)
+               info->mtd = do_map_probe(*probe_type, &info->map);
+       if (!info->mtd) {
+               dev_err(&dev->dev, "map_probe failed\n");
+               err = -ENXIO;
+               goto err_out;
+       }
+       info->mtd->owner = THIS_MODULE;
+       if (err)
+               goto err_out;
+
+#ifdef CONFIG_MTD_PARTITIONS
+       err = parse_mtd_partitions(info->mtd, part_probe_types,
+                               &info->parts, 0);
+       if (err > 0) {
+               add_mtd_partitions(info->mtd, info->parts, err);
+               info->nr_parts = err;
+               return 0;
+       }
+
+       if (pdata->nr_parts) {
+               pr_notice("Using rbtx4939 partition information\n");
+               add_mtd_partitions(info->mtd, pdata->parts, pdata->nr_parts);
+               return 0;
+       }
+#endif
+
+       add_mtd_device(info->mtd);
+       return 0;
+
+err_out:
+       rbtx4939_flash_remove(dev);
+       return err;
+}
+
+#ifdef CONFIG_PM
+static int rbtx4939_flash_suspend(struct platform_device *dev,
+                                 pm_message_t state)
+{
+       struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
+
+       if (info->mtd->suspend)
+               return info->mtd->suspend(info->mtd);
+       return 0;
+}
+
+static int rbtx4939_flash_resume(struct platform_device *dev)
+{
+       struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
+
+       if (info->mtd->resume)
+               info->mtd->resume(info->mtd);
+       return 0;
+}
+
+static void rbtx4939_flash_shutdown(struct platform_device *dev)
+{
+       struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
+
+       if (info->mtd->suspend && info->mtd->resume)
+               if (info->mtd->suspend(info->mtd) == 0)
+                       info->mtd->resume(info->mtd);
+}
+#else
+#define rbtx4939_flash_suspend NULL
+#define rbtx4939_flash_resume NULL
+#define rbtx4939_flash_shutdown NULL
+#endif
+
+static struct platform_driver rbtx4939_flash_driver = {
+       .probe          = rbtx4939_flash_probe,
+       .remove         = rbtx4939_flash_remove,
+       .suspend        = rbtx4939_flash_suspend,
+       .resume         = rbtx4939_flash_resume,
+       .shutdown       = rbtx4939_flash_shutdown,
+       .driver         = {
+               .name   = "rbtx4939-flash",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init rbtx4939_flash_init(void)
+{
+       return platform_driver_register(&rbtx4939_flash_driver);
+}
+
+static void __exit rbtx4939_flash_exit(void)
+{
+       platform_driver_unregister(&rbtx4939_flash_driver);
+}
+
+module_init(rbtx4939_flash_init);
+module_exit(rbtx4939_flash_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("RBTX4939 MTD map driver");
+MODULE_ALIAS("platform:rbtx4939-flash");
index 8f57b6f40aa2d3cfef54db252a3b9d63f9d47974..05e9362dc7f0fdd252d7c7ecb5d6922cf000de98 100644 (file)
@@ -351,7 +351,7 @@ sa1100_setup_mtd(struct platform_device *pdev, struct flash_platform_data *plat)
 
 static const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL };
 
-static int __init sa1100_mtd_probe(struct platform_device *pdev)
+static int __devinit sa1100_mtd_probe(struct platform_device *pdev)
 {
        struct flash_platform_data *plat = pdev->dev.platform_data;
        struct mtd_partition *parts;
diff --git a/drivers/mtd/maps/sharpsl-flash.c b/drivers/mtd/maps/sharpsl-flash.c
deleted file mode 100644 (file)
index b392f09..0000000
+++ /dev/null
@@ -1,116 +0,0 @@
-/*
- * sharpsl-flash.c
- *
- * Copyright (C) 2001 Lineo Japan, Inc.
- * Copyright (C) 2002  SHARP
- *
- * based on rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp
- *          Handle mapping of the flash on the RPX Lite and CLLF boards
- *
- * 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.
- *
- */
-
-#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>
-#include <asm/mach-types.h>
-
-#define WINDOW_ADDR 0x00000000
-#define WINDOW_SIZE 0x00800000
-#define BANK_WIDTH 2
-
-static struct mtd_info *mymtd;
-
-struct map_info sharpsl_map = {
-       .name = "sharpsl-flash",
-       .size = WINDOW_SIZE,
-       .bankwidth = BANK_WIDTH,
-       .phys = WINDOW_ADDR
-};
-
-static struct mtd_partition sharpsl_partitions[1] = {
-       {
-               name:           "Boot PROM Filesystem",
-       }
-};
-
-static int __init init_sharpsl(void)
-{
-       struct mtd_partition *parts;
-       int nb_parts = 0;
-       char *part_type = "static";
-
-       printk(KERN_NOTICE "Sharp SL series flash device: %x at %x\n",
-               WINDOW_SIZE, WINDOW_ADDR);
-       sharpsl_map.virt = ioremap(WINDOW_ADDR, WINDOW_SIZE);
-       if (!sharpsl_map.virt) {
-               printk("Failed to ioremap\n");
-               return -EIO;
-       }
-
-       simple_map_init(&sharpsl_map);
-
-       mymtd = do_map_probe("map_rom", &sharpsl_map);
-       if (!mymtd) {
-               iounmap(sharpsl_map.virt);
-               return -ENXIO;
-       }
-
-       mymtd->owner = THIS_MODULE;
-
-       if (machine_is_corgi() || machine_is_shepherd() || machine_is_husky()
-               || machine_is_poodle()) {
-               sharpsl_partitions[0].size=0x006d0000;
-               sharpsl_partitions[0].offset=0x00120000;
-       } else if (machine_is_tosa()) {
-               sharpsl_partitions[0].size=0x006a0000;
-               sharpsl_partitions[0].offset=0x00160000;
-       } else if (machine_is_spitz() || machine_is_akita() || machine_is_borzoi()) {
-               sharpsl_partitions[0].size=0x006b0000;
-               sharpsl_partitions[0].offset=0x00140000;
-       } else {
-               map_destroy(mymtd);
-               iounmap(sharpsl_map.virt);
-               return -ENODEV;
-       }
-
-       parts = sharpsl_partitions;
-       nb_parts = ARRAY_SIZE(sharpsl_partitions);
-
-       printk(KERN_NOTICE "Using %s partition definition\n", part_type);
-       add_mtd_partitions(mymtd, parts, nb_parts);
-
-       return 0;
-}
-
-static void __exit cleanup_sharpsl(void)
-{
-       if (mymtd) {
-               del_mtd_partitions(mymtd);
-               map_destroy(mymtd);
-       }
-       if (sharpsl_map.virt) {
-               iounmap(sharpsl_map.virt);
-               sharpsl_map.virt = 0;
-       }
-}
-
-module_init(init_sharpsl);
-module_exit(cleanup_sharpsl);
-
-MODULE_LICENSE("GPL");
-MODULE_AUTHOR("SHARP (Original: Arnold Christensen <AKC@pel.dk>)");
-MODULE_DESCRIPTION("MTD map driver for SHARP SL series");
index 1409f01406f6e7ebc356cf906ab4880f7719b1c1..a49a9c8f2cb1faffe5c16d782ea793839062dcc4 100644 (file)
@@ -286,6 +286,7 @@ int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new)
        gd->private_data = new;
        new->blkcore_priv = gd;
        gd->queue = tr->blkcore_priv->rq;
+       gd->driverfs_dev = new->mtd->dev.parent;
 
        if (new->readonly)
                set_disk_ro(gd, 1);
@@ -382,11 +383,12 @@ int register_mtd_blktrans(struct mtd_blktrans_ops *tr)
        tr->blkcore_priv->thread = kthread_run(mtd_blktrans_thread, tr,
                        "%sd", tr->name);
        if (IS_ERR(tr->blkcore_priv->thread)) {
+               int 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 PTR_ERR(tr->blkcore_priv->thread);
+               return ret;
        }
 
        INIT_LIST_HEAD(&tr->devs);
diff --git a/drivers/mtd/mtdbdi.c b/drivers/mtd/mtdbdi.c
new file mode 100644 (file)
index 0000000..5ca5aed
--- /dev/null
@@ -0,0 +1,43 @@
+/* MTD backing device capabilities
+ *
+ * Copyright Â© 2006 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/backing-dev.h>
+#include <linux/mtd/mtd.h>
+#include "internal.h"
+
+/*
+ * backing device capabilities for non-mappable devices (such as NAND flash)
+ * - permits private mappings, copies are taken of the data
+ */
+struct backing_dev_info mtd_bdi_unmappable = {
+       .capabilities   = BDI_CAP_MAP_COPY,
+};
+
+/*
+ * backing device capabilities for R/O mappable devices (such as ROM)
+ * - permits private mappings, copies are taken of the data
+ * - permits non-writable shared mappings
+ */
+struct backing_dev_info mtd_bdi_ro_mappable = {
+       .capabilities   = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
+                          BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP),
+};
+
+/*
+ * backing device capabilities for writable mappable devices (such as RAM)
+ * - permits private mappings, copies are taken of the data
+ * - permits non-writable shared mappings
+ */
+struct backing_dev_info mtd_bdi_rw_mappable = {
+       .capabilities   = (BDI_CAP_MAP_COPY | BDI_CAP_MAP_DIRECT |
+                          BDI_CAP_EXEC_MAP | BDI_CAP_READ_MAP |
+                          BDI_CAP_WRITE_MAP),
+};
index e9ec59e9a566ab2a7560e9a166cfaa506077703f..763d3f0a1f428104d7a630de7746a09bc9883bb9 100644 (file)
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/smp_lock.h>
+#include <linux/backing-dev.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/compatmac.h>
 
 #include <asm/uaccess.h>
 
-static struct class *mtd_class;
-
-static void mtd_notify_add(struct mtd_info* mtd)
-{
-       if (!mtd)
-               return;
-
-       device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2),
-                     NULL, "mtd%d", mtd->index);
-
-       device_create(mtd_class, NULL, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1),
-                     NULL, "mtd%dro", mtd->index);
-}
-
-static void mtd_notify_remove(struct mtd_info* mtd)
-{
-       if (!mtd)
-               return;
-
-       device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2));
-       device_destroy(mtd_class, MKDEV(MTD_CHAR_MAJOR, mtd->index*2+1));
-}
-
-static struct mtd_notifier notifier = {
-       .add    = mtd_notify_add,
-       .remove = mtd_notify_remove,
-};
 
 /*
  * Data structure to hold the pointer to the mtd device as well
@@ -107,12 +81,15 @@ static int mtd_open(struct inode *inode, struct file *file)
                goto out;
        }
 
-       if (MTD_ABSENT == mtd->type) {
+       if (mtd->type == MTD_ABSENT) {
                put_mtd_device(mtd);
                ret = -ENODEV;
                goto out;
        }
 
+       if (mtd->backing_dev_info)
+               file->f_mapping->backing_dev_info = mtd->backing_dev_info;
+
        /* You can't open it RW if it's not a writeable device */
        if ((file->f_mode & FMODE_WRITE) && !(mtd->flags & MTD_WRITEABLE)) {
                put_mtd_device(mtd);
@@ -781,6 +758,59 @@ static int mtd_ioctl(struct inode *inode, struct file *file,
        return ret;
 } /* memory_ioctl */
 
+/*
+ * try to determine where a shared mapping can be made
+ * - only supported for NOMMU at the moment (MMU can't doesn't copy private
+ *   mappings)
+ */
+#ifndef CONFIG_MMU
+static unsigned long mtd_get_unmapped_area(struct file *file,
+                                          unsigned long addr,
+                                          unsigned long len,
+                                          unsigned long pgoff,
+                                          unsigned long flags)
+{
+       struct mtd_file_info *mfi = file->private_data;
+       struct mtd_info *mtd = mfi->mtd;
+
+       if (mtd->get_unmapped_area) {
+               unsigned long offset;
+
+               if (addr != 0)
+                       return (unsigned long) -EINVAL;
+
+               if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
+                       return (unsigned long) -EINVAL;
+
+               offset = pgoff << PAGE_SHIFT;
+               if (offset > mtd->size - len)
+                       return (unsigned long) -EINVAL;
+
+               return mtd->get_unmapped_area(mtd, len, offset, flags);
+       }
+
+       /* can't map directly */
+       return (unsigned long) -ENOSYS;
+}
+#endif
+
+/*
+ * set up a mapping for shared memory segments
+ */
+static int mtd_mmap(struct file *file, struct vm_area_struct *vma)
+{
+#ifdef CONFIG_MMU
+       struct mtd_file_info *mfi = file->private_data;
+       struct mtd_info *mtd = mfi->mtd;
+
+       if (mtd->type == MTD_RAM || mtd->type == MTD_ROM)
+               return 0;
+       return -ENOSYS;
+#else
+       return vma->vm_flags & VM_SHARED ? 0 : -ENOSYS;
+#endif
+}
+
 static const struct file_operations mtd_fops = {
        .owner          = THIS_MODULE,
        .llseek         = mtd_lseek,
@@ -789,39 +819,36 @@ static const struct file_operations mtd_fops = {
        .ioctl          = mtd_ioctl,
        .open           = mtd_open,
        .release        = mtd_close,
+       .mmap           = mtd_mmap,
+#ifndef CONFIG_MMU
+       .get_unmapped_area = mtd_get_unmapped_area,
+#endif
 };
 
 static int __init init_mtdchar(void)
 {
-       if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) {
+       int status;
+
+       status = register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops);
+       if (status < 0) {
                printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n",
                       MTD_CHAR_MAJOR);
-               return -EAGAIN;
        }
 
-       mtd_class = class_create(THIS_MODULE, "mtd");
-
-       if (IS_ERR(mtd_class)) {
-               printk(KERN_ERR "Error creating mtd class.\n");
-               unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
-               return PTR_ERR(mtd_class);
-       }
-
-       register_mtd_user(&notifier);
-       return 0;
+       return status;
 }
 
 static void __exit cleanup_mtdchar(void)
 {
-       unregister_mtd_user(&notifier);
-       class_destroy(mtd_class);
        unregister_chrdev(MTD_CHAR_MAJOR, "mtd");
 }
 
 module_init(init_mtdchar);
 module_exit(cleanup_mtdchar);
 
+MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
 
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 MODULE_DESCRIPTION("Direct character-device access to MTD devices");
+MODULE_ALIAS_CHARDEV_MAJOR(MTD_CHAR_MAJOR);
index 3dbb1b38db666cf4294047ec8cac5f8610c19525..792b547786b8fdf6c4122f28240b1458c0d80f66 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/slab.h>
 #include <linux/sched.h>
 #include <linux/types.h>
+#include <linux/backing-dev.h>
 
 #include <linux/mtd/mtd.h>
 #include <linux/mtd/concat.h>
@@ -683,6 +684,40 @@ static int concat_block_markbad(struct mtd_info *mtd, loff_t ofs)
        return err;
 }
 
+/*
+ * try to support NOMMU mmaps on concatenated devices
+ * - we don't support subdev spanning as we can't guarantee it'll work
+ */
+static unsigned long concat_get_unmapped_area(struct mtd_info *mtd,
+                                             unsigned long len,
+                                             unsigned long offset,
+                                             unsigned long flags)
+{
+       struct mtd_concat *concat = CONCAT(mtd);
+       int i;
+
+       for (i = 0; i < concat->num_subdev; i++) {
+               struct mtd_info *subdev = concat->subdev[i];
+
+               if (offset >= subdev->size) {
+                       offset -= subdev->size;
+                       continue;
+               }
+
+               /* we've found the subdev over which the mapping will reside */
+               if (offset + len > subdev->size)
+                       return (unsigned long) -EINVAL;
+
+               if (subdev->get_unmapped_area)
+                       return subdev->get_unmapped_area(subdev, len, offset,
+                                                        flags);
+
+               break;
+       }
+
+       return (unsigned long) -ENOSYS;
+}
+
 /*
  * This function constructs a virtual MTD device by concatenating
  * num_devs MTD devices. A pointer to the new device object is
@@ -740,6 +775,8 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
 
        concat->mtd.ecc_stats.badblocks = subdev[0]->ecc_stats.badblocks;
 
+       concat->mtd.backing_dev_info = subdev[0]->backing_dev_info;
+
        concat->subdev[0] = subdev[0];
 
        for (i = 1; i < num_devs; i++) {
@@ -766,6 +803,15 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],      /* subdevices to c
                                concat->mtd.flags |=
                                    subdev[i]->flags & MTD_WRITEABLE;
                }
+
+               /* only permit direct mapping if the BDIs are all the same
+                * - copy-mapping is still permitted
+                */
+               if (concat->mtd.backing_dev_info !=
+                   subdev[i]->backing_dev_info)
+                       concat->mtd.backing_dev_info =
+                               &default_backing_dev_info;
+
                concat->mtd.size += subdev[i]->size;
                concat->mtd.ecc_stats.badblocks +=
                        subdev[i]->ecc_stats.badblocks;
@@ -796,6 +842,7 @@ struct mtd_info *mtd_concat_create(struct mtd_info *subdev[],       /* subdevices to c
        concat->mtd.unlock = concat_unlock;
        concat->mtd.suspend = concat_suspend;
        concat->mtd.resume = concat_resume;
+       concat->mtd.get_unmapped_area = concat_get_unmapped_area;
 
        /*
         * Combine the erase block size info of the subdevices:
index 76fe0a1e7a5e26534749a49436665b1d7ebcd086..fdd6ae8593972f78987f7d775c6fda93aa4661cd 100644 (file)
 #include <linux/proc_fs.h>
 
 #include <linux/mtd/mtd.h>
+#include "internal.h"
 
 #include "mtdcore.h"
 
+
+static struct class *mtd_class;
+
 /* These are exported solely for the purpose of mtd_blkdevs.c. You
    should not use them for _anything_ else */
 DEFINE_MUTEX(mtd_table_mutex);
@@ -32,6 +36,160 @@ EXPORT_SYMBOL_GPL(mtd_table);
 
 static LIST_HEAD(mtd_notifiers);
 
+
+#if defined(CONFIG_MTD_CHAR) || defined(CONFIG_MTD_CHAR_MODULE)
+#define MTD_DEVT(index) MKDEV(MTD_CHAR_MAJOR, (index)*2)
+#else
+#define MTD_DEVT(index) 0
+#endif
+
+/* REVISIT once MTD uses the driver model better, whoever allocates
+ * the mtd_info will probably want to use the release() hook...
+ */
+static void mtd_release(struct device *dev)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+
+       /* remove /dev/mtdXro node if needed */
+       if (MTD_DEVT(mtd->index))
+               device_destroy(mtd_class, MTD_DEVT(mtd->index) + 1);
+}
+
+static ssize_t mtd_type_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+       char *type;
+
+       switch (mtd->type) {
+       case MTD_ABSENT:
+               type = "absent";
+               break;
+       case MTD_RAM:
+               type = "ram";
+               break;
+       case MTD_ROM:
+               type = "rom";
+               break;
+       case MTD_NORFLASH:
+               type = "nor";
+               break;
+       case MTD_NANDFLASH:
+               type = "nand";
+               break;
+       case MTD_DATAFLASH:
+               type = "dataflash";
+               break;
+       case MTD_UBIVOLUME:
+               type = "ubi";
+               break;
+       default:
+               type = "unknown";
+       }
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", type);
+}
+static DEVICE_ATTR(type, S_IRUGO, mtd_type_show, NULL);
+
+static ssize_t mtd_flags_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+
+       return snprintf(buf, PAGE_SIZE, "0x%lx\n", (unsigned long)mtd->flags);
+
+}
+static DEVICE_ATTR(flags, S_IRUGO, mtd_flags_show, NULL);
+
+static ssize_t mtd_size_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%llu\n",
+               (unsigned long long)mtd->size);
+
+}
+static DEVICE_ATTR(size, S_IRUGO, mtd_size_show, NULL);
+
+static ssize_t mtd_erasesize_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->erasesize);
+
+}
+static DEVICE_ATTR(erasesize, S_IRUGO, mtd_erasesize_show, NULL);
+
+static ssize_t mtd_writesize_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->writesize);
+
+}
+static DEVICE_ATTR(writesize, S_IRUGO, mtd_writesize_show, NULL);
+
+static ssize_t mtd_oobsize_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%lu\n", (unsigned long)mtd->oobsize);
+
+}
+static DEVICE_ATTR(oobsize, S_IRUGO, mtd_oobsize_show, NULL);
+
+static ssize_t mtd_numeraseregions_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%u\n", mtd->numeraseregions);
+
+}
+static DEVICE_ATTR(numeraseregions, S_IRUGO, mtd_numeraseregions_show,
+       NULL);
+
+static ssize_t mtd_name_show(struct device *dev,
+               struct device_attribute *attr, char *buf)
+{
+       struct mtd_info *mtd = dev_to_mtd(dev);
+
+       return snprintf(buf, PAGE_SIZE, "%s\n", mtd->name);
+
+}
+static DEVICE_ATTR(name, S_IRUGO, mtd_name_show, NULL);
+
+static struct attribute *mtd_attrs[] = {
+       &dev_attr_type.attr,
+       &dev_attr_flags.attr,
+       &dev_attr_size.attr,
+       &dev_attr_erasesize.attr,
+       &dev_attr_writesize.attr,
+       &dev_attr_oobsize.attr,
+       &dev_attr_numeraseregions.attr,
+       &dev_attr_name.attr,
+       NULL,
+};
+
+struct attribute_group mtd_group = {
+       .attrs          = mtd_attrs,
+};
+
+struct attribute_group *mtd_groups[] = {
+       &mtd_group,
+       NULL,
+};
+
+static struct device_type mtd_devtype = {
+       .name           = "mtd",
+       .groups         = mtd_groups,
+       .release        = mtd_release,
+};
+
 /**
  *     add_mtd_device - register an MTD device
  *     @mtd: pointer to new MTD device info structure
@@ -40,12 +198,27 @@ static LIST_HEAD(mtd_notifiers);
  *     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.
  */
 
 int add_mtd_device(struct mtd_info *mtd)
 {
        int i;
 
+       if (!mtd->backing_dev_info) {
+               switch (mtd->type) {
+               case MTD_RAM:
+                       mtd->backing_dev_info = &mtd_bdi_rw_mappable;
+                       break;
+               case MTD_ROM:
+                       mtd->backing_dev_info = &mtd_bdi_ro_mappable;
+                       break;
+               default:
+                       mtd->backing_dev_info = &mtd_bdi_unmappable;
+                       break;
+               }
+       }
+
        BUG_ON(mtd->writesize == 0);
        mutex_lock(&mtd_table_mutex);
 
@@ -80,6 +253,23 @@ int add_mtd_device(struct mtd_info *mtd)
                                               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);
+                       if (device_register(&mtd->dev) != 0) {
+                               mtd_table[i] = NULL;
+                               break;
+                       }
+
+                       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 */
@@ -124,6 +314,8 @@ int del_mtd_device (struct mtd_info *mtd)
        } 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)
@@ -393,28 +585,38 @@ done:
         return ((count < begin+len-off) ? count : begin+len-off);
 }
 
+#endif /* CONFIG_PROC_FS */
+
 /*====================================================================*/
 /* Init code */
 
 static int __init init_mtd(void)
 {
+       mtd_class = class_create(THIS_MODULE, "mtd");
+
+       if (IS_ERR(mtd_class)) {
+               pr_err("Error creating mtd class.\n");
+               return PTR_ERR(mtd_class);
+       }
+#ifdef CONFIG_PROC_FS
        if ((proc_mtd = create_proc_entry( "mtd", 0, NULL )))
                proc_mtd->read_proc = mtd_read_proc;
+#endif /* CONFIG_PROC_FS */
        return 0;
 }
 
 static void __exit cleanup_mtd(void)
 {
+#ifdef CONFIG_PROC_FS
         if (proc_mtd)
                remove_proc_entry( "mtd", NULL);
+#endif /* CONFIG_PROC_FS */
+       class_destroy(mtd_class);
 }
 
 module_init(init_mtd);
 module_exit(cleanup_mtd);
 
-#endif /* CONFIG_PROC_FS */
-
-
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
 MODULE_DESCRIPTION("Core MTD registration and access routines");
index 1a6b3beabe8d04f70762f9aa3e470160a8e7c653..1060337c06df7a57b0a1a76455635a004ad22543 100644 (file)
@@ -44,6 +44,7 @@ static struct mtdoops_context {
        int oops_pages;
        int nextpage;
        int nextcount;
+       char *name;
 
        void *oops_buf;
 
@@ -273,6 +274,9 @@ static void mtdoops_notify_add(struct mtd_info *mtd)
 {
        struct mtdoops_context *cxt = &oops_cxt;
 
+       if (cxt->name && !strcmp(mtd->name, cxt->name))
+               cxt->mtd_index = mtd->index;
+
        if ((mtd->index != cxt->mtd_index) || cxt->mtd_index < 0)
                return;
 
@@ -357,8 +361,10 @@ mtdoops_console_write(struct console *co, const char *s, unsigned int count)
        spin_lock_irqsave(&cxt->writecount_lock, flags);
 
        /* Check ready status didn't change whilst waiting for the lock */
-       if (!cxt->ready)
+       if (!cxt->ready) {
+               spin_unlock_irqrestore(&cxt->writecount_lock, flags);
                return;
+       }
 
        if (cxt->writecount == 0) {
                u32 *stamp = cxt->oops_buf;
@@ -383,8 +389,12 @@ static int __init mtdoops_console_setup(struct console *co, char *options)
 {
        struct mtdoops_context *cxt = co->data;
 
-       if (cxt->mtd_index != -1)
+       if (cxt->mtd_index != -1 || cxt->name)
                return -EBUSY;
+       if (options) {
+               cxt->name = kstrdup(options, GFP_KERNEL);
+               return 0;
+       }
        if (co->index == -1)
                return -EINVAL;
 
@@ -412,6 +422,7 @@ static int __init mtdoops_console_init(void)
 
        cxt->mtd_index = -1;
        cxt->oops_buf = vmalloc(OOPS_PAGE_SIZE);
+       spin_lock_init(&cxt->writecount_lock);
 
        if (!cxt->oops_buf) {
                printk(KERN_ERR "Failed to allocate mtdoops buffer workspace\n");
@@ -432,6 +443,7 @@ static void __exit mtdoops_console_exit(void)
 
        unregister_mtd_user(&mtdoops_notifier);
        unregister_console(&mtdoops_console);
+       kfree(cxt->name);
        vfree(cxt->oops_buf);
 }
 
index 144e6b613a77cebdaf848707d8ad5c7abf4add89..29675edb44b46180bb954ed520f3d329c451a83d 100644 (file)
@@ -48,8 +48,11 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
                size_t *retlen, u_char *buf)
 {
        struct mtd_part *part = PART(mtd);
+       struct mtd_ecc_stats stats;
        int res;
 
+       stats = part->master->ecc_stats;
+
        if (from >= mtd->size)
                len = 0;
        else if (from + len > mtd->size)
@@ -58,9 +61,9 @@ static int part_read(struct mtd_info *mtd, loff_t from, size_t len,
                                   len, retlen, buf);
        if (unlikely(res)) {
                if (res == -EUCLEAN)
-                       mtd->ecc_stats.corrected++;
+                       mtd->ecc_stats.corrected += part->master->ecc_stats.corrected - stats.corrected;
                if (res == -EBADMSG)
-                       mtd->ecc_stats.failed++;
+                       mtd->ecc_stats.failed += part->master->ecc_stats.failed - stats.failed;
        }
        return res;
 }
@@ -84,6 +87,18 @@ static void part_unpoint(struct mtd_info *mtd, loff_t from, size_t len)
        part->master->unpoint(part->master, from + part->offset, len);
 }
 
+static unsigned long part_get_unmapped_area(struct mtd_info *mtd,
+                                           unsigned long len,
+                                           unsigned long offset,
+                                           unsigned long flags)
+{
+       struct mtd_part *part = PART(mtd);
+
+       offset += part->offset;
+       return part->master->get_unmapped_area(part->master, len, offset,
+                                              flags);
+}
+
 static int part_read_oob(struct mtd_info *mtd, loff_t from,
                struct mtd_oob_ops *ops)
 {
@@ -342,6 +357,12 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
 
        slave->mtd.name = part->name;
        slave->mtd.owner = master->owner;
+       slave->mtd.backing_dev_info = master->backing_dev_info;
+
+       /* NOTE:  we don't arrange MTDs as a tree; it'd be error-prone
+        * to have the same data be in two different partitions.
+        */
+       slave->mtd.dev.parent = master->dev.parent;
 
        slave->mtd.read = part_read;
        slave->mtd.write = part_write;
@@ -354,6 +375,8 @@ static struct mtd_part *add_one_partition(struct mtd_info *master,
                slave->mtd.unpoint = part_unpoint;
        }
 
+       if (master->get_unmapped_area)
+               slave->mtd.get_unmapped_area = part_get_unmapped_area;
        if (master->read_oob)
                slave->mtd.read_oob = part_read_oob;
        if (master->write_oob)
@@ -493,7 +516,9 @@ out_register:
  * This function, given a master MTD object and a partition table, creates
  * and registers slave MTD objects which are bound to the master according to
  * the partition definitions.
- * (Q: should we register the master MTD object as well?)
+ *
+ * We don't register the master, or expect the caller to have done so,
+ * for reasons of data integrity.
  */
 
 int add_mtd_partitions(struct mtd_info *master,
index 2ff88791cebc359aae7b830a18cf5b9679ec15b4..890936d0275ed21b957e2a46a9af52764a1fe28b 100644 (file)
@@ -334,7 +334,7 @@ config MTD_NAND_ATMEL_ECC_NONE
 endchoice
 
 config MTD_NAND_PXA3xx
-       bool "Support for NAND flash devices on PXA3xx"
+       tristate "Support for NAND flash devices on PXA3xx"
        depends on MTD_NAND && PXA3xx
        help
          This enables the driver for the NAND flash device found on
@@ -427,4 +427,23 @@ config MTD_NAND_SH_FLCTL
          Several Renesas SuperH CPU has FLCTL. This option enables support
          for NAND Flash using FLCTL. This driver support SH7723.
 
+config MTD_NAND_DAVINCI
+        tristate "Support NAND on DaVinci SoC"
+        depends on ARCH_DAVINCI
+        help
+         Enable the driver for NAND flash chips on Texas Instruments
+         DaVinci processors.
+
+config MTD_NAND_TXX9NDFMC
+       tristate "NAND Flash support for TXx9 SoC"
+       depends on SOC_TX4938 || SOC_TX4939
+       help
+         This enables the NAND flash controller on the TXx9 SoCs.
+
+config MTD_NAND_SOCRATES
+       tristate "Support for NAND on Socrates board"
+       depends on MTD_NAND && SOCRATES
+       help
+         Enables support for NAND Flash chips wired onto Socrates board.
+
 endif # MTD_NAND
index b661586afbfc38f5b2d616b4c5615207c9d5862e..d33860ac42c396a4f500d6498daf9559787b50c7 100644 (file)
@@ -14,6 +14,7 @@ obj-$(CONFIG_MTD_NAND_AU1550)         += au1550nd.o
 obj-$(CONFIG_MTD_NAND_BF5XX)           += bf5xx_nand.o
 obj-$(CONFIG_MTD_NAND_PPCHAMELEONEVB)  += ppchameleonevb.o
 obj-$(CONFIG_MTD_NAND_S3C2410)         += s3c2410.o
+obj-$(CONFIG_MTD_NAND_DAVINCI)         += davinci_nand.o
 obj-$(CONFIG_MTD_NAND_DISKONCHIP)      += diskonchip.o
 obj-$(CONFIG_MTD_NAND_H1900)           += h1910.o
 obj-$(CONFIG_MTD_NAND_RTC_FROM4)       += rtc_from4.o
@@ -36,5 +37,7 @@ obj-$(CONFIG_MTD_NAND_FSL_ELBC)               += fsl_elbc_nand.o
 obj-$(CONFIG_MTD_NAND_FSL_UPM)         += fsl_upm.o
 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
 
 nand-objs := nand_base.o nand_bbt.o
index 9af2a2cc1153adf36c51da04ea6b5ce7f765753c..4c2a67ca801e3e07c4ebbfa82e449b3210a296d3 100644 (file)
@@ -552,7 +552,6 @@ static void bf5xx_nand_dma_write_buf(struct mtd_info *mtd,
 static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
 {
        int ret;
-       unsigned short val;
 
        /* Do not use dma */
        if (!hardware_ecc)
@@ -560,13 +559,6 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
 
        init_completion(&info->dma_completion);
 
-#ifdef CONFIG_BF54x
-       /* Setup DMAC1 channel mux for NFC which shared with SDH */
-       val = bfin_read_DMAC1_PERIMUX();
-       val &= 0xFFFE;
-       bfin_write_DMAC1_PERIMUX(val);
-       SSYNC();
-#endif
        /* Request NFC DMA channel */
        ret = request_dma(CH_NFC, "BF5XX NFC driver");
        if (ret < 0) {
@@ -574,7 +566,13 @@ static int bf5xx_nand_dma_init(struct bf5xx_nand_info *info)
                return ret;
        }
 
-       set_dma_callback(CH_NFC, (void *) bf5xx_nand_dma_irq, (void *) info);
+#ifdef CONFIG_BF54x
+       /* Setup DMAC1 channel mux for NFC which shared with SDH */
+       bfin_write_DMAC1_PERIMUX(bfin_read_DMAC1_PERIMUX() & ~1);
+       SSYNC();
+#endif
+
+       set_dma_callback(CH_NFC, bf5xx_nand_dma_irq, info);
 
        /* Turn off the DMA channel first */
        disable_dma(CH_NFC);
@@ -632,7 +630,7 @@ static int bf5xx_nand_hw_init(struct bf5xx_nand_info *info)
 /*
  * Device management interface
  */
-static int bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
+static int __devinit bf5xx_nand_add_partition(struct bf5xx_nand_info *info)
 {
        struct mtd_info *mtd = &info->mtd;
 
index 22a6b2e50e917467dbacb08f45775934d5a51832..7c5b257ce8e482c8a974fae8b6eb8ba286a39277 100644 (file)
@@ -654,6 +654,7 @@ static int __devinit cafe_nand_probe(struct pci_dev *pdev,
        }
        cafe = (void *)(&mtd[1]);
 
+       mtd->dev.parent = &pdev->dev;
        mtd->priv = cafe;
        mtd->owner = THIS_MODULE;
 
diff --git a/drivers/mtd/nand/davinci_nand.c b/drivers/mtd/nand/davinci_nand.c
new file mode 100644 (file)
index 0000000..0119220
--- /dev/null
@@ -0,0 +1,570 @@
+/*
+ * davinci_nand.c - NAND Flash Driver for DaVinci family chips
+ *
+ * Copyright Â© 2006 Texas Instruments.
+ *
+ * Port to 2.6.23 Copyright Â© 2008 by:
+ *   Sander Huijsen <Shuijsen@optelecom-nkf.com>
+ *   Troy Kisky <troy.kisky@boundarydevices.com>
+ *   Dirk Behme <Dirk.Behme@gmail.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/err.h>
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+
+#include <mach/nand.h>
+
+#include <asm/mach-types.h>
+
+
+/*
+ * This is a device driver for the NAND flash controller found on the
+ * various DaVinci family chips.  It handles up to four SoC chipselects,
+ * and some flavors of secondary chipselect (e.g. based on A12) as used
+ * with multichip packages.
+ *
+ * The 1-bit ECC hardware is supported, but not yet the newer 4-bit ECC
+ * available on chips like the DM355 and OMAP-L137 and needed with the
+ * more error-prone MLC NAND chips.
+ *
+ * This driver assumes EM_WAIT connects all the NAND devices' RDY/nBUSY
+ * outputs in a "wire-AND" configuration, with no per-chip signals.
+ */
+struct davinci_nand_info {
+       struct mtd_info         mtd;
+       struct nand_chip        chip;
+
+       struct device           *dev;
+       struct clk              *clk;
+       bool                    partitioned;
+
+       void __iomem            *base;
+       void __iomem            *vaddr;
+
+       uint32_t                ioaddr;
+       uint32_t                current_cs;
+
+       uint32_t                mask_chipsel;
+       uint32_t                mask_ale;
+       uint32_t                mask_cle;
+
+       uint32_t                core_chipsel;
+};
+
+static DEFINE_SPINLOCK(davinci_nand_lock);
+
+#define to_davinci_nand(m) container_of(m, struct davinci_nand_info, mtd)
+
+
+static inline unsigned int davinci_nand_readl(struct davinci_nand_info *info,
+               int offset)
+{
+       return __raw_readl(info->base + offset);
+}
+
+static inline void davinci_nand_writel(struct davinci_nand_info *info,
+               int offset, unsigned long value)
+{
+       __raw_writel(value, info->base + offset);
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * Access to hardware control lines:  ALE, CLE, secondary chipselect.
+ */
+
+static void nand_davinci_hwcontrol(struct mtd_info *mtd, int cmd,
+                                  unsigned int ctrl)
+{
+       struct davinci_nand_info        *info = to_davinci_nand(mtd);
+       uint32_t                        addr = info->current_cs;
+       struct nand_chip                *nand = mtd->priv;
+
+       /* Did the control lines change? */
+       if (ctrl & NAND_CTRL_CHANGE) {
+               if ((ctrl & NAND_CTRL_CLE) == NAND_CTRL_CLE)
+                       addr |= info->mask_cle;
+               else if ((ctrl & NAND_CTRL_ALE) == NAND_CTRL_ALE)
+                       addr |= info->mask_ale;
+
+               nand->IO_ADDR_W = (void __iomem __force *)addr;
+       }
+
+       if (cmd != NAND_CMD_NONE)
+               iowrite8(cmd, nand->IO_ADDR_W);
+}
+
+static void nand_davinci_select_chip(struct mtd_info *mtd, int chip)
+{
+       struct davinci_nand_info        *info = to_davinci_nand(mtd);
+       uint32_t                        addr = info->ioaddr;
+
+       /* maybe kick in a second chipselect */
+       if (chip > 0)
+               addr |= info->mask_chipsel;
+       info->current_cs = addr;
+
+       info->chip.IO_ADDR_W = (void __iomem __force *)addr;
+       info->chip.IO_ADDR_R = info->chip.IO_ADDR_W;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * 1-bit hardware ECC ... context maintained for each core chipselect
+ */
+
+static inline uint32_t nand_davinci_readecc_1bit(struct mtd_info *mtd)
+{
+       struct davinci_nand_info *info = to_davinci_nand(mtd);
+
+       return davinci_nand_readl(info, NANDF1ECC_OFFSET
+                       + 4 * info->core_chipsel);
+}
+
+static void nand_davinci_hwctl_1bit(struct mtd_info *mtd, int mode)
+{
+       struct davinci_nand_info *info;
+       uint32_t nandcfr;
+       unsigned long flags;
+
+       info = to_davinci_nand(mtd);
+
+       /* Reset ECC hardware */
+       nand_davinci_readecc_1bit(mtd);
+
+       spin_lock_irqsave(&davinci_nand_lock, flags);
+
+       /* Restart ECC hardware */
+       nandcfr = davinci_nand_readl(info, NANDFCR_OFFSET);
+       nandcfr |= BIT(8 + info->core_chipsel);
+       davinci_nand_writel(info, NANDFCR_OFFSET, nandcfr);
+
+       spin_unlock_irqrestore(&davinci_nand_lock, flags);
+}
+
+/*
+ * Read hardware ECC value and pack into three bytes
+ */
+static int nand_davinci_calculate_1bit(struct mtd_info *mtd,
+                                     const u_char *dat, u_char *ecc_code)
+{
+       unsigned int ecc_val = nand_davinci_readecc_1bit(mtd);
+       unsigned int ecc24 = (ecc_val & 0x0fff) | ((ecc_val & 0x0fff0000) >> 4);
+
+       /* invert so that erased block ecc is correct */
+       ecc24 = ~ecc24;
+       ecc_code[0] = (u_char)(ecc24);
+       ecc_code[1] = (u_char)(ecc24 >> 8);
+       ecc_code[2] = (u_char)(ecc24 >> 16);
+
+       return 0;
+}
+
+static int nand_davinci_correct_1bit(struct mtd_info *mtd, u_char *dat,
+                                    u_char *read_ecc, u_char *calc_ecc)
+{
+       struct nand_chip *chip = mtd->priv;
+       uint32_t eccNand = read_ecc[0] | (read_ecc[1] << 8) |
+                                         (read_ecc[2] << 16);
+       uint32_t eccCalc = calc_ecc[0] | (calc_ecc[1] << 8) |
+                                         (calc_ecc[2] << 16);
+       uint32_t diff = eccCalc ^ eccNand;
+
+       if (diff) {
+               if ((((diff >> 12) ^ diff) & 0xfff) == 0xfff) {
+                       /* Correctable error */
+                       if ((diff >> (12 + 3)) < chip->ecc.size) {
+                               dat[diff >> (12 + 3)] ^= BIT((diff >> 12) & 7);
+                               return 1;
+                       } else {
+                               return -1;
+                       }
+               } else if (!(diff & (diff - 1))) {
+                       /* Single bit ECC error in the ECC itself,
+                        * nothing to fix */
+                       return 1;
+               } else {
+                       /* Uncorrectable error */
+                       return -1;
+               }
+
+       }
+       return 0;
+}
+
+/*----------------------------------------------------------------------*/
+
+/*
+ * NOTE:  NAND boot requires ALE == EM_A[1], CLE == EM_A[2], so that's
+ * how these chips are normally wired.  This translates to both 8 and 16
+ * bit busses using ALE == BIT(3) in byte addresses, and CLE == BIT(4).
+ *
+ * For now we assume that configuration, or any other one which ignores
+ * the two LSBs for NAND access ... so we can issue 32-bit reads/writes
+ * and have that transparently morphed into multiple NAND operations.
+ */
+static void nand_davinci_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       struct nand_chip *chip = mtd->priv;
+
+       if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
+               ioread32_rep(chip->IO_ADDR_R, buf, len >> 2);
+       else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
+               ioread16_rep(chip->IO_ADDR_R, buf, len >> 1);
+       else
+               ioread8_rep(chip->IO_ADDR_R, buf, len);
+}
+
+static void nand_davinci_write_buf(struct mtd_info *mtd,
+               const uint8_t *buf, int len)
+{
+       struct nand_chip *chip = mtd->priv;
+
+       if ((0x03 & ((unsigned)buf)) == 0 && (0x03 & len) == 0)
+               iowrite32_rep(chip->IO_ADDR_R, buf, len >> 2);
+       else if ((0x01 & ((unsigned)buf)) == 0 && (0x01 & len) == 0)
+               iowrite16_rep(chip->IO_ADDR_R, buf, len >> 1);
+       else
+               iowrite8_rep(chip->IO_ADDR_R, buf, len);
+}
+
+/*
+ * Check hardware register for wait status. Returns 1 if device is ready,
+ * 0 if it is still busy.
+ */
+static int nand_davinci_dev_ready(struct mtd_info *mtd)
+{
+       struct davinci_nand_info *info = to_davinci_nand(mtd);
+
+       return davinci_nand_readl(info, NANDFSR_OFFSET) & BIT(0);
+}
+
+static void __init nand_dm6446evm_flash_init(struct davinci_nand_info *info)
+{
+       uint32_t regval, a1cr;
+
+       /*
+        * NAND FLASH timings @ PLL1 == 459 MHz
+        *  - AEMIF.CLK freq   = PLL1/6 = 459/6 = 76.5 MHz
+        *  - AEMIF.CLK period = 1/76.5 MHz = 13.1 ns
+        */
+       regval = 0
+               | (0 << 31)           /* selectStrobe */
+               | (0 << 30)           /* extWait (never with NAND) */
+               | (1 << 26)           /* writeSetup      10 ns */
+               | (3 << 20)           /* writeStrobe     40 ns */
+               | (1 << 17)           /* writeHold       10 ns */
+               | (0 << 13)           /* readSetup       10 ns */
+               | (3 << 7)            /* readStrobe      60 ns */
+               | (0 << 4)            /* readHold        10 ns */
+               | (3 << 2)            /* turnAround      ?? ns */
+               | (0 << 0)            /* asyncSize       8-bit bus */
+               ;
+       a1cr = davinci_nand_readl(info, A1CR_OFFSET);
+       if (a1cr != regval) {
+               dev_dbg(info->dev, "Warning: NAND config: Set A1CR " \
+                      "reg to 0x%08x, was 0x%08x, should be done by " \
+                      "bootloader.\n", regval, a1cr);
+               davinci_nand_writel(info, A1CR_OFFSET, regval);
+       }
+}
+
+/*----------------------------------------------------------------------*/
+
+static int __init nand_davinci_probe(struct platform_device *pdev)
+{
+       struct davinci_nand_pdata       *pdata = pdev->dev.platform_data;
+       struct davinci_nand_info        *info;
+       struct resource                 *res1;
+       struct resource                 *res2;
+       void __iomem                    *vaddr;
+       void __iomem                    *base;
+       int                             ret;
+       uint32_t                        val;
+       nand_ecc_modes_t                ecc_mode;
+
+       /* which external chipselect will we be managing? */
+       if (pdev->id < 0 || pdev->id > 3)
+               return -ENODEV;
+
+       info = kzalloc(sizeof(*info), GFP_KERNEL);
+       if (!info) {
+               dev_err(&pdev->dev, "unable to allocate memory\n");
+               ret = -ENOMEM;
+               goto err_nomem;
+       }
+
+       platform_set_drvdata(pdev, info);
+
+       res1 = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       res2 = platform_get_resource(pdev, IORESOURCE_MEM, 1);
+       if (!res1 || !res2) {
+               dev_err(&pdev->dev, "resource missing\n");
+               ret = -EINVAL;
+               goto err_nomem;
+       }
+
+       vaddr = ioremap(res1->start, res1->end - res1->start);
+       base = ioremap(res2->start, res2->end - res2->start);
+       if (!vaddr || !base) {
+               dev_err(&pdev->dev, "ioremap failed\n");
+               ret = -EINVAL;
+               goto err_ioremap;
+       }
+
+       info->dev               = &pdev->dev;
+       info->base              = base;
+       info->vaddr             = vaddr;
+
+       info->mtd.priv          = &info->chip;
+       info->mtd.name          = dev_name(&pdev->dev);
+       info->mtd.owner         = THIS_MODULE;
+
+       info->mtd.dev.parent    = &pdev->dev;
+
+       info->chip.IO_ADDR_R    = vaddr;
+       info->chip.IO_ADDR_W    = vaddr;
+       info->chip.chip_delay   = 0;
+       info->chip.select_chip  = nand_davinci_select_chip;
+
+       /* options such as NAND_USE_FLASH_BBT or 16-bit widths */
+       info->chip.options      = pdata ? pdata->options : 0;
+
+       info->ioaddr            = (uint32_t __force) vaddr;
+
+       info->current_cs        = info->ioaddr;
+       info->core_chipsel      = pdev->id;
+       info->mask_chipsel      = pdata->mask_chipsel;
+
+       /* use nandboot-capable ALE/CLE masks by default */
+       if (pdata && pdata->mask_ale)
+               info->mask_ale  = pdata->mask_cle;
+       else
+               info->mask_ale  = MASK_ALE;
+       if (pdata && pdata->mask_cle)
+               info->mask_cle  = pdata->mask_cle;
+       else
+               info->mask_cle  = MASK_CLE;
+
+       /* Set address of hardware control function */
+       info->chip.cmd_ctrl     = nand_davinci_hwcontrol;
+       info->chip.dev_ready    = nand_davinci_dev_ready;
+
+       /* Speed up buffer I/O */
+       info->chip.read_buf     = nand_davinci_read_buf;
+       info->chip.write_buf    = nand_davinci_write_buf;
+
+       /* use board-specific ECC config; else, the best available */
+       if (pdata)
+               ecc_mode = pdata->ecc_mode;
+       else
+               ecc_mode = NAND_ECC_HW;
+
+       switch (ecc_mode) {
+       case NAND_ECC_NONE:
+       case NAND_ECC_SOFT:
+               break;
+       case NAND_ECC_HW:
+               info->chip.ecc.calculate = nand_davinci_calculate_1bit;
+               info->chip.ecc.correct = nand_davinci_correct_1bit;
+               info->chip.ecc.hwctl = nand_davinci_hwctl_1bit;
+               info->chip.ecc.size = 512;
+               info->chip.ecc.bytes = 3;
+               break;
+       case NAND_ECC_HW_SYNDROME:
+               /* FIXME implement */
+               info->chip.ecc.size = 512;
+               info->chip.ecc.bytes = 10;
+
+               dev_warn(&pdev->dev, "4-bit ECC nyet supported\n");
+               /* FALL THROUGH */
+       default:
+               ret = -EINVAL;
+               goto err_ecc;
+       }
+       info->chip.ecc.mode = ecc_mode;
+
+       info->clk = clk_get(&pdev->dev, "AEMIFCLK");
+       if (IS_ERR(info->clk)) {
+               ret = PTR_ERR(info->clk);
+               dev_dbg(&pdev->dev, "unable to get AEMIFCLK, err %d\n", ret);
+               goto err_clk;
+       }
+
+       ret = clk_enable(info->clk);
+       if (ret < 0) {
+               dev_dbg(&pdev->dev, "unable to enable AEMIFCLK, err %d\n", ret);
+               goto err_clk_enable;
+       }
+
+       /* EMIF timings should normally be set by the boot loader,
+        * especially after boot-from-NAND.  The *only* reason to
+        * have this special casing for the DM6446 EVM is to work
+        * with boot-from-NOR ... with CS0 manually re-jumpered
+        * (after startup) so it addresses the NAND flash, not NOR.
+        * Even for dev boards, that's unusually rude...
+        */
+       if (machine_is_davinci_evm())
+               nand_dm6446evm_flash_init(info);
+
+       spin_lock_irq(&davinci_nand_lock);
+
+       /* put CSxNAND into NAND mode */
+       val = davinci_nand_readl(info, NANDFCR_OFFSET);
+       val |= BIT(info->core_chipsel);
+       davinci_nand_writel(info, NANDFCR_OFFSET, val);
+
+       spin_unlock_irq(&davinci_nand_lock);
+
+       /* Scan to find existence of the device(s) */
+       ret = nand_scan(&info->mtd, pdata->mask_chipsel ? 2 : 1);
+       if (ret < 0) {
+               dev_dbg(&pdev->dev, "no NAND chip(s) found\n");
+               goto err_scan;
+       }
+
+       if (mtd_has_partitions()) {
+               struct mtd_partition    *mtd_parts = NULL;
+               int                     mtd_parts_nb = 0;
+
+               if (mtd_has_cmdlinepart()) {
+                       static const char *probes[] __initconst =
+                               { "cmdlinepart", NULL };
+
+                       const char              *master_name;
+
+                       /* Set info->mtd.name = 0 temporarily */
+                       master_name             = info->mtd.name;
+                       info->mtd.name          = (char *)0;
+
+                       /* info->mtd.name == 0, means: don't bother checking
+                          <mtd-id> */
+                       mtd_parts_nb = parse_mtd_partitions(&info->mtd, probes,
+                                                           &mtd_parts, 0);
+
+                       /* Restore info->mtd.name */
+                       info->mtd.name = master_name;
+               }
+
+               if (mtd_parts_nb <= 0 && pdata) {
+                       mtd_parts = pdata->parts;
+                       mtd_parts_nb = pdata->nr_parts;
+               }
+
+               /* Register any partitions */
+               if (mtd_parts_nb > 0) {
+                       ret = add_mtd_partitions(&info->mtd,
+                                       mtd_parts, mtd_parts_nb);
+                       if (ret == 0)
+                               info->partitioned = true;
+               }
+
+       } else if (pdata && pdata->nr_parts) {
+               dev_warn(&pdev->dev, "ignoring %d default partitions on %s\n",
+                               pdata->nr_parts, info->mtd.name);
+       }
+
+       /* If there's no partition info, just package the whole chip
+        * as a single MTD device.
+        */
+       if (!info->partitioned)
+               ret = add_mtd_device(&info->mtd) ? -ENODEV : 0;
+
+       if (ret < 0)
+               goto err_scan;
+
+       val = davinci_nand_readl(info, NRCSR_OFFSET);
+       dev_info(&pdev->dev, "controller rev. %d.%d\n",
+              (val >> 8) & 0xff, val & 0xff);
+
+       return 0;
+
+err_scan:
+       clk_disable(info->clk);
+
+err_clk_enable:
+       clk_put(info->clk);
+
+err_ecc:
+err_clk:
+err_ioremap:
+       if (base)
+               iounmap(base);
+       if (vaddr)
+               iounmap(vaddr);
+
+err_nomem:
+       kfree(info);
+       return ret;
+}
+
+static int __exit nand_davinci_remove(struct platform_device *pdev)
+{
+       struct davinci_nand_info *info = platform_get_drvdata(pdev);
+       int status;
+
+       if (mtd_has_partitions() && info->partitioned)
+               status = del_mtd_partitions(&info->mtd);
+       else
+               status = del_mtd_device(&info->mtd);
+
+       iounmap(info->base);
+       iounmap(info->vaddr);
+
+       nand_release(&info->mtd);
+
+       clk_disable(info->clk);
+       clk_put(info->clk);
+
+       kfree(info);
+
+       return 0;
+}
+
+static struct platform_driver nand_davinci_driver = {
+       .remove         = __exit_p(nand_davinci_remove),
+       .driver         = {
+               .name   = "davinci_nand",
+       },
+};
+MODULE_ALIAS("platform:davinci_nand");
+
+static int __init nand_davinci_init(void)
+{
+       return platform_driver_probe(&nand_davinci_driver, nand_davinci_probe);
+}
+module_init(nand_davinci_init);
+
+static void __exit nand_davinci_exit(void)
+{
+       platform_driver_unregister(&nand_davinci_driver);
+}
+module_exit(nand_davinci_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Texas Instruments");
+MODULE_DESCRIPTION("Davinci NAND flash driver");
+
index 7815a404a63287a04ec4c25e4a77afbc8a1b6a8e..d120cd8d72674919fc453870978765ebf95d9a00 100644 (file)
 #include <linux/io.h>
 #include <asm/fsl_lbc.h>
 
+#define FSL_UPM_WAIT_RUN_PATTERN  0x1
+#define FSL_UPM_WAIT_WRITE_BYTE   0x2
+#define FSL_UPM_WAIT_WRITE_BUFFER 0x4
+
 struct fsl_upm_nand {
        struct device *dev;
        struct mtd_info mtd;
@@ -36,8 +40,12 @@ struct fsl_upm_nand {
        uint8_t upm_addr_offset;
        uint8_t upm_cmd_offset;
        void __iomem *io_base;
-       int rnb_gpio;
+       int rnb_gpio[NAND_MAX_CHIPS];
+       uint32_t mchip_offsets[NAND_MAX_CHIPS];
+       uint32_t mchip_count;
+       uint32_t mchip_number;
        int chip_delay;
+       uint32_t wait_flags;
 };
 
 #define to_fsl_upm_nand(mtd) container_of(mtd, struct fsl_upm_nand, mtd)
@@ -46,7 +54,7 @@ static int fun_chip_ready(struct mtd_info *mtd)
 {
        struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
 
-       if (gpio_get_value(fun->rnb_gpio))
+       if (gpio_get_value(fun->rnb_gpio[fun->mchip_number]))
                return 1;
 
        dev_vdbg(fun->dev, "busy\n");
@@ -55,9 +63,9 @@ static int fun_chip_ready(struct mtd_info *mtd)
 
 static void fun_wait_rnb(struct fsl_upm_nand *fun)
 {
-       int cnt = 1000000;
+       if (fun->rnb_gpio[fun->mchip_number] >= 0) {
+               int cnt = 1000000;
 
-       if (fun->rnb_gpio >= 0) {
                while (--cnt && !fun_chip_ready(&fun->mtd))
                        cpu_relax();
                if (!cnt)
@@ -69,7 +77,9 @@ static void fun_wait_rnb(struct fsl_upm_nand *fun)
 
 static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
 {
+       struct nand_chip *chip = mtd->priv;
        struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
+       u32 mar;
 
        if (!(ctrl & fun->last_ctrl)) {
                fsl_upm_end_pattern(&fun->upm);
@@ -87,9 +97,28 @@ static void fun_cmd_ctrl(struct mtd_info *mtd, int cmd, unsigned int ctrl)
                        fsl_upm_start_pattern(&fun->upm, fun->upm_cmd_offset);
        }
 
-       fsl_upm_run_pattern(&fun->upm, fun->io_base, cmd);
+       mar = (cmd << (32 - fun->upm.width)) |
+               fun->mchip_offsets[fun->mchip_number];
+       fsl_upm_run_pattern(&fun->upm, chip->IO_ADDR_R, mar);
+
+       if (fun->wait_flags & FSL_UPM_WAIT_RUN_PATTERN)
+               fun_wait_rnb(fun);
+}
+
+static void fun_select_chip(struct mtd_info *mtd, int mchip_nr)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct fsl_upm_nand *fun = to_fsl_upm_nand(mtd);
 
-       fun_wait_rnb(fun);
+       if (mchip_nr == -1) {
+               chip->cmd_ctrl(mtd, NAND_CMD_NONE, 0 | NAND_CTRL_CHANGE);
+       } else if (mchip_nr >= 0) {
+               fun->mchip_number = mchip_nr;
+               chip->IO_ADDR_R = fun->io_base + fun->mchip_offsets[mchip_nr];
+               chip->IO_ADDR_W = chip->IO_ADDR_R;
+       } else {
+               BUG();
+       }
 }
 
 static uint8_t fun_read_byte(struct mtd_info *mtd)
@@ -115,8 +144,11 @@ static void fun_write_buf(struct mtd_info *mtd, const uint8_t *buf, int len)
 
        for (i = 0; i < len; i++) {
                out_8(fun->chip.IO_ADDR_W, buf[i]);
-               fun_wait_rnb(fun);
+               if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BYTE)
+                       fun_wait_rnb(fun);
        }
+       if (fun->wait_flags & FSL_UPM_WAIT_WRITE_BUFFER)
+               fun_wait_rnb(fun);
 }
 
 static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
@@ -137,8 +169,10 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
        fun->chip.read_buf = fun_read_buf;
        fun->chip.write_buf = fun_write_buf;
        fun->chip.ecc.mode = NAND_ECC_SOFT;
+       if (fun->mchip_count > 1)
+               fun->chip.select_chip = fun_select_chip;
 
-       if (fun->rnb_gpio >= 0)
+       if (fun->rnb_gpio[0] >= 0)
                fun->chip.dev_ready = fun_chip_ready;
 
        fun->mtd.priv = &fun->chip;
@@ -155,7 +189,7 @@ static int __devinit fun_chip_init(struct fsl_upm_nand *fun,
                goto err;
        }
 
-       ret = nand_scan(&fun->mtd, 1);
+       ret = nand_scan(&fun->mtd, fun->mchip_count);
        if (ret)
                goto err;
 
@@ -185,8 +219,10 @@ static int __devinit fun_probe(struct of_device *ofdev,
        struct fsl_upm_nand *fun;
        struct resource io_res;
        const uint32_t *prop;
+       int rnb_gpio;
        int ret;
        int size;
+       int i;
 
        fun = kzalloc(sizeof(*fun), GFP_KERNEL);
        if (!fun)
@@ -208,7 +244,7 @@ static int __devinit fun_probe(struct of_device *ofdev,
        if (!prop || size != sizeof(uint32_t)) {
                dev_err(&ofdev->dev, "can't get UPM address offset\n");
                ret = -EINVAL;
-               goto err2;
+               goto err1;
        }
        fun->upm_addr_offset = *prop;
 
@@ -216,21 +252,40 @@ static int __devinit fun_probe(struct of_device *ofdev,
        if (!prop || size != sizeof(uint32_t)) {
                dev_err(&ofdev->dev, "can't get UPM command offset\n");
                ret = -EINVAL;
-               goto err2;
+               goto err1;
        }
        fun->upm_cmd_offset = *prop;
 
-       fun->rnb_gpio = of_get_gpio(ofdev->node, 0);
-       if (fun->rnb_gpio >= 0) {
-               ret = gpio_request(fun->rnb_gpio, dev_name(&ofdev->dev));
-               if (ret) {
-                       dev_err(&ofdev->dev, "can't request RNB gpio\n");
+       prop = of_get_property(ofdev->node,
+                              "fsl,upm-addr-line-cs-offsets", &size);
+       if (prop && (size / sizeof(uint32_t)) > 0) {
+               fun->mchip_count = size / sizeof(uint32_t);
+               if (fun->mchip_count >= NAND_MAX_CHIPS) {
+                       dev_err(&ofdev->dev, "too much multiple chips\n");
+                       goto err1;
+               }
+               for (i = 0; i < fun->mchip_count; i++)
+                       fun->mchip_offsets[i] = prop[i];
+       } else {
+               fun->mchip_count = 1;
+       }
+
+       for (i = 0; i < fun->mchip_count; i++) {
+               fun->rnb_gpio[i] = -1;
+               rnb_gpio = of_get_gpio(ofdev->node, i);
+               if (rnb_gpio >= 0) {
+                       ret = gpio_request(rnb_gpio, dev_name(&ofdev->dev));
+                       if (ret) {
+                               dev_err(&ofdev->dev,
+                                       "can't request RNB gpio #%d\n", i);
+                               goto err2;
+                       }
+                       gpio_direction_input(rnb_gpio);
+                       fun->rnb_gpio[i] = rnb_gpio;
+               } else if (rnb_gpio == -EINVAL) {
+                       dev_err(&ofdev->dev, "RNB gpio #%d is invalid\n", i);
                        goto err2;
                }
-               gpio_direction_input(fun->rnb_gpio);
-       } else if (fun->rnb_gpio == -EINVAL) {
-               dev_err(&ofdev->dev, "specified RNB gpio is invalid\n");
-               goto err2;
        }
 
        prop = of_get_property(ofdev->node, "chip-delay", NULL);
@@ -239,8 +294,15 @@ static int __devinit fun_probe(struct of_device *ofdev,
        else
                fun->chip_delay = 50;
 
+       prop = of_get_property(ofdev->node, "fsl,upm-wait-flags", &size);
+       if (prop && size == sizeof(uint32_t))
+               fun->wait_flags = *prop;
+       else
+               fun->wait_flags = FSL_UPM_WAIT_RUN_PATTERN |
+                                 FSL_UPM_WAIT_WRITE_BYTE;
+
        fun->io_base = devm_ioremap_nocache(&ofdev->dev, io_res.start,
-                                         io_res.end - io_res.start + 1);
+                                           io_res.end - io_res.start + 1);
        if (!fun->io_base) {
                ret = -ENOMEM;
                goto err2;
@@ -257,8 +319,11 @@ static int __devinit fun_probe(struct of_device *ofdev,
 
        return 0;
 err2:
-       if (fun->rnb_gpio >= 0)
-               gpio_free(fun->rnb_gpio);
+       for (i = 0; i < fun->mchip_count; i++) {
+               if (fun->rnb_gpio[i] < 0)
+                       break;
+               gpio_free(fun->rnb_gpio[i]);
+       }
 err1:
        kfree(fun);
 
@@ -268,12 +333,16 @@ err1:
 static int __devexit fun_remove(struct of_device *ofdev)
 {
        struct fsl_upm_nand *fun = dev_get_drvdata(&ofdev->dev);
+       int i;
 
        nand_release(&fun->mtd);
        kfree(fun->mtd.name);
 
-       if (fun->rnb_gpio >= 0)
-               gpio_free(fun->rnb_gpio);
+       for (i = 0; i < fun->mchip_count; i++) {
+               if (fun->rnb_gpio[i] < 0)
+                       break;
+               gpio_free(fun->rnb_gpio[i]);
+       }
 
        kfree(fun);
 
index bad048aca89af040b48248bd7e3852365c143044..f3548d0480142cb5fa8c5e599ff3b8add7b91e97 100644 (file)
@@ -866,6 +866,7 @@ static int __init mxcnd_probe(struct platform_device *pdev)
        mtd = &host->mtd;
        mtd->priv = this;
        mtd->owner = THIS_MODULE;
+       mtd->dev.parent = &pdev->dev;
 
        /* 50 us command delay time */
        this->chip_delay = 5;
index 5f71371eb1b085ce6ad469d0120264dbc5bb1f33..3d7ed432fa4157ec9d655c3354c4c1edc699ebc8 100644 (file)
@@ -82,6 +82,20 @@ static struct nand_ecclayout nand_oob_64 = {
                 .length = 38}}
 };
 
+static struct nand_ecclayout nand_oob_128 = {
+       .eccbytes = 48,
+       .eccpos = {
+                  80, 81, 82, 83, 84, 85, 86, 87,
+                  88, 89, 90, 91, 92, 93, 94, 95,
+                  96, 97, 98, 99, 100, 101, 102, 103,
+                  104, 105, 106, 107, 108, 109, 110, 111,
+                  112, 113, 114, 115, 116, 117, 118, 119,
+                  120, 121, 122, 123, 124, 125, 126, 127},
+       .oobfree = {
+               {.offset = 2,
+                .length = 78}}
+};
+
 static int nand_get_device(struct nand_chip *chip, struct mtd_info *mtd,
                           int new_state);
 
@@ -748,6 +762,8 @@ static int nand_wait(struct mtd_info *mtd, struct nand_chip *chip)
  * @mtd:       mtd info structure
  * @chip:      nand chip info structure
  * @buf:       buffer to store read data
+ *
+ * Not for syndrome calculating ecc controllers, which use a special oob layout
  */
 static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
                              uint8_t *buf)
@@ -757,6 +773,47 @@ static int nand_read_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
        return 0;
 }
 
+/**
+ * nand_read_page_raw_syndrome - [Intern] read raw page data without ecc
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       buffer to store read data
+ *
+ * We need a special oob layout and handling even when OOB isn't used.
+ */
+static int nand_read_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+                             uint8_t *buf)
+{
+       int eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       uint8_t *oob = chip->oob_poi;
+       int steps, size;
+
+       for (steps = chip->ecc.steps; steps > 0; steps--) {
+               chip->read_buf(mtd, buf, eccsize);
+               buf += eccsize;
+
+               if (chip->ecc.prepad) {
+                       chip->read_buf(mtd, oob, chip->ecc.prepad);
+                       oob += chip->ecc.prepad;
+               }
+
+               chip->read_buf(mtd, oob, eccbytes);
+               oob += eccbytes;
+
+               if (chip->ecc.postpad) {
+                       chip->read_buf(mtd, oob, chip->ecc.postpad);
+                       oob += chip->ecc.postpad;
+               }
+       }
+
+       size = mtd->oobsize - (oob - chip->oob_poi);
+       if (size)
+               chip->read_buf(mtd, oob, size);
+
+       return 0;
+}
+
 /**
  * nand_read_page_swecc - [REPLACABLE] software ecc based page read function
  * @mtd:       mtd info structure
@@ -1482,6 +1539,8 @@ static int nand_read_oob(struct mtd_info *mtd, loff_t from,
  * @mtd:       mtd info structure
  * @chip:      nand chip info structure
  * @buf:       data buffer
+ *
+ * Not for syndrome calculating ecc controllers, which use a special oob layout
  */
 static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
                                const uint8_t *buf)
@@ -1490,6 +1549,44 @@ static void nand_write_page_raw(struct mtd_info *mtd, struct nand_chip *chip,
        chip->write_buf(mtd, chip->oob_poi, mtd->oobsize);
 }
 
+/**
+ * nand_write_page_raw_syndrome - [Intern] raw page write function
+ * @mtd:       mtd info structure
+ * @chip:      nand chip info structure
+ * @buf:       data buffer
+ *
+ * We need a special oob layout and handling even when ECC isn't checked.
+ */
+static void nand_write_page_raw_syndrome(struct mtd_info *mtd, struct nand_chip *chip,
+                               const uint8_t *buf)
+{
+       int eccsize = chip->ecc.size;
+       int eccbytes = chip->ecc.bytes;
+       uint8_t *oob = chip->oob_poi;
+       int steps, size;
+
+       for (steps = chip->ecc.steps; steps > 0; steps--) {
+               chip->write_buf(mtd, buf, eccsize);
+               buf += eccsize;
+
+               if (chip->ecc.prepad) {
+                       chip->write_buf(mtd, oob, chip->ecc.prepad);
+                       oob += chip->ecc.prepad;
+               }
+
+               chip->read_buf(mtd, oob, eccbytes);
+               oob += eccbytes;
+
+               if (chip->ecc.postpad) {
+                       chip->write_buf(mtd, oob, chip->ecc.postpad);
+                       oob += chip->ecc.postpad;
+               }
+       }
+
+       size = mtd->oobsize - (oob - chip->oob_poi);
+       if (size)
+               chip->write_buf(mtd, oob, size);
+}
 /**
  * nand_write_page_swecc - [REPLACABLE] software ecc based page write function
  * @mtd:       mtd info structure
@@ -1863,7 +1960,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
        }
 
        if (unlikely(ops->ooboffs >= len)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: "
                        "Attempt to start write outside oob\n");
                return -EINVAL;
        }
@@ -1873,7 +1970,7 @@ static int nand_do_write_oob(struct mtd_info *mtd, loff_t to,
                     ops->ooboffs + ops->ooblen >
                        ((mtd->size >> chip->page_shift) -
                         (to >> chip->page_shift)) * len)) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_do_write_oob: "
                        "Attempt write beyond end of device\n");
                return -EINVAL;
        }
@@ -1929,8 +2026,8 @@ static int nand_write_oob(struct mtd_info *mtd, loff_t to,
 
        /* Do not allow writes past end of device */
        if (ops->datbuf && (to + ops->len) > mtd->size) {
-               DEBUG(MTD_DEBUG_LEVEL0, "nand_read_oob: "
-                     "Attempt read beyond end of device\n");
+               DEBUG(MTD_DEBUG_LEVEL0, "nand_write_oob: "
+                     "Attempt write beyond end of device\n");
                return -EINVAL;
        }
 
@@ -2555,6 +2652,9 @@ int nand_scan_tail(struct mtd_info *mtd)
                case 64:
                        chip->ecc.layout = &nand_oob_64;
                        break;
+               case 128:
+                       chip->ecc.layout = &nand_oob_128;
+                       break;
                default:
                        printk(KERN_WARNING "No oob scheme defined for "
                               "oobsize %d\n", mtd->oobsize);
@@ -2569,10 +2669,6 @@ int nand_scan_tail(struct mtd_info *mtd)
         * check ECC mode, default to software if 3byte/512byte hardware ECC is
         * selected and we have 256 byte pagesize fallback to software ECC
         */
-       if (!chip->ecc.read_page_raw)
-               chip->ecc.read_page_raw = nand_read_page_raw;
-       if (!chip->ecc.write_page_raw)
-               chip->ecc.write_page_raw = nand_write_page_raw;
 
        switch (chip->ecc.mode) {
        case NAND_ECC_HW:
@@ -2581,6 +2677,10 @@ int nand_scan_tail(struct mtd_info *mtd)
                        chip->ecc.read_page = nand_read_page_hwecc;
                if (!chip->ecc.write_page)
                        chip->ecc.write_page = nand_write_page_hwecc;
+               if (!chip->ecc.read_page_raw)
+                       chip->ecc.read_page_raw = nand_read_page_raw;
+               if (!chip->ecc.write_page_raw)
+                       chip->ecc.write_page_raw = nand_write_page_raw;
                if (!chip->ecc.read_oob)
                        chip->ecc.read_oob = nand_read_oob_std;
                if (!chip->ecc.write_oob)
@@ -2602,6 +2702,10 @@ int nand_scan_tail(struct mtd_info *mtd)
                        chip->ecc.read_page = nand_read_page_syndrome;
                if (!chip->ecc.write_page)
                        chip->ecc.write_page = nand_write_page_syndrome;
+               if (!chip->ecc.read_page_raw)
+                       chip->ecc.read_page_raw = nand_read_page_raw_syndrome;
+               if (!chip->ecc.write_page_raw)
+                       chip->ecc.write_page_raw = nand_write_page_raw_syndrome;
                if (!chip->ecc.read_oob)
                        chip->ecc.read_oob = nand_read_oob_syndrome;
                if (!chip->ecc.write_oob)
@@ -2620,6 +2724,8 @@ int nand_scan_tail(struct mtd_info *mtd)
                chip->ecc.read_page = nand_read_page_swecc;
                chip->ecc.read_subpage = nand_read_subpage;
                chip->ecc.write_page = nand_write_page_swecc;
+               chip->ecc.read_page_raw = nand_read_page_raw;
+               chip->ecc.write_page_raw = nand_write_page_raw;
                chip->ecc.read_oob = nand_read_oob_std;
                chip->ecc.write_oob = nand_write_oob_std;
                chip->ecc.size = 256;
@@ -2632,6 +2738,8 @@ int nand_scan_tail(struct mtd_info *mtd)
                chip->ecc.read_page = nand_read_page_raw;
                chip->ecc.write_page = nand_write_page_raw;
                chip->ecc.read_oob = nand_read_oob_std;
+               chip->ecc.read_page_raw = nand_read_page_raw;
+               chip->ecc.write_page_raw = nand_write_page_raw;
                chip->ecc.write_oob = nand_write_oob_std;
                chip->ecc.size = mtd->writesize;
                chip->ecc.bytes = 0;
@@ -2676,6 +2784,7 @@ int nand_scan_tail(struct mtd_info *mtd)
                        break;
                case 4:
                case 8:
+               case 16:
                        mtd->subpage_sft = 2;
                        break;
                }
index 75f9f4874ecfad57195220f720181b18c486862f..86e1d08eee00761f05c1edc710fd178cbc44cef4 100644 (file)
@@ -30,7 +30,7 @@ struct plat_nand_data {
 /*
  * Probe for the NAND device.
  */
-static int __init plat_nand_probe(struct platform_device *pdev)
+static int __devinit plat_nand_probe(struct platform_device *pdev)
 {
        struct platform_nand_data *pdata = pdev->dev.platform_data;
        struct plat_nand_data *data;
index 61b69cc4000956cee468f116653bc5766fa1037f..30a8ce6d3e69bcdc1f0429daafacd06115baf06e 100644 (file)
@@ -170,7 +170,13 @@ static int use_dma = 1;
 module_param(use_dma, bool, 0444);
 MODULE_PARM_DESC(use_dma, "enable DMA for data transfering to/from NAND HW");
 
-#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
+/*
+ * Default NAND flash controller configuration setup by the
+ * bootloader. This configuration is used only when pdata->keep_config is set
+ */
+static struct pxa3xx_nand_timing default_timing;
+static struct pxa3xx_nand_flash default_flash;
+
 static struct pxa3xx_nand_cmdset smallpage_cmdset = {
        .read1          = 0x0000,
        .read2          = 0x0050,
@@ -197,6 +203,7 @@ static struct pxa3xx_nand_cmdset largepage_cmdset = {
        .lock_status    = 0x007A,
 };
 
+#ifdef CONFIG_MTD_NAND_PXA3xx_BUILTIN
 static struct pxa3xx_nand_timing samsung512MbX16_timing = {
        .tCH    = 10,
        .tCS    = 0,
@@ -296,9 +303,23 @@ static struct pxa3xx_nand_flash *builtin_flash_types[] = {
 #define NDTR1_tWHR(c)  (min((c), 15) << 4)
 #define NDTR1_tAR(c)   (min((c), 15) << 0)
 
+#define tCH_NDTR0(r)   (((r) >> 19) & 0x7)
+#define tCS_NDTR0(r)   (((r) >> 16) & 0x7)
+#define tWH_NDTR0(r)   (((r) >> 11) & 0x7)
+#define tWP_NDTR0(r)   (((r) >> 8) & 0x7)
+#define tRH_NDTR0(r)   (((r) >> 3) & 0x7)
+#define tRP_NDTR0(r)   (((r) >> 0) & 0x7)
+
+#define tR_NDTR1(r)    (((r) >> 16) & 0xffff)
+#define tWHR_NDTR1(r)  (((r) >> 4) & 0xf)
+#define tAR_NDTR1(r)   (((r) >> 0) & 0xf)
+
 /* convert nano-seconds to nand flash controller clock cycles */
 #define ns2cycle(ns, clk)      (int)(((ns) * (clk / 1000000) / 1000) - 1)
 
+/* convert nand flash controller clock cycles to nano-seconds */
+#define cycle2ns(c, clk)       ((((c) + 1) * 1000000 + clk / 500) / (clk / 1000))
+
 static void pxa3xx_nand_set_timing(struct pxa3xx_nand_info *info,
                                   const struct pxa3xx_nand_timing *t)
 {
@@ -920,6 +941,82 @@ static int pxa3xx_nand_config_flash(struct pxa3xx_nand_info *info,
        return 0;
 }
 
+static void pxa3xx_nand_detect_timing(struct pxa3xx_nand_info *info,
+                                     struct pxa3xx_nand_timing *t)
+{
+       unsigned long nand_clk = clk_get_rate(info->clk);
+       uint32_t ndtr0 = nand_readl(info, NDTR0CS0);
+       uint32_t ndtr1 = nand_readl(info, NDTR1CS0);
+
+       t->tCH = cycle2ns(tCH_NDTR0(ndtr0), nand_clk);
+       t->tCS = cycle2ns(tCS_NDTR0(ndtr0), nand_clk);
+       t->tWH = cycle2ns(tWH_NDTR0(ndtr0), nand_clk);
+       t->tWP = cycle2ns(tWP_NDTR0(ndtr0), nand_clk);
+       t->tRH = cycle2ns(tRH_NDTR0(ndtr0), nand_clk);
+       t->tRP = cycle2ns(tRP_NDTR0(ndtr0), nand_clk);
+
+       t->tR = cycle2ns(tR_NDTR1(ndtr1), nand_clk);
+       t->tWHR = cycle2ns(tWHR_NDTR1(ndtr1), nand_clk);
+       t->tAR = cycle2ns(tAR_NDTR1(ndtr1), nand_clk);
+}
+
+static int pxa3xx_nand_detect_config(struct pxa3xx_nand_info *info)
+{
+       uint32_t ndcr = nand_readl(info, NDCR);
+       struct nand_flash_dev *type = NULL;
+       uint32_t id = -1;
+       int i;
+
+       default_flash.page_per_block = ndcr & NDCR_PG_PER_BLK ? 64 : 32;
+       default_flash.page_size = ndcr & NDCR_PAGE_SZ ? 2048 : 512;
+       default_flash.flash_width = ndcr & NDCR_DWIDTH_M ? 16 : 8;
+       default_flash.dfc_width = ndcr & NDCR_DWIDTH_C ? 16 : 8;
+
+       if (default_flash.page_size == 2048)
+               default_flash.cmdset = &largepage_cmdset;
+       else
+               default_flash.cmdset = &smallpage_cmdset;
+
+       /* set info fields needed to __readid */
+       info->flash_info = &default_flash;
+       info->read_id_bytes = (default_flash.page_size == 2048) ? 4 : 2;
+       info->reg_ndcr = ndcr;
+
+       if (__readid(info, &id))
+               return -ENODEV;
+
+       /* Lookup the flash id */
+       id = (id >> 8) & 0xff;          /* device id is byte 2 */
+       for (i = 0; nand_flash_ids[i].name != NULL; i++) {
+               if (id == nand_flash_ids[i].id) {
+                       type =  &nand_flash_ids[i];
+                       break;
+               }
+       }
+
+       if (!type)
+               return -ENODEV;
+
+       /* fill the missing flash information */
+       i = __ffs(default_flash.page_per_block * default_flash.page_size);
+       default_flash.num_blocks = type->chipsize << (20 - i);
+
+       info->oob_size = (default_flash.page_size == 2048) ? 64 : 16;
+
+       /* calculate addressing information */
+       info->col_addr_cycles = (default_flash.page_size == 2048) ? 2 : 1;
+
+       if (default_flash.num_blocks * default_flash.page_per_block > 65536)
+               info->row_addr_cycles = 3;
+       else
+               info->row_addr_cycles = 2;
+
+       pxa3xx_nand_detect_timing(info, &default_timing);
+       default_flash.timing = &default_timing;
+
+       return 0;
+}
+
 static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
                                    const struct pxa3xx_nand_platform_data *pdata)
 {
@@ -927,6 +1024,10 @@ static int pxa3xx_nand_detect_flash(struct pxa3xx_nand_info *info,
        uint32_t id = -1;
        int i;
 
+       if (pdata->keep_config)
+               if (pxa3xx_nand_detect_config(info) == 0)
+                       return 0;
+
        for (i = 0; i<pdata->num_flash; ++i) {
                f = pdata->flash + i;
 
@@ -1078,6 +1179,7 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
 
        this = &info->nand_chip;
        mtd->priv = info;
+       mtd->owner = THIS_MODULE;
 
        info->clk = clk_get(&pdev->dev, NULL);
        if (IS_ERR(info->clk)) {
@@ -1117,14 +1219,14 @@ static int pxa3xx_nand_probe(struct platform_device *pdev)
                goto fail_put_clk;
        }
 
-       r = request_mem_region(r->start, r->end - r->start + 1, pdev->name);
+       r = request_mem_region(r->start, resource_size(r), pdev->name);
        if (r == NULL) {
                dev_err(&pdev->dev, "failed to request memory resource\n");
                ret = -EBUSY;
                goto fail_put_clk;
        }
 
-       info->mmio_base = ioremap(r->start, r->end - r->start + 1);
+       info->mmio_base = ioremap(r->start, resource_size(r));
        if (info->mmio_base == NULL) {
                dev_err(&pdev->dev, "ioremap() failed\n");
                ret = -ENODEV;
@@ -1173,7 +1275,7 @@ fail_free_buf:
 fail_free_io:
        iounmap(info->mmio_base);
 fail_free_res:
-       release_mem_region(r->start, r->end - r->start + 1);
+       release_mem_region(r->start, resource_size(r));
 fail_put_clk:
        clk_disable(info->clk);
        clk_put(info->clk);
@@ -1186,6 +1288,7 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
 {
        struct mtd_info *mtd = platform_get_drvdata(pdev);
        struct pxa3xx_nand_info *info = mtd->priv;
+       struct resource *r;
 
        platform_set_drvdata(pdev, NULL);
 
@@ -1198,6 +1301,14 @@ static int pxa3xx_nand_remove(struct platform_device *pdev)
                                info->data_buff, info->data_buff_phys);
        } else
                kfree(info->data_buff);
+
+       iounmap(info->mmio_base);
+       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       release_mem_region(r->start, resource_size(r));
+
+       clk_disable(info->clk);
+       clk_put(info->clk);
+
        kfree(mtd);
        return 0;
 }
index 821acb08ff1cc5441be0e0e1c307830c54619450..2bc896623e2d7e21ea5d76977ce3417958293c6f 100644 (file)
@@ -58,7 +58,7 @@ static struct nand_bbt_descr flctl_4secc_smallpage = {
 };
 
 static struct nand_bbt_descr flctl_4secc_largepage = {
-       .options = 0,
+       .options = NAND_BBT_SCAN2NDPAGE,
        .offs = 58,
        .len = 2,
        .pattern = scan_ff_pattern,
@@ -149,7 +149,7 @@ static void wait_wfifo_ready(struct sh_flctl *flctl)
        printk(KERN_ERR "wait_wfifo_ready(): Timeout occured \n");
 }
 
-static int wait_recfifo_ready(struct sh_flctl *flctl)
+static int wait_recfifo_ready(struct sh_flctl *flctl, int sector_number)
 {
        uint32_t timeout = LOOP_TIMEOUT_MAX;
        int checked[4];
@@ -183,7 +183,12 @@ static int wait_recfifo_ready(struct sh_flctl *flctl)
                                uint8_t org;
                                int index;
 
-                               index = data >> 16;
+                               if (flctl->page_size)
+                                       index = (512 * sector_number) +
+                                               (data >> 16);
+                               else
+                                       index = data >> 16;
+
                                org = flctl->done_buff[index];
                                flctl->done_buff[index] = org ^ (data & 0xFF);
                                checked[i] = 1;
@@ -238,14 +243,14 @@ static void read_fiforeg(struct sh_flctl *flctl, int rlen, int offset)
        }
 }
 
-static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff)
+static int read_ecfiforeg(struct sh_flctl *flctl, uint8_t *buff, int sector)
 {
        int i;
        unsigned long *ecc_buf = (unsigned long *)buff;
        void *fifo_addr = (void *)FLECFIFO(flctl);
 
        for (i = 0; i < 4; i++) {
-               if (wait_recfifo_ready(flctl))
+               if (wait_recfifo_ready(flctl , sector))
                        return 1;
                ecc_buf[i] = readl(fifo_addr);
                ecc_buf[i] = be32_to_cpu(ecc_buf[i]);
@@ -384,7 +389,8 @@ static void execmd_read_page_sector(struct mtd_info *mtd, int page_addr)
                read_fiforeg(flctl, 512, 512 * sector);
 
                ret = read_ecfiforeg(flctl,
-                       &flctl->done_buff[mtd->writesize + 16 * sector]);
+                       &flctl->done_buff[mtd->writesize + 16 * sector],
+                       sector);
 
                if (ret)
                        flctl->hwecc_cant_correct[sector] = 1;
diff --git a/drivers/mtd/nand/socrates_nand.c b/drivers/mtd/nand/socrates_nand.c
new file mode 100644 (file)
index 0000000..a4519a7
--- /dev/null
@@ -0,0 +1,325 @@
+/*
+ * drivers/mtd/nand/socrates_nand.c
+ *
+ *  Copyright Â© 2008 Ilya Yanok, Emcraft Systems
+ *
+ *
+ * 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 <linux/slab.h>
+#include <linux/module.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/partitions.h>
+#include <linux/of_platform.h>
+#include <linux/io.h>
+
+#define FPGA_NAND_CMD_MASK             (0x7 << 28)
+#define FPGA_NAND_CMD_COMMAND          (0x0 << 28)
+#define FPGA_NAND_CMD_ADDR             (0x1 << 28)
+#define FPGA_NAND_CMD_READ             (0x2 << 28)
+#define FPGA_NAND_CMD_WRITE            (0x3 << 28)
+#define FPGA_NAND_BUSY                 (0x1 << 15)
+#define FPGA_NAND_ENABLE               (0x1 << 31)
+#define FPGA_NAND_DATA_SHIFT           16
+
+struct socrates_nand_host {
+       struct nand_chip        nand_chip;
+       struct mtd_info         mtd;
+       void __iomem            *io_base;
+       struct device           *dev;
+};
+
+/**
+ * socrates_nand_write_buf -  write buffer to chip
+ * @mtd:       MTD device structure
+ * @buf:       data buffer
+ * @len:       number of bytes to write
+ */
+static void socrates_nand_write_buf(struct mtd_info *mtd,
+               const uint8_t *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+       struct socrates_nand_host *host = this->priv;
+
+       for (i = 0; i < len; i++) {
+               out_be32(host->io_base, FPGA_NAND_ENABLE |
+                               FPGA_NAND_CMD_WRITE |
+                               (buf[i] << FPGA_NAND_DATA_SHIFT));
+       }
+}
+
+/**
+ * socrates_nand_read_buf -  read chip data into buffer
+ * @mtd:       MTD device structure
+ * @buf:       buffer to store date
+ * @len:       number of bytes to read
+ */
+static void socrates_nand_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       int i;
+       struct nand_chip *this = mtd->priv;
+       struct socrates_nand_host *host = this->priv;
+       uint32_t val;
+
+       val = FPGA_NAND_ENABLE | FPGA_NAND_CMD_READ;
+
+       out_be32(host->io_base, val);
+       for (i = 0; i < len; i++) {
+               buf[i] = (in_be32(host->io_base) >>
+                               FPGA_NAND_DATA_SHIFT) & 0xff;
+       }
+}
+
+/**
+ * socrates_nand_read_byte -  read one byte from the chip
+ * @mtd:       MTD device structure
+ */
+static uint8_t socrates_nand_read_byte(struct mtd_info *mtd)
+{
+       uint8_t byte;
+       socrates_nand_read_buf(mtd, &byte, sizeof(byte));
+       return byte;
+}
+
+/**
+ * socrates_nand_read_word -  read one word from the chip
+ * @mtd:       MTD device structure
+ */
+static uint16_t socrates_nand_read_word(struct mtd_info *mtd)
+{
+       uint16_t word;
+       socrates_nand_read_buf(mtd, (uint8_t *)&word, sizeof(word));
+       return word;
+}
+
+/**
+ * socrates_nand_verify_buf -  Verify chip data against buffer
+ * @mtd:       MTD device structure
+ * @buf:       buffer containing the data to compare
+ * @len:       number of bytes to compare
+ */
+static int socrates_nand_verify_buf(struct mtd_info *mtd, const u8 *buf,
+               int len)
+{
+       int i;
+
+       for (i = 0; i < len; i++) {
+               if (buf[i] != socrates_nand_read_byte(mtd))
+                       return -EFAULT;
+       }
+       return 0;
+}
+
+/*
+ * Hardware specific access to control-lines
+ */
+static void socrates_nand_cmd_ctrl(struct mtd_info *mtd, int cmd,
+               unsigned int ctrl)
+{
+       struct nand_chip *nand_chip = mtd->priv;
+       struct socrates_nand_host *host = nand_chip->priv;
+       uint32_t val;
+
+       if (cmd == NAND_CMD_NONE)
+               return;
+
+       if (ctrl & NAND_CLE)
+               val = FPGA_NAND_CMD_COMMAND;
+       else
+               val = FPGA_NAND_CMD_ADDR;
+
+       if (ctrl & NAND_NCE)
+               val |= FPGA_NAND_ENABLE;
+
+       val |= (cmd & 0xff) << FPGA_NAND_DATA_SHIFT;
+
+       out_be32(host->io_base, val);
+}
+
+/*
+ * Read the Device Ready pin.
+ */
+static int socrates_nand_device_ready(struct mtd_info *mtd)
+{
+       struct nand_chip *nand_chip = mtd->priv;
+       struct socrates_nand_host *host = nand_chip->priv;
+
+       if (in_be32(host->io_base) & FPGA_NAND_BUSY)
+               return 0; /* busy */
+       return 1;
+}
+
+#ifdef CONFIG_MTD_PARTITIONS
+static const char *part_probes[] = { "cmdlinepart", NULL };
+#endif
+
+/*
+ * Probe for the NAND device.
+ */
+static int __devinit socrates_nand_probe(struct of_device *ofdev,
+                                        const struct of_device_id *ofid)
+{
+       struct socrates_nand_host *host;
+       struct mtd_info *mtd;
+       struct nand_chip *nand_chip;
+       int res;
+
+#ifdef CONFIG_MTD_PARTITIONS
+       struct mtd_partition *partitions = NULL;
+       int num_partitions = 0;
+#endif
+
+       /* Allocate memory for the device structure (and zero it) */
+       host = kzalloc(sizeof(struct socrates_nand_host), GFP_KERNEL);
+       if (!host) {
+               printk(KERN_ERR
+                      "socrates_nand: failed to allocate device structure.\n");
+               return -ENOMEM;
+       }
+
+       host->io_base = of_iomap(ofdev->node, 0);
+       if (host->io_base == NULL) {
+               printk(KERN_ERR "socrates_nand: ioremap failed\n");
+               kfree(host);
+               return -EIO;
+       }
+
+       mtd = &host->mtd;
+       nand_chip = &host->nand_chip;
+       host->dev = &ofdev->dev;
+
+       nand_chip->priv = host;         /* link the private data structures */
+       mtd->priv = nand_chip;
+       mtd->name = "socrates_nand";
+       mtd->owner = THIS_MODULE;
+       mtd->dev.parent = &ofdev->dev;
+
+       /*should never be accessed directly */
+       nand_chip->IO_ADDR_R = (void *)0xdeadbeef;
+       nand_chip->IO_ADDR_W = (void *)0xdeadbeef;
+
+       nand_chip->cmd_ctrl = socrates_nand_cmd_ctrl;
+       nand_chip->read_byte = socrates_nand_read_byte;
+       nand_chip->read_word = socrates_nand_read_word;
+       nand_chip->write_buf = socrates_nand_write_buf;
+       nand_chip->read_buf = socrates_nand_read_buf;
+       nand_chip->verify_buf = socrates_nand_verify_buf;
+       nand_chip->dev_ready = socrates_nand_device_ready;
+
+       nand_chip->ecc.mode = NAND_ECC_SOFT;    /* enable ECC */
+
+       /* TODO: I have no idea what real delay is. */
+       nand_chip->chip_delay = 20;             /* 20us command delay time */
+
+       dev_set_drvdata(&ofdev->dev, host);
+
+       /* first scan to find the device and get the page size */
+       if (nand_scan_ident(mtd, 1)) {
+               res = -ENXIO;
+               goto out;
+       }
+
+       /* second phase scan */
+       if (nand_scan_tail(mtd)) {
+               res = -ENXIO;
+               goto out;
+       }
+
+#ifdef CONFIG_MTD_PARTITIONS
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+       num_partitions = parse_mtd_partitions(mtd, part_probes,
+                                             &partitions, 0);
+       if (num_partitions < 0) {
+               res = num_partitions;
+               goto release;
+       }
+#endif
+
+#ifdef CONFIG_MTD_OF_PARTS
+       if (num_partitions == 0) {
+               num_partitions = of_mtd_parse_partitions(&ofdev->dev,
+                                                        ofdev->node,
+                                                        &partitions);
+               if (num_partitions < 0) {
+                       res = num_partitions;
+                       goto release;
+               }
+       }
+#endif
+       if (partitions && (num_partitions > 0))
+               res = add_mtd_partitions(mtd, partitions, num_partitions);
+       else
+#endif
+               res = add_mtd_device(mtd);
+
+       if (!res)
+               return res;
+
+#ifdef CONFIG_MTD_PARTITIONS
+release:
+#endif
+       nand_release(mtd);
+
+out:
+       dev_set_drvdata(&ofdev->dev, NULL);
+       iounmap(host->io_base);
+       kfree(host);
+       return res;
+}
+
+/*
+ * Remove a NAND device.
+ */
+static int __devexit socrates_nand_remove(struct of_device *ofdev)
+{
+       struct socrates_nand_host *host = dev_get_drvdata(&ofdev->dev);
+       struct mtd_info *mtd = &host->mtd;
+
+       nand_release(mtd);
+
+       dev_set_drvdata(&ofdev->dev, NULL);
+       iounmap(host->io_base);
+       kfree(host);
+
+       return 0;
+}
+
+static struct of_device_id socrates_nand_match[] =
+{
+       {
+               .compatible   = "abb,socrates-nand",
+       },
+       {},
+};
+
+MODULE_DEVICE_TABLE(of, socrates_nand_match);
+
+static struct of_platform_driver socrates_nand_driver = {
+       .name           = "socrates_nand",
+       .match_table    = socrates_nand_match,
+       .probe          = socrates_nand_probe,
+       .remove         = __devexit_p(socrates_nand_remove),
+};
+
+static int __init socrates_nand_init(void)
+{
+       return of_register_platform_driver(&socrates_nand_driver);
+}
+
+static void __exit socrates_nand_exit(void)
+{
+       of_unregister_platform_driver(&socrates_nand_driver);
+}
+
+module_init(socrates_nand_init);
+module_exit(socrates_nand_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_AUTHOR("Ilya Yanok");
+MODULE_DESCRIPTION("NAND driver for Socrates board");
diff --git a/drivers/mtd/nand/txx9ndfmc.c b/drivers/mtd/nand/txx9ndfmc.c
new file mode 100644 (file)
index 0000000..8124792
--- /dev/null
@@ -0,0 +1,428 @@
+/*
+ * TXx9 NAND flash memory controller driver
+ * Based on RBTX49xx patch from CELF patch archive.
+ *
+ * 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.
+ *
+ * (C) Copyright TOSHIBA CORPORATION 2004-2007
+ * All Rights Reserved.
+ */
+#include <linux/init.h>
+#include <linux/slab.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/mtd/mtd.h>
+#include <linux/mtd/nand.h>
+#include <linux/mtd/nand_ecc.h>
+#include <linux/mtd/partitions.h>
+#include <linux/io.h>
+#include <asm/txx9/ndfmc.h>
+
+/* TXX9 NDFMC Registers */
+#define TXX9_NDFDTR    0x00
+#define TXX9_NDFMCR    0x04
+#define TXX9_NDFSR     0x08
+#define TXX9_NDFISR    0x0c
+#define TXX9_NDFIMR    0x10
+#define TXX9_NDFSPR    0x14
+#define TXX9_NDFRSTR   0x18    /* not TX4939 */
+
+/* NDFMCR : NDFMC Mode Control */
+#define TXX9_NDFMCR_WE 0x80
+#define TXX9_NDFMCR_ECC_ALL    0x60
+#define TXX9_NDFMCR_ECC_RESET  0x60
+#define TXX9_NDFMCR_ECC_READ   0x40
+#define TXX9_NDFMCR_ECC_ON     0x20
+#define TXX9_NDFMCR_ECC_OFF    0x00
+#define TXX9_NDFMCR_CE 0x10
+#define TXX9_NDFMCR_BSPRT      0x04    /* TX4925/TX4926 only */
+#define TXX9_NDFMCR_ALE        0x02
+#define TXX9_NDFMCR_CLE        0x01
+/* TX4939 only */
+#define TXX9_NDFMCR_X16        0x0400
+#define TXX9_NDFMCR_DMAREQ_MASK        0x0300
+#define TXX9_NDFMCR_DMAREQ_NODMA       0x0000
+#define TXX9_NDFMCR_DMAREQ_128 0x0100
+#define TXX9_NDFMCR_DMAREQ_256 0x0200
+#define TXX9_NDFMCR_DMAREQ_512 0x0300
+#define TXX9_NDFMCR_CS_MASK    0x0c
+#define TXX9_NDFMCR_CS(ch)     ((ch) << 2)
+
+/* NDFMCR : NDFMC Status */
+#define TXX9_NDFSR_BUSY        0x80
+/* TX4939 only */
+#define TXX9_NDFSR_DMARUN      0x40
+
+/* NDFMCR : NDFMC Reset */
+#define TXX9_NDFRSTR_RST       0x01
+
+struct txx9ndfmc_priv {
+       struct platform_device *dev;
+       struct nand_chip chip;
+       struct mtd_info mtd;
+       int cs;
+       char mtdname[BUS_ID_SIZE + 2];
+};
+
+#define MAX_TXX9NDFMC_DEV      4
+struct txx9ndfmc_drvdata {
+       struct mtd_info *mtds[MAX_TXX9NDFMC_DEV];
+       void __iomem *base;
+       unsigned char hold;     /* in gbusclock */
+       unsigned char spw;      /* in gbusclock */
+       struct nand_hw_control hw_control;
+#ifdef CONFIG_MTD_PARTITIONS
+       struct mtd_partition *parts[MAX_TXX9NDFMC_DEV];
+#endif
+};
+
+static struct platform_device *mtd_to_platdev(struct mtd_info *mtd)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct txx9ndfmc_priv *txx9_priv = chip->priv;
+       return txx9_priv->dev;
+}
+
+static void __iomem *ndregaddr(struct platform_device *dev, unsigned int reg)
+{
+       struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
+       struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
+
+       return drvdata->base + (reg << plat->shift);
+}
+
+static u32 txx9ndfmc_read(struct platform_device *dev, unsigned int reg)
+{
+       return __raw_readl(ndregaddr(dev, reg));
+}
+
+static void txx9ndfmc_write(struct platform_device *dev,
+                           u32 val, unsigned int reg)
+{
+       __raw_writel(val, ndregaddr(dev, reg));
+}
+
+static uint8_t txx9ndfmc_read_byte(struct mtd_info *mtd)
+{
+       struct platform_device *dev = mtd_to_platdev(mtd);
+
+       return txx9ndfmc_read(dev, TXX9_NDFDTR);
+}
+
+static void txx9ndfmc_write_buf(struct mtd_info *mtd, const uint8_t *buf,
+                               int len)
+{
+       struct platform_device *dev = mtd_to_platdev(mtd);
+       void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
+       u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
+
+       txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_WE, TXX9_NDFMCR);
+       while (len--)
+               __raw_writel(*buf++, ndfdtr);
+       txx9ndfmc_write(dev, mcr, TXX9_NDFMCR);
+}
+
+static void txx9ndfmc_read_buf(struct mtd_info *mtd, uint8_t *buf, int len)
+{
+       struct platform_device *dev = mtd_to_platdev(mtd);
+       void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
+
+       while (len--)
+               *buf++ = __raw_readl(ndfdtr);
+}
+
+static int txx9ndfmc_verify_buf(struct mtd_info *mtd, const uint8_t *buf,
+                               int len)
+{
+       struct platform_device *dev = mtd_to_platdev(mtd);
+       void __iomem *ndfdtr = ndregaddr(dev, TXX9_NDFDTR);
+
+       while (len--)
+               if (*buf++ != (uint8_t)__raw_readl(ndfdtr))
+                       return -EFAULT;
+       return 0;
+}
+
+static void txx9ndfmc_cmd_ctrl(struct mtd_info *mtd, int cmd,
+                              unsigned int ctrl)
+{
+       struct nand_chip *chip = mtd->priv;
+       struct txx9ndfmc_priv *txx9_priv = chip->priv;
+       struct platform_device *dev = txx9_priv->dev;
+       struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
+
+       if (ctrl & NAND_CTRL_CHANGE) {
+               u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
+
+               mcr &= ~(TXX9_NDFMCR_CLE | TXX9_NDFMCR_ALE | TXX9_NDFMCR_CE);
+               mcr |= ctrl & NAND_CLE ? TXX9_NDFMCR_CLE : 0;
+               mcr |= ctrl & NAND_ALE ? TXX9_NDFMCR_ALE : 0;
+               /* TXX9_NDFMCR_CE bit is 0:high 1:low */
+               mcr |= ctrl & NAND_NCE ? TXX9_NDFMCR_CE : 0;
+               if (txx9_priv->cs >= 0 && (ctrl & NAND_NCE)) {
+                       mcr &= ~TXX9_NDFMCR_CS_MASK;
+                       mcr |= TXX9_NDFMCR_CS(txx9_priv->cs);
+               }
+               txx9ndfmc_write(dev, mcr, TXX9_NDFMCR);
+       }
+       if (cmd != NAND_CMD_NONE)
+               txx9ndfmc_write(dev, cmd & 0xff, TXX9_NDFDTR);
+       if (plat->flags & NDFMC_PLAT_FLAG_DUMMYWRITE) {
+               /* dummy write to update external latch */
+               if ((ctrl & NAND_CTRL_CHANGE) && cmd == NAND_CMD_NONE)
+                       txx9ndfmc_write(dev, 0, TXX9_NDFDTR);
+       }
+       mmiowb();
+}
+
+static int txx9ndfmc_dev_ready(struct mtd_info *mtd)
+{
+       struct platform_device *dev = mtd_to_platdev(mtd);
+
+       return !(txx9ndfmc_read(dev, TXX9_NDFSR) & TXX9_NDFSR_BUSY);
+}
+
+static int txx9ndfmc_calculate_ecc(struct mtd_info *mtd, const uint8_t *dat,
+                                  uint8_t *ecc_code)
+{
+       struct platform_device *dev = mtd_to_platdev(mtd);
+       u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
+
+       mcr &= ~TXX9_NDFMCR_ECC_ALL;
+       txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
+       txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_READ, TXX9_NDFMCR);
+       ecc_code[1] = txx9ndfmc_read(dev, TXX9_NDFDTR);
+       ecc_code[0] = txx9ndfmc_read(dev, TXX9_NDFDTR);
+       ecc_code[2] = txx9ndfmc_read(dev, TXX9_NDFDTR);
+       txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
+       return 0;
+}
+
+static void txx9ndfmc_enable_hwecc(struct mtd_info *mtd, int mode)
+{
+       struct platform_device *dev = mtd_to_platdev(mtd);
+       u32 mcr = txx9ndfmc_read(dev, TXX9_NDFMCR);
+
+       mcr &= ~TXX9_NDFMCR_ECC_ALL;
+       txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_RESET, TXX9_NDFMCR);
+       txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_OFF, TXX9_NDFMCR);
+       txx9ndfmc_write(dev, mcr | TXX9_NDFMCR_ECC_ON, TXX9_NDFMCR);
+}
+
+static void txx9ndfmc_initialize(struct platform_device *dev)
+{
+       struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
+       struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
+       int tmout = 100;
+
+       if (plat->flags & NDFMC_PLAT_FLAG_NO_RSTR)
+               ; /* no NDFRSTR.  Write to NDFSPR resets the NDFMC. */
+       else {
+               /* reset NDFMC */
+               txx9ndfmc_write(dev,
+                               txx9ndfmc_read(dev, TXX9_NDFRSTR) |
+                               TXX9_NDFRSTR_RST,
+                               TXX9_NDFRSTR);
+               while (txx9ndfmc_read(dev, TXX9_NDFRSTR) & TXX9_NDFRSTR_RST) {
+                       if (--tmout == 0) {
+                               dev_err(&dev->dev, "reset failed.\n");
+                               break;
+                       }
+                       udelay(1);
+               }
+       }
+       /* setup Hold Time, Strobe Pulse Width */
+       txx9ndfmc_write(dev, (drvdata->hold << 4) | drvdata->spw, TXX9_NDFSPR);
+       txx9ndfmc_write(dev,
+                       (plat->flags & NDFMC_PLAT_FLAG_USE_BSPRT) ?
+                       TXX9_NDFMCR_BSPRT : 0, TXX9_NDFMCR);
+}
+
+#define TXX9NDFMC_NS_TO_CYC(gbusclk, ns) \
+       DIV_ROUND_UP((ns) * DIV_ROUND_UP(gbusclk, 1000), 1000000)
+
+static int __init txx9ndfmc_probe(struct platform_device *dev)
+{
+       struct txx9ndfmc_platform_data *plat = dev->dev.platform_data;
+#ifdef CONFIG_MTD_PARTITIONS
+       static const char *probes[] = { "cmdlinepart", NULL };
+#endif
+       int hold, spw;
+       int i;
+       struct txx9ndfmc_drvdata *drvdata;
+       unsigned long gbusclk = plat->gbus_clock;
+       struct resource *res;
+
+       res = platform_get_resource(dev, IORESOURCE_MEM, 0);
+       if (!res)
+               return -ENODEV;
+       drvdata = devm_kzalloc(&dev->dev, sizeof(*drvdata), GFP_KERNEL);
+       if (!drvdata)
+               return -ENOMEM;
+       if (!devm_request_mem_region(&dev->dev, res->start,
+                                    resource_size(res), dev_name(&dev->dev)))
+               return -EBUSY;
+       drvdata->base = devm_ioremap(&dev->dev, res->start,
+                                    resource_size(res));
+       if (!drvdata->base)
+               return -EBUSY;
+
+       hold = plat->hold ?: 20; /* tDH */
+       spw = plat->spw ?: 90; /* max(tREADID, tWP, tRP) */
+
+       hold = TXX9NDFMC_NS_TO_CYC(gbusclk, hold);
+       spw = TXX9NDFMC_NS_TO_CYC(gbusclk, spw);
+       if (plat->flags & NDFMC_PLAT_FLAG_HOLDADD)
+               hold -= 2;      /* actual hold time : (HOLD + 2) BUSCLK */
+       spw -= 1;       /* actual wait time : (SPW + 1) BUSCLK */
+       hold = clamp(hold, 1, 15);
+       drvdata->hold = hold;
+       spw = clamp(spw, 1, 15);
+       drvdata->spw = spw;
+       dev_info(&dev->dev, "CLK:%ldMHz HOLD:%d SPW:%d\n",
+                (gbusclk + 500000) / 1000000, hold, spw);
+
+       spin_lock_init(&drvdata->hw_control.lock);
+       init_waitqueue_head(&drvdata->hw_control.wq);
+
+       platform_set_drvdata(dev, drvdata);
+       txx9ndfmc_initialize(dev);
+
+       for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
+               struct txx9ndfmc_priv *txx9_priv;
+               struct nand_chip *chip;
+               struct mtd_info *mtd;
+#ifdef CONFIG_MTD_PARTITIONS
+               int nr_parts;
+#endif
+
+               if (!(plat->ch_mask & (1 << i)))
+                       continue;
+               txx9_priv = kzalloc(sizeof(struct txx9ndfmc_priv),
+                                   GFP_KERNEL);
+               if (!txx9_priv) {
+                       dev_err(&dev->dev, "Unable to allocate "
+                               "TXx9 NDFMC MTD device structure.\n");
+                       continue;
+               }
+               chip = &txx9_priv->chip;
+               mtd = &txx9_priv->mtd;
+               mtd->owner = THIS_MODULE;
+
+               mtd->priv = chip;
+
+               chip->read_byte = txx9ndfmc_read_byte;
+               chip->read_buf = txx9ndfmc_read_buf;
+               chip->write_buf = txx9ndfmc_write_buf;
+               chip->verify_buf = txx9ndfmc_verify_buf;
+               chip->cmd_ctrl = txx9ndfmc_cmd_ctrl;
+               chip->dev_ready = txx9ndfmc_dev_ready;
+               chip->ecc.calculate = txx9ndfmc_calculate_ecc;
+               chip->ecc.correct = nand_correct_data;
+               chip->ecc.hwctl = txx9ndfmc_enable_hwecc;
+               chip->ecc.mode = NAND_ECC_HW;
+               chip->ecc.size = 256;
+               chip->ecc.bytes = 3;
+               chip->chip_delay = 100;
+               chip->controller = &drvdata->hw_control;
+
+               chip->priv = txx9_priv;
+               txx9_priv->dev = dev;
+
+               if (plat->ch_mask != 1) {
+                       txx9_priv->cs = i;
+                       sprintf(txx9_priv->mtdname, "%s.%u",
+                               dev_name(&dev->dev), i);
+               } else {
+                       txx9_priv->cs = -1;
+                       strcpy(txx9_priv->mtdname, dev_name(&dev->dev));
+               }
+               if (plat->wide_mask & (1 << i))
+                       chip->options |= NAND_BUSWIDTH_16;
+
+               if (nand_scan(mtd, 1)) {
+                       kfree(txx9_priv);
+                       continue;
+               }
+               mtd->name = txx9_priv->mtdname;
+
+#ifdef CONFIG_MTD_PARTITIONS
+               nr_parts = parse_mtd_partitions(mtd, probes,
+                                               &drvdata->parts[i], 0);
+               if (nr_parts > 0)
+                       add_mtd_partitions(mtd, drvdata->parts[i], nr_parts);
+#endif
+               add_mtd_device(mtd);
+               drvdata->mtds[i] = mtd;
+       }
+
+       return 0;
+}
+
+static int __exit txx9ndfmc_remove(struct platform_device *dev)
+{
+       struct txx9ndfmc_drvdata *drvdata = platform_get_drvdata(dev);
+       int i;
+
+       platform_set_drvdata(dev, NULL);
+       if (!drvdata)
+               return 0;
+       for (i = 0; i < MAX_TXX9NDFMC_DEV; i++) {
+               struct mtd_info *mtd = drvdata->mtds[i];
+               struct nand_chip *chip;
+               struct txx9ndfmc_priv *txx9_priv;
+
+               if (!mtd)
+                       continue;
+               chip = mtd->priv;
+               txx9_priv = chip->priv;
+
+#ifdef CONFIG_MTD_PARTITIONS
+               del_mtd_partitions(mtd);
+               kfree(drvdata->parts[i]);
+#endif
+               del_mtd_device(mtd);
+               kfree(txx9_priv);
+       }
+       return 0;
+}
+
+#ifdef CONFIG_PM
+static int txx9ndfmc_resume(struct platform_device *dev)
+{
+       if (platform_get_drvdata(dev))
+               txx9ndfmc_initialize(dev);
+       return 0;
+}
+#else
+#define txx9ndfmc_resume NULL
+#endif
+
+static struct platform_driver txx9ndfmc_driver = {
+       .remove         = __exit_p(txx9ndfmc_remove),
+       .resume         = txx9ndfmc_resume,
+       .driver         = {
+               .name   = "txx9ndfmc",
+               .owner  = THIS_MODULE,
+       },
+};
+
+static int __init txx9ndfmc_init(void)
+{
+       return platform_driver_probe(&txx9ndfmc_driver, txx9ndfmc_probe);
+}
+
+static void __exit txx9ndfmc_exit(void)
+{
+       platform_driver_unregister(&txx9ndfmc_driver);
+}
+
+module_init(txx9ndfmc_init);
+module_exit(txx9ndfmc_exit);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("TXx9 SoC NAND flash controller driver");
+MODULE_ALIAS("platform:txx9ndfmc");
index d1c4546513f7ac88706646eb8e04083ec4bc465a..e3f8495a94c27492ccaaca85e0a96a1d478a98cd 100644 (file)
 #include <asm/errno.h>
 #include <asm/io.h>
 #include <asm/uaccess.h>
-#include <linux/miscdevice.h>
 #include <linux/delay.h>
 #include <linux/slab.h>
 #include <linux/init.h>
 #include <linux/hdreg.h>
+#include <linux/blkdev.h>
 
 #include <linux/kmod.h>
 #include <linux/mtd/mtd.h>
@@ -818,3 +818,4 @@ module_exit(cleanup_nftl);
 MODULE_LICENSE("GPL");
 MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al.");
 MODULE_DESCRIPTION("Support code for NAND Flash Translation Layer, used on M-Systems DiskOnChip 2000 and Millennium");
+MODULE_ALIAS_BLOCKDEV_MAJOR(NFTL_MAJOR);
index 9e45b3f39c0e7e6cf4f4417f874376e5bfdda9df..3e164f0c9295c7bc779b0b0c72c798f76176c09e 100644 (file)
@@ -46,6 +46,13 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
                const u32 *reg;
                int len;
 
+               /* check if this is a partition node */
+               partname = of_get_property(pp, "name", &len);
+               if (strcmp(partname, "partition") != 0) {
+                       nr_parts--;
+                       continue;
+               }
+
                reg = of_get_property(pp, "reg", &len);
                if (!reg || (len != 2 * sizeof(u32))) {
                        of_node_put(pp);
index 77a4f14461564043da7ee45bfd638bad5974a445..f2e9de1414dfcdb4002419c72b60bf1d1ce4927f 100644 (file)
@@ -294,6 +294,10 @@ static int omap3_onenand_read_bufferram(struct mtd_info *mtd, int area,
        if (bram_offset & 3 || (size_t)buf & 3 || count < 384)
                goto out_copy;
 
+       /* panic_write() may be in an interrupt context */
+       if (in_interrupt())
+               goto out_copy;
+
        if (buf >= high_memory) {
                struct page *p1;
 
@@ -672,6 +676,8 @@ static int __devinit omap2_onenand_probe(struct platform_device *pdev)
        c->mtd.priv = &c->onenand;
        c->mtd.owner = THIS_MODULE;
 
+       c->mtd.dev.parent = &pdev->dev;
+
        if (c->dma_channel >= 0) {
                struct onenand_chip *this = &c->onenand;
 
index 529af271db17486074ef44a6610bd365ace65d28..30d6999e5f9f5a75a22078ee56459798ea0fc865 100644 (file)
@@ -1455,7 +1455,8 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                                struct mtd_oob_ops *ops)
 {
        struct onenand_chip *this = mtd->priv;
-       int written = 0, column, thislen, subpage;
+       int written = 0, column, thislen = 0, subpage = 0;
+       int prev = 0, prevlen = 0, prev_subpage = 0, first = 1;
        int oobwritten = 0, oobcolumn, thisooblen, oobsize;
        size_t len = ops->len;
        size_t ooblen = ops->ooblen;
@@ -1482,6 +1483,10 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
                 return -EINVAL;
         }
 
+       /* Check zero length */
+       if (!len)
+               return 0;
+
        if (ops->mode == MTD_OOB_AUTO)
                oobsize = this->ecclayout->oobavail;
        else
@@ -1492,79 +1497,121 @@ static int onenand_write_ops_nolock(struct mtd_info *mtd, loff_t to,
        column = to & (mtd->writesize - 1);
 
        /* Loop until all data write */
-       while (written < len) {
-               u_char *wbuf = (u_char *) buf;
+       while (1) {
+               if (written < len) {
+                       u_char *wbuf = (u_char *) buf;
 
-               thislen = min_t(int, mtd->writesize - column, len - written);
-               thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
+                       thislen = min_t(int, mtd->writesize - column, len - written);
+                       thisooblen = min_t(int, oobsize - oobcolumn, ooblen - oobwritten);
 
-               cond_resched();
+                       cond_resched();
 
-               this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
+                       this->command(mtd, ONENAND_CMD_BUFFERRAM, to, thislen);
 
-               /* Partial page write */
-               subpage = thislen < mtd->writesize;
-               if (subpage) {
-                       memset(this->page_buf, 0xff, mtd->writesize);
-                       memcpy(this->page_buf + column, buf, thislen);
-                       wbuf = this->page_buf;
-               }
+                       /* Partial page write */
+                       subpage = thislen < mtd->writesize;
+                       if (subpage) {
+                               memset(this->page_buf, 0xff, mtd->writesize);
+                               memcpy(this->page_buf + column, buf, thislen);
+                               wbuf = this->page_buf;
+                       }
 
-               this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
+                       this->write_bufferram(mtd, ONENAND_DATARAM, wbuf, 0, mtd->writesize);
 
-               if (oob) {
-                       oobbuf = this->oob_buf;
+                       if (oob) {
+                               oobbuf = this->oob_buf;
 
-                       /* We send data to spare ram with oobsize
-                        * to prevent byte access */
-                       memset(oobbuf, 0xff, mtd->oobsize);
-                       if (ops->mode == MTD_OOB_AUTO)
-                               onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
-                       else
-                               memcpy(oobbuf + oobcolumn, oob, thisooblen);
+                               /* We send data to spare ram with oobsize
+                                * to prevent byte access */
+                               memset(oobbuf, 0xff, mtd->oobsize);
+                               if (ops->mode == MTD_OOB_AUTO)
+                                       onenand_fill_auto_oob(mtd, oobbuf, oob, oobcolumn, thisooblen);
+                               else
+                                       memcpy(oobbuf + oobcolumn, oob, thisooblen);
 
-                       oobwritten += thisooblen;
-                       oob += thisooblen;
-                       oobcolumn = 0;
+                               oobwritten += thisooblen;
+                               oob += thisooblen;
+                               oobcolumn = 0;
+                       } else
+                               oobbuf = (u_char *) ffchars;
+
+                       this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
                } else
-                       oobbuf = (u_char *) ffchars;
+                       ONENAND_SET_NEXT_BUFFERRAM(this);
 
-               this->write_bufferram(mtd, ONENAND_SPARERAM, oobbuf, 0, mtd->oobsize);
+               /*
+                * 2 PLANE, MLC, and Flex-OneNAND doesn't support
+                * write-while-programe feature.
+                */
+               if (!ONENAND_IS_2PLANE(this) && !first) {
+                       ONENAND_SET_PREV_BUFFERRAM(this);
 
-               this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
+                       ret = this->wait(mtd, FL_WRITING);
 
-               ret = this->wait(mtd, FL_WRITING);
+                       /* In partial page write we don't update bufferram */
+                       onenand_update_bufferram(mtd, prev, !ret && !prev_subpage);
+                       if (ret) {
+                               written -= prevlen;
+                               printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+                               break;
+                       }
 
-               /* In partial page write we don't update bufferram */
-               onenand_update_bufferram(mtd, to, !ret && !subpage);
-               if (ONENAND_IS_2PLANE(this)) {
-                       ONENAND_SET_BUFFERRAM1(this);
-                       onenand_update_bufferram(mtd, to + this->writesize, !ret && !subpage);
-               }
+                       if (written == len) {
+                               /* Only check verify write turn on */
+                               ret = onenand_verify(mtd, buf - len, to - len, len);
+                               if (ret)
+                                       printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
+                               break;
+                       }
 
-               if (ret) {
-                       printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
-                       break;
+                       ONENAND_SET_NEXT_BUFFERRAM(this);
                }
 
-               /* Only check verify write turn on */
-               ret = onenand_verify(mtd, buf, to, thislen);
-               if (ret) {
-                       printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
-                       break;
-               }
+               this->command(mtd, ONENAND_CMD_PROG, to, mtd->writesize);
 
-               written += thislen;
+               /*
+                * 2 PLANE, MLC, and Flex-OneNAND wait here
+                */
+               if (ONENAND_IS_2PLANE(this)) {
+                       ret = this->wait(mtd, FL_WRITING);
 
-               if (written == len)
-                       break;
+                       /* In partial page write we don't update bufferram */
+                       onenand_update_bufferram(mtd, to, !ret && !subpage);
+                       if (ret) {
+                               printk(KERN_ERR "onenand_write_ops_nolock: write filaed %d\n", ret);
+                               break;
+                       }
+
+                       /* Only check verify write turn on */
+                       ret = onenand_verify(mtd, buf, to, thislen);
+                       if (ret) {
+                               printk(KERN_ERR "onenand_write_ops_nolock: verify failed %d\n", ret);
+                               break;
+                       }
+
+                       written += thislen;
+
+                       if (written == len)
+                               break;
+
+               } else
+                       written += thislen;
 
                column = 0;
+               prev_subpage = subpage;
+               prev = to;
+               prevlen = thislen;
                to += thislen;
                buf += thislen;
+               first = 0;
        }
 
+       /* In error case, clear all bufferrams */
+       if (written != len)
+               onenand_invalidate_bufferram(mtd, 0, -1);
+
        ops->retlen = written;
+       ops->oobretlen = oobwritten;
 
        return ret;
 }
index 77ccf8cb0823c805ff9af233aba63404d0df01a2..043740dde20c75fb317bfdedac040abedf9004aa 100644 (file)
@@ -38,12 +38,12 @@ static int jffs2_acl_count(size_t size)
        size_t s;
 
        size -= sizeof(struct jffs2_acl_header);
-       s = size - 4 * sizeof(struct jffs2_acl_entry_short);
-       if (s < 0) {
+       if (size < 4 * sizeof(struct jffs2_acl_entry_short)) {
                if (size % sizeof(struct jffs2_acl_entry_short))
                        return -1;
                return size / sizeof(struct jffs2_acl_entry_short);
        } else {
+               s = size - 4 * sizeof(struct jffs2_acl_entry_short);
                if (s % sizeof(struct jffs2_acl_entry))
                        return -1;
                return s / sizeof(struct jffs2_acl_entry) + 4;
index f9211252b5f16f424fda655a0e5881680fc8eb78..9eff2bdae8a798631cb2c987215e92eaddd75538 100644 (file)
@@ -284,10 +284,9 @@ void jffs2_free_inode_cache(struct jffs2_inode_cache *x)
 struct jffs2_xattr_datum *jffs2_alloc_xattr_datum(void)
 {
        struct jffs2_xattr_datum *xd;
-       xd = kmem_cache_alloc(xattr_datum_cache, GFP_KERNEL);
+       xd = kmem_cache_zalloc(xattr_datum_cache, GFP_KERNEL);
        dbg_memalloc("%p\n", xd);
 
-       memset(xd, 0, sizeof(struct jffs2_xattr_datum));
        xd->class = RAWNODE_CLASS_XATTR_DATUM;
        xd->node = (void *)xd;
        INIT_LIST_HEAD(&xd->xindex);
@@ -303,10 +302,9 @@ void jffs2_free_xattr_datum(struct jffs2_xattr_datum *xd)
 struct jffs2_xattr_ref *jffs2_alloc_xattr_ref(void)
 {
        struct jffs2_xattr_ref *ref;
-       ref = kmem_cache_alloc(xattr_ref_cache, GFP_KERNEL);
+       ref = kmem_cache_zalloc(xattr_ref_cache, GFP_KERNEL);
        dbg_memalloc("%p\n", ref);
 
-       memset(ref, 0, sizeof(struct jffs2_xattr_ref));
        ref->class = RAWNODE_CLASS_XATTR_REF;
        ref->node = (void *)ref;
        return ref;
index 1a17020f9faf3a40c4e52e8bdbf75f3ffa327bce..ce2d6bcc6266b4e663ddc0608354141aca4a75b7 100644 (file)
@@ -1,6 +1,6 @@
 config ROMFS_FS
        tristate "ROM file system support"
-       depends on BLOCK
+       depends on BLOCK || MTD
        ---help---
          This is a very small read-only file system mainly intended for
          initial ram disks of installation disks, but it could be used for
@@ -14,3 +14,49 @@ config ROMFS_FS
 
          If you don't know whether you need it, then you don't need it:
          answer N.
+
+#
+# Select the backing stores to be supported
+#
+choice
+       prompt "RomFS backing stores"
+       depends on ROMFS_FS
+       default ROMFS_BACKED_BY_BLOCK
+       help
+         Select the backing stores to be supported.
+
+config ROMFS_BACKED_BY_BLOCK
+       bool "Block device-backed ROM file system support"
+       depends on BLOCK
+       help
+         This permits ROMFS to use block devices buffered through the page
+         cache as the medium from which to retrieve data.  It does not allow
+         direct mapping of the medium.
+
+         If unsure, answer Y.
+
+config ROMFS_BACKED_BY_MTD
+       bool "MTD-backed ROM file system support"
+       depends on MTD=y || (ROMFS_FS=m && MTD)
+       help
+         This permits ROMFS to use MTD based devices directly, without the
+         intercession of the block layer (which may have been disabled).  It
+         also allows direct mapping of MTD devices through romfs files under
+         NOMMU conditions if the underlying device is directly addressable by
+         the CPU.
+
+         If unsure, answer Y.
+
+config ROMFS_BACKED_BY_BOTH
+       bool "Both the above"
+       depends on BLOCK && (MTD=y || (ROMFS_FS=m && MTD))
+endchoice
+
+
+config ROMFS_ON_BLOCK
+       bool
+       default y if ROMFS_BACKED_BY_BLOCK || ROMFS_BACKED_BY_BOTH
+
+config ROMFS_ON_MTD
+       bool
+       default y if ROMFS_BACKED_BY_MTD || ROMFS_BACKED_BY_BOTH
index c95b21cf49a389b59f858df4465b4d1241b84d7f..420beb7d495cdcbedf917815a85ff905bf5b785f 100644 (file)
@@ -1,7 +1,12 @@
 #
-# Makefile for the linux romfs filesystem routines.
+# Makefile for the linux RomFS filesystem routines.
 #
 
 obj-$(CONFIG_ROMFS_FS) += romfs.o
 
-romfs-objs := inode.o
+romfs-y := storage.o super.o
+
+ifneq ($(CONFIG_MMU),y)
+romfs-$(CONFIG_ROMFS_ON_MTD) += mmap-nommu.o
+endif
+
diff --git a/fs/romfs/inode.c b/fs/romfs/inode.c
deleted file mode 100644 (file)
index 98a232f..0000000
+++ /dev/null
@@ -1,665 +0,0 @@
-/*
- * ROMFS file system, Linux implementation
- *
- * Copyright (C) 1997-1999  Janos Farkas <chexum@shadow.banki.hu>
- *
- * Using parts of the minix filesystem
- * Copyright (C) 1991, 1992  Linus Torvalds
- *
- * and parts of the affs filesystem additionally
- * Copyright (C) 1993  Ray Burr
- * Copyright (C) 1996  Hans-Joachim Widmaier
- *
- * 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.
- *
- * Changes
- *                                     Changed for 2.1.19 modules
- *     Jan 1997                        Initial release
- *     Jun 1997                        2.1.43+ changes
- *                                     Proper page locking in readpage
- *                                     Changed to work with 2.1.45+ fs
- *     Jul 1997                        Fixed follow_link
- *                     2.1.47
- *                                     lookup shouldn't return -ENOENT
- *                                     from Horst von Brand:
- *                                       fail on wrong checksum
- *                                       double unlock_super was possible
- *                                       correct namelen for statfs
- *                                     spotted by Bill Hawes:
- *                                       readlink shouldn't iput()
- *     Jun 1998        2.1.106         from Avery Pennarun: glibc scandir()
- *                                       exposed a problem in readdir
- *                     2.1.107         code-freeze spellchecker run
- *     Aug 1998                        2.1.118+ VFS changes
- *     Sep 1998        2.1.122         another VFS change (follow_link)
- *     Apr 1999        2.2.7           no more EBADF checking in
- *                                       lookup/readdir, use ERR_PTR
- *     Jun 1999        2.3.6           d_alloc_root use changed
- *                     2.3.9           clean up usage of ENOENT/negative
- *                                       dentries in lookup
- *                                     clean up page flags setting
- *                                       (error, uptodate, locking) in
- *                                       in readpage
- *                                     use init_special_inode for
- *                                       fifos/sockets (and streamline) in
- *                                       read_inode, fix _ops table order
- *     Aug 1999        2.3.16          __initfunc() => __init change
- *     Oct 1999        2.3.24          page->owner hack obsoleted
- *     Nov 1999        2.3.27          2.3.25+ page->offset => index change
- */
-
-/* todo:
- *     - see Documentation/filesystems/romfs.txt
- *     - use allocated, not stack memory for file names?
- *     - considering write access...
- *     - network (tftp) files?
- *     - merge back some _op tables
- */
-
-/*
- * Sorry about some optimizations and for some goto's.  I just wanted
- * to squeeze some more bytes out of this code.. :)
- */
-
-#include <linux/module.h>
-#include <linux/types.h>
-#include <linux/errno.h>
-#include <linux/slab.h>
-#include <linux/romfs_fs.h>
-#include <linux/fs.h>
-#include <linux/init.h>
-#include <linux/pagemap.h>
-#include <linux/smp_lock.h>
-#include <linux/buffer_head.h>
-#include <linux/vfs.h>
-
-#include <asm/uaccess.h>
-
-struct romfs_inode_info {
-       unsigned long i_metasize;       /* size of non-data area */
-       unsigned long i_dataoffset;     /* from the start of fs */
-       struct inode vfs_inode;
-};
-
-static struct inode *romfs_iget(struct super_block *, unsigned long);
-
-/* instead of private superblock data */
-static inline unsigned long romfs_maxsize(struct super_block *sb)
-{
-       return (unsigned long)sb->s_fs_info;
-}
-
-static inline struct romfs_inode_info *ROMFS_I(struct inode *inode)
-{
-       return container_of(inode, struct romfs_inode_info, vfs_inode);
-}
-
-static __u32
-romfs_checksum(void *data, int size)
-{
-       __u32 sum;
-       __be32 *ptr;
-
-       sum = 0; ptr = data;
-       size>>=2;
-       while (size>0) {
-               sum += be32_to_cpu(*ptr++);
-               size--;
-       }
-       return sum;
-}
-
-static const struct super_operations romfs_ops;
-
-static int romfs_fill_super(struct super_block *s, void *data, int silent)
-{
-       struct buffer_head *bh;
-       struct romfs_super_block *rsb;
-       struct inode *root;
-       int sz, ret = -EINVAL;
-
-       /* I would parse the options here, but there are none.. :) */
-
-       sb_set_blocksize(s, ROMBSIZE);
-       s->s_maxbytes = 0xFFFFFFFF;
-
-       bh = sb_bread(s, 0);
-       if (!bh) {
-               /* XXX merge with other printk? */
-                printk ("romfs: unable to read superblock\n");
-               goto outnobh;
-       }
-
-       rsb = (struct romfs_super_block *)bh->b_data;
-       sz = be32_to_cpu(rsb->size);
-       if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1
-          || sz < ROMFH_SIZE) {
-               if (!silent)
-                       printk ("VFS: Can't find a romfs filesystem on dev "
-                               "%s.\n", s->s_id);
-               goto out;
-       }
-       if (romfs_checksum(rsb, min_t(int, sz, 512))) {
-               printk ("romfs: bad initial checksum on dev "
-                       "%s.\n", s->s_id);
-               goto out;
-       }
-
-       s->s_magic = ROMFS_MAGIC;
-       s->s_fs_info = (void *)(long)sz;
-
-       s->s_flags |= MS_RDONLY;
-
-       /* Find the start of the fs */
-       sz = (ROMFH_SIZE +
-             strnlen(rsb->name, ROMFS_MAXFN) + 1 + ROMFH_PAD)
-            & ROMFH_MASK;
-
-       s->s_op = &romfs_ops;
-       root = romfs_iget(s, sz);
-       if (IS_ERR(root)) {
-               ret = PTR_ERR(root);
-               goto out;
-       }
-
-       ret = -ENOMEM;
-       s->s_root = d_alloc_root(root);
-       if (!s->s_root)
-               goto outiput;
-
-       brelse(bh);
-       return 0;
-
-outiput:
-       iput(root);
-out:
-       brelse(bh);
-outnobh:
-       return ret;
-}
-
-/* That's simple too. */
-
-static int
-romfs_statfs(struct dentry *dentry, struct kstatfs *buf)
-{
-       buf->f_type = ROMFS_MAGIC;
-       buf->f_bsize = ROMBSIZE;
-       buf->f_bfree = buf->f_bavail = buf->f_ffree;
-       buf->f_blocks = (romfs_maxsize(dentry->d_sb)+ROMBSIZE-1)>>ROMBSBITS;
-       buf->f_namelen = ROMFS_MAXFN;
-       return 0;
-}
-
-/* some helper routines */
-
-static int
-romfs_strnlen(struct inode *i, unsigned long offset, unsigned long count)
-{
-       struct buffer_head *bh;
-       unsigned long avail, maxsize, res;
-
-       maxsize = romfs_maxsize(i->i_sb);
-       if (offset >= maxsize)
-               return -1;
-
-       /* strnlen is almost always valid */
-       if (count > maxsize || offset+count > maxsize)
-               count = maxsize-offset;
-
-       bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
-       if (!bh)
-               return -1;              /* error */
-
-       avail = ROMBSIZE - (offset & ROMBMASK);
-       maxsize = min_t(unsigned long, count, avail);
-       res = strnlen(((char *)bh->b_data)+(offset&ROMBMASK), maxsize);
-       brelse(bh);
-
-       if (res < maxsize)
-               return res;             /* found all of it */
-
-       while (res < count) {
-               offset += maxsize;
-
-               bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
-               if (!bh)
-                       return -1;
-               maxsize = min_t(unsigned long, count - res, ROMBSIZE);
-               avail = strnlen(bh->b_data, maxsize);
-               res += avail;
-               brelse(bh);
-               if (avail < maxsize)
-                       return res;
-       }
-       return res;
-}
-
-static int
-romfs_copyfrom(struct inode *i, void *dest, unsigned long offset, unsigned long count)
-{
-       struct buffer_head *bh;
-       unsigned long avail, maxsize, res;
-
-       maxsize = romfs_maxsize(i->i_sb);
-       if (offset >= maxsize || count > maxsize || offset+count>maxsize)
-               return -1;
-
-       bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
-       if (!bh)
-               return -1;              /* error */
-
-       avail = ROMBSIZE - (offset & ROMBMASK);
-       maxsize = min_t(unsigned long, count, avail);
-       memcpy(dest, ((char *)bh->b_data) + (offset & ROMBMASK), maxsize);
-       brelse(bh);
-
-       res = maxsize;                  /* all of it */
-
-       while (res < count) {
-               offset += maxsize;
-               dest += maxsize;
-
-               bh = sb_bread(i->i_sb, offset>>ROMBSBITS);
-               if (!bh)
-                       return -1;
-               maxsize = min_t(unsigned long, count - res, ROMBSIZE);
-               memcpy(dest, bh->b_data, maxsize);
-               brelse(bh);
-               res += maxsize;
-       }
-       return res;
-}
-
-static unsigned char romfs_dtype_table[] = {
-       DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO
-};
-
-static int
-romfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
-{
-       struct inode *i = filp->f_path.dentry->d_inode;
-       struct romfs_inode ri;
-       unsigned long offset, maxoff;
-       int j, ino, nextfh;
-       int stored = 0;
-       char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
-
-       lock_kernel();
-
-       maxoff = romfs_maxsize(i->i_sb);
-
-       offset = filp->f_pos;
-       if (!offset) {
-               offset = i->i_ino & ROMFH_MASK;
-               if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
-                       goto out;
-               offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
-       }
-
-       /* Not really failsafe, but we are read-only... */
-       for(;;) {
-               if (!offset || offset >= maxoff) {
-                       offset = maxoff;
-                       filp->f_pos = offset;
-                       goto out;
-               }
-               filp->f_pos = offset;
-
-               /* Fetch inode info */
-               if (romfs_copyfrom(i, &ri, offset, ROMFH_SIZE) <= 0)
-                       goto out;
-
-               j = romfs_strnlen(i, offset+ROMFH_SIZE, sizeof(fsname)-1);
-               if (j < 0)
-                       goto out;
-
-               fsname[j]=0;
-               romfs_copyfrom(i, fsname, offset+ROMFH_SIZE, j);
-
-               ino = offset;
-               nextfh = be32_to_cpu(ri.next);
-               if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
-                       ino = be32_to_cpu(ri.spec);
-               if (filldir(dirent, fsname, j, offset, ino,
-                           romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0) {
-                       goto out;
-               }
-               stored++;
-               offset = nextfh & ROMFH_MASK;
-       }
-out:
-       unlock_kernel();
-       return stored;
-}
-
-static struct dentry *
-romfs_lookup(struct inode *dir, struct dentry *dentry, struct nameidata *nd)
-{
-       unsigned long offset, maxoff;
-       long res;
-       int fslen;
-       struct inode *inode = NULL;
-       char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
-       struct romfs_inode ri;
-       const char *name;               /* got from dentry */
-       int len;
-
-       res = -EACCES;                  /* placeholder for "no data here" */
-       offset = dir->i_ino & ROMFH_MASK;
-       lock_kernel();
-       if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
-               goto error;
-
-       maxoff = romfs_maxsize(dir->i_sb);
-       offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
-
-       /* OK, now find the file whose name is in "dentry" in the
-        * directory specified by "dir".  */
-
-       name = dentry->d_name.name;
-       len = dentry->d_name.len;
-
-       for(;;) {
-               if (!offset || offset >= maxoff)
-                       goto success; /* negative success */
-               if (romfs_copyfrom(dir, &ri, offset, ROMFH_SIZE) <= 0)
-                       goto error;
-
-               /* try to match the first 16 bytes of name */
-               fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, ROMFH_SIZE);
-               if (len < ROMFH_SIZE) {
-                       if (len == fslen) {
-                               /* both are shorter, and same size */
-                               romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
-                               if (strncmp (name, fsname, len) == 0)
-                                       break;
-                       }
-               } else if (fslen >= ROMFH_SIZE) {
-                       /* both are longer; XXX optimize max size */
-                       fslen = romfs_strnlen(dir, offset+ROMFH_SIZE, sizeof(fsname)-1);
-                       if (len == fslen) {
-                               romfs_copyfrom(dir, fsname, offset+ROMFH_SIZE, len+1);
-                               if (strncmp(name, fsname, len) == 0)
-                                       break;
-                       }
-               }
-               /* next entry */
-               offset = be32_to_cpu(ri.next) & ROMFH_MASK;
-       }
-
-       /* Hard link handling */
-       if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
-               offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
-
-       inode = romfs_iget(dir->i_sb, offset);
-       if (IS_ERR(inode)) {
-               res = PTR_ERR(inode);
-               goto error;
-       }
-
-success:
-       d_add(dentry, inode);
-       res = 0;
-error:
-       unlock_kernel();
-       return ERR_PTR(res);
-}
-
-/*
- * Ok, we do readpage, to be able to execute programs.  Unfortunately,
- * we can't use bmap, since we may have looser alignments.
- */
-
-static int
-romfs_readpage(struct file *file, struct page * page)
-{
-       struct inode *inode = page->mapping->host;
-       loff_t offset, size;
-       unsigned long filled;
-       void *buf;
-       int result = -EIO;
-
-       page_cache_get(page);
-       lock_kernel();
-       buf = kmap(page);
-       if (!buf)
-               goto err_out;
-
-       /* 32 bit warning -- but not for us :) */
-       offset = page_offset(page);
-       size = i_size_read(inode);
-       filled = 0;
-       result = 0;
-       if (offset < size) {
-               unsigned long readlen;
-
-               size -= offset;
-               readlen = size > PAGE_SIZE ? PAGE_SIZE : size;
-
-               filled = romfs_copyfrom(inode, buf, ROMFS_I(inode)->i_dataoffset+offset, readlen);
-
-               if (filled != readlen) {
-                       SetPageError(page);
-                       filled = 0;
-                       result = -EIO;
-               }
-       }
-
-       if (filled < PAGE_SIZE)
-               memset(buf + filled, 0, PAGE_SIZE-filled);
-
-       if (!result)
-               SetPageUptodate(page);
-       flush_dcache_page(page);
-
-       unlock_page(page);
-
-       kunmap(page);
-err_out:
-       page_cache_release(page);
-       unlock_kernel();
-
-       return result;
-}
-
-/* Mapping from our types to the kernel */
-
-static const struct address_space_operations romfs_aops = {
-       .readpage = romfs_readpage
-};
-
-static const struct file_operations romfs_dir_operations = {
-       .read           = generic_read_dir,
-       .readdir        = romfs_readdir,
-};
-
-static const struct inode_operations romfs_dir_inode_operations = {
-       .lookup         = romfs_lookup,
-};
-
-static mode_t romfs_modemap[] =
-{
-       0, S_IFDIR+0644, S_IFREG+0644, S_IFLNK+0777,
-       S_IFBLK+0600, S_IFCHR+0600, S_IFSOCK+0644, S_IFIFO+0644
-};
-
-static struct inode *
-romfs_iget(struct super_block *sb, unsigned long ino)
-{
-       int nextfh, ret;
-       struct romfs_inode ri;
-       struct inode *i;
-
-       ino &= ROMFH_MASK;
-       i = iget_locked(sb, ino);
-       if (!i)
-               return ERR_PTR(-ENOMEM);
-       if (!(i->i_state & I_NEW))
-               return i;
-
-       i->i_mode = 0;
-
-       /* Loop for finding the real hard link */
-       for(;;) {
-               if (romfs_copyfrom(i, &ri, ino, ROMFH_SIZE) <= 0) {
-                       printk(KERN_ERR "romfs: read error for inode 0x%lx\n",
-                               ino);
-                       iget_failed(i);
-                       return ERR_PTR(-EIO);
-               }
-               /* XXX: do romfs_checksum here too (with name) */
-
-               nextfh = be32_to_cpu(ri.next);
-               if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
-                       break;
-
-               ino = be32_to_cpu(ri.spec) & ROMFH_MASK;
-       }
-
-       i->i_nlink = 1;         /* Hard to decide.. */
-       i->i_size = be32_to_cpu(ri.size);
-       i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0;
-       i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0;
-
-        /* Precalculate the data offset */
-       ret = romfs_strnlen(i, ino + ROMFH_SIZE, ROMFS_MAXFN);
-       if (ret >= 0)
-               ino = (ROMFH_SIZE + ret + 1 + ROMFH_PAD) & ROMFH_MASK;
-       else
-               ino = 0;
-
-        ROMFS_I(i)->i_metasize = ino;
-        ROMFS_I(i)->i_dataoffset = ino+(i->i_ino&ROMFH_MASK);
-
-        /* Compute permissions */
-        ino = romfs_modemap[nextfh & ROMFH_TYPE];
-       /* only "normal" files have ops */
-       switch (nextfh & ROMFH_TYPE) {
-               case 1:
-                       i->i_size = ROMFS_I(i)->i_metasize;
-                       i->i_op = &romfs_dir_inode_operations;
-                       i->i_fop = &romfs_dir_operations;
-                       if (nextfh & ROMFH_EXEC)
-                               ino |= S_IXUGO;
-                       i->i_mode = ino;
-                       break;
-               case 2:
-                       i->i_fop = &generic_ro_fops;
-                       i->i_data.a_ops = &romfs_aops;
-                       if (nextfh & ROMFH_EXEC)
-                               ino |= S_IXUGO;
-                       i->i_mode = ino;
-                       break;
-               case 3:
-                       i->i_op = &page_symlink_inode_operations;
-                       i->i_data.a_ops = &romfs_aops;
-                       i->i_mode = ino | S_IRWXUGO;
-                       break;
-               default:
-                       /* depending on MBZ for sock/fifos */
-                       nextfh = be32_to_cpu(ri.spec);
-                       init_special_inode(i, ino,
-                                       MKDEV(nextfh>>16,nextfh&0xffff));
-       }
-       unlock_new_inode(i);
-       return i;
-}
-
-static struct kmem_cache * romfs_inode_cachep;
-
-static struct inode *romfs_alloc_inode(struct super_block *sb)
-{
-       struct romfs_inode_info *ei;
-       ei = kmem_cache_alloc(romfs_inode_cachep, GFP_KERNEL);
-       if (!ei)
-               return NULL;
-       return &ei->vfs_inode;
-}
-
-static void romfs_destroy_inode(struct inode *inode)
-{
-       kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode));
-}
-
-static void init_once(void *foo)
-{
-       struct romfs_inode_info *ei = foo;
-
-       inode_init_once(&ei->vfs_inode);
-}
-
-static int init_inodecache(void)
-{
-       romfs_inode_cachep = kmem_cache_create("romfs_inode_cache",
-                                            sizeof(struct romfs_inode_info),
-                                            0, (SLAB_RECLAIM_ACCOUNT|
-                                               SLAB_MEM_SPREAD),
-                                            init_once);
-       if (romfs_inode_cachep == NULL)
-               return -ENOMEM;
-       return 0;
-}
-
-static void destroy_inodecache(void)
-{
-       kmem_cache_destroy(romfs_inode_cachep);
-}
-
-static int romfs_remount(struct super_block *sb, int *flags, char *data)
-{
-       *flags |= MS_RDONLY;
-       return 0;
-}
-
-static const struct super_operations romfs_ops = {
-       .alloc_inode    = romfs_alloc_inode,
-       .destroy_inode  = romfs_destroy_inode,
-       .statfs         = romfs_statfs,
-       .remount_fs     = romfs_remount,
-};
-
-static int romfs_get_sb(struct file_system_type *fs_type,
-       int flags, const char *dev_name, void *data, struct vfsmount *mnt)
-{
-       return get_sb_bdev(fs_type, flags, dev_name, data, romfs_fill_super,
-                          mnt);
-}
-
-static struct file_system_type romfs_fs_type = {
-       .owner          = THIS_MODULE,
-       .name           = "romfs",
-       .get_sb         = romfs_get_sb,
-       .kill_sb        = kill_block_super,
-       .fs_flags       = FS_REQUIRES_DEV,
-};
-
-static int __init init_romfs_fs(void)
-{
-       int err = init_inodecache();
-       if (err)
-               goto out1;
-        err = register_filesystem(&romfs_fs_type);
-       if (err)
-               goto out;
-       return 0;
-out:
-       destroy_inodecache();
-out1:
-       return err;
-}
-
-static void __exit exit_romfs_fs(void)
-{
-       unregister_filesystem(&romfs_fs_type);
-       destroy_inodecache();
-}
-
-/* Yes, works even as a module... :) */
-
-module_init(init_romfs_fs)
-module_exit(exit_romfs_fs)
-MODULE_LICENSE("GPL");
diff --git a/fs/romfs/internal.h b/fs/romfs/internal.h
new file mode 100644 (file)
index 0000000..06044a9
--- /dev/null
@@ -0,0 +1,47 @@
+/* RomFS internal definitions
+ *
+ * Copyright Â© 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/romfs_fs.h>
+
+struct romfs_inode_info {
+       struct inode    vfs_inode;
+       unsigned long   i_metasize;     /* size of non-data area */
+       unsigned long   i_dataoffset;   /* from the start of fs */
+};
+
+static inline size_t romfs_maxsize(struct super_block *sb)
+{
+       return (size_t) (unsigned long) sb->s_fs_info;
+}
+
+static inline struct romfs_inode_info *ROMFS_I(struct inode *inode)
+{
+       return container_of(inode, struct romfs_inode_info, vfs_inode);
+}
+
+/*
+ * mmap-nommu.c
+ */
+#if !defined(CONFIG_MMU) && defined(CONFIG_ROMFS_ON_MTD)
+extern const struct file_operations romfs_ro_fops;
+#else
+#define romfs_ro_fops  generic_ro_fops
+#endif
+
+/*
+ * storage.c
+ */
+extern int romfs_dev_read(struct super_block *sb, unsigned long pos,
+                         void *buf, size_t buflen);
+extern ssize_t romfs_dev_strnlen(struct super_block *sb,
+                                unsigned long pos, size_t maxlen);
+extern int romfs_dev_strncmp(struct super_block *sb, unsigned long pos,
+                            const char *str, size_t size);
diff --git a/fs/romfs/mmap-nommu.c b/fs/romfs/mmap-nommu.c
new file mode 100644 (file)
index 0000000..f0511e8
--- /dev/null
@@ -0,0 +1,75 @@
+/* NOMMU mmap support for RomFS on MTD devices
+ *
+ * Copyright Â© 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/mm.h>
+#include <linux/mtd/super.h>
+#include "internal.h"
+
+/*
+ * try to determine where a shared mapping can be made
+ * - only supported for NOMMU at the moment (MMU can't doesn't copy private
+ *   mappings)
+ * - attempts to map through to the underlying MTD device
+ */
+static unsigned long romfs_get_unmapped_area(struct file *file,
+                                            unsigned long addr,
+                                            unsigned long len,
+                                            unsigned long pgoff,
+                                            unsigned long flags)
+{
+       struct inode *inode = file->f_mapping->host;
+       struct mtd_info *mtd = inode->i_sb->s_mtd;
+       unsigned long isize, offset;
+
+       if (!mtd)
+               goto cant_map_directly;
+
+       isize = i_size_read(inode);
+       offset = pgoff << PAGE_SHIFT;
+       if (offset > isize || len > isize || offset > isize - len)
+               return (unsigned long) -EINVAL;
+
+       /* we need to call down to the MTD layer to do the actual mapping */
+       if (mtd->get_unmapped_area) {
+               if (addr != 0)
+                       return (unsigned long) -EINVAL;
+
+               if (len > mtd->size || pgoff >= (mtd->size >> PAGE_SHIFT))
+                       return (unsigned long) -EINVAL;
+
+               offset += ROMFS_I(inode)->i_dataoffset;
+               if (offset > mtd->size - len)
+                       return (unsigned long) -EINVAL;
+
+               return mtd->get_unmapped_area(mtd, len, offset, flags);
+       }
+
+cant_map_directly:
+       return (unsigned long) -ENOSYS;
+}
+
+/*
+ * permit a R/O mapping to be made directly through onto an MTD device if
+ * possible
+ */
+static int romfs_mmap(struct file *file, struct vm_area_struct *vma)
+{
+       return vma->vm_flags & (VM_SHARED | VM_MAYSHARE) ? 0 : -ENOSYS;
+}
+
+const struct file_operations romfs_ro_fops = {
+       .llseek                 = generic_file_llseek,
+       .read                   = do_sync_read,
+       .aio_read               = generic_file_aio_read,
+       .splice_read            = generic_file_splice_read,
+       .mmap                   = romfs_mmap,
+       .get_unmapped_area      = romfs_get_unmapped_area,
+};
diff --git a/fs/romfs/storage.c b/fs/romfs/storage.c
new file mode 100644 (file)
index 0000000..7e3e1e1
--- /dev/null
@@ -0,0 +1,261 @@
+/* RomFS storage access routines
+ *
+ * Copyright Â© 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <linux/fs.h>
+#include <linux/mtd/super.h>
+#include <linux/buffer_head.h>
+#include "internal.h"
+
+#if !defined(CONFIG_ROMFS_ON_MTD) && !defined(CONFIG_ROMFS_ON_BLOCK)
+#error no ROMFS backing store interface configured
+#endif
+
+#ifdef CONFIG_ROMFS_ON_MTD
+#define ROMFS_MTD_READ(sb, ...) ((sb)->s_mtd->read((sb)->s_mtd, ##__VA_ARGS__))
+
+/*
+ * read data from an romfs image on an MTD device
+ */
+static int romfs_mtd_read(struct super_block *sb, unsigned long pos,
+                         void *buf, size_t buflen)
+{
+       size_t rlen;
+       int ret;
+
+       ret = ROMFS_MTD_READ(sb, pos, buflen, &rlen, buf);
+       return (ret < 0 || rlen != buflen) ? -EIO : 0;
+}
+
+/*
+ * determine the length of a string in a romfs image on an MTD device
+ */
+static ssize_t romfs_mtd_strnlen(struct super_block *sb,
+                                unsigned long pos, size_t maxlen)
+{
+       ssize_t n = 0;
+       size_t segment;
+       u_char buf[16], *p;
+       size_t len;
+       int ret;
+
+       /* scan the string up to 16 bytes at a time */
+       while (maxlen > 0) {
+               segment = min_t(size_t, maxlen, 16);
+               ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
+               if (ret < 0)
+                       return ret;
+               p = memchr(buf, 0, len);
+               if (p)
+                       return n + (p - buf);
+               maxlen -= len;
+               pos += len;
+               n += len;
+       }
+
+       return n;
+}
+
+/*
+ * compare a string to one in a romfs image on MTD
+ * - return 1 if matched, 0 if differ, -ve if error
+ */
+static int romfs_mtd_strncmp(struct super_block *sb, unsigned long pos,
+                            const char *str, size_t size)
+{
+       u_char buf[16];
+       size_t len, segment;
+       int ret;
+
+       /* scan the string up to 16 bytes at a time */
+       while (size > 0) {
+               segment = min_t(size_t, size, 16);
+               ret = ROMFS_MTD_READ(sb, pos, segment, &len, buf);
+               if (ret < 0)
+                       return ret;
+               if (memcmp(buf, str, len) != 0)
+                       return 0;
+               size -= len;
+               pos += len;
+               str += len;
+       }
+
+       return 1;
+}
+#endif /* CONFIG_ROMFS_ON_MTD */
+
+#ifdef CONFIG_ROMFS_ON_BLOCK
+/*
+ * read data from an romfs image on a block device
+ */
+static int romfs_blk_read(struct super_block *sb, unsigned long pos,
+                         void *buf, size_t buflen)
+{
+       struct buffer_head *bh;
+       unsigned long offset;
+       size_t segment;
+
+       /* copy the string up to blocksize bytes at a time */
+       while (buflen > 0) {
+               offset = pos & (ROMBSIZE - 1);
+               segment = min_t(size_t, buflen, ROMBSIZE - offset);
+               bh = sb_bread(sb, pos >> ROMBSBITS);
+               if (!bh)
+                       return -EIO;
+               memcpy(buf, bh->b_data + offset, segment);
+               brelse(bh);
+               buflen -= segment;
+               pos += segment;
+       }
+
+       return 0;
+}
+
+/*
+ * determine the length of a string in romfs on a block device
+ */
+static ssize_t romfs_blk_strnlen(struct super_block *sb,
+                                unsigned long pos, size_t limit)
+{
+       struct buffer_head *bh;
+       unsigned long offset;
+       ssize_t n = 0;
+       size_t segment;
+       u_char *buf, *p;
+
+       /* scan the string up to blocksize bytes at a time */
+       while (limit > 0) {
+               offset = pos & (ROMBSIZE - 1);
+               segment = min_t(size_t, limit, ROMBSIZE - offset);
+               bh = sb_bread(sb, pos >> ROMBSBITS);
+               if (!bh)
+                       return -EIO;
+               buf = bh->b_data + offset;
+               p = memchr(buf, 0, segment);
+               brelse(bh);
+               if (p)
+                       return n + (p - buf);
+               limit -= segment;
+               pos += segment;
+               n += segment;
+       }
+
+       return n;
+}
+
+/*
+ * compare a string to one in a romfs image on a block device
+ * - return 1 if matched, 0 if differ, -ve if error
+ */
+static int romfs_blk_strncmp(struct super_block *sb, unsigned long pos,
+                            const char *str, size_t size)
+{
+       struct buffer_head *bh;
+       unsigned long offset;
+       size_t segment;
+       bool x;
+
+       /* scan the string up to 16 bytes at a time */
+       while (size > 0) {
+               offset = pos & (ROMBSIZE - 1);
+               segment = min_t(size_t, size, ROMBSIZE - offset);
+               bh = sb_bread(sb, pos >> ROMBSBITS);
+               if (!bh)
+                       return -EIO;
+               x = (memcmp(bh->b_data + offset, str, segment) != 0);
+               brelse(bh);
+               if (x)
+                       return 0;
+               size -= segment;
+               pos += segment;
+               str += segment;
+       }
+
+       return 1;
+}
+#endif /* CONFIG_ROMFS_ON_BLOCK */
+
+/*
+ * read data from the romfs image
+ */
+int romfs_dev_read(struct super_block *sb, unsigned long pos,
+                  void *buf, size_t buflen)
+{
+       size_t limit;
+
+       limit = romfs_maxsize(sb);
+       if (pos >= limit)
+               return -EIO;
+       if (buflen > limit - pos)
+               buflen = limit - pos;
+
+#ifdef CONFIG_ROMFS_ON_MTD
+       if (sb->s_mtd)
+               return romfs_mtd_read(sb, pos, buf, buflen);
+#endif
+#ifdef CONFIG_ROMFS_ON_BLOCK
+       if (sb->s_bdev)
+               return romfs_blk_read(sb, pos, buf, buflen);
+#endif
+       return -EIO;
+}
+
+/*
+ * determine the length of a string in romfs
+ */
+ssize_t romfs_dev_strnlen(struct super_block *sb,
+                         unsigned long pos, size_t maxlen)
+{
+       size_t limit;
+
+       limit = romfs_maxsize(sb);
+       if (pos >= limit)
+               return -EIO;
+       if (maxlen > limit - pos)
+               maxlen = limit - pos;
+
+#ifdef CONFIG_ROMFS_ON_MTD
+       if (sb->s_mtd)
+               return romfs_mtd_strnlen(sb, pos, limit);
+#endif
+#ifdef CONFIG_ROMFS_ON_BLOCK
+       if (sb->s_bdev)
+               return romfs_blk_strnlen(sb, pos, limit);
+#endif
+       return -EIO;
+}
+
+/*
+ * compare a string to one in romfs
+ * - return 1 if matched, 0 if differ, -ve if error
+ */
+int romfs_dev_strncmp(struct super_block *sb, unsigned long pos,
+                     const char *str, size_t size)
+{
+       size_t limit;
+
+       limit = romfs_maxsize(sb);
+       if (pos >= limit)
+               return -EIO;
+       if (size > ROMFS_MAXFN)
+               return -ENAMETOOLONG;
+       if (size > limit - pos)
+               return -EIO;
+
+#ifdef CONFIG_ROMFS_ON_MTD
+       if (sb->s_mtd)
+               return romfs_mtd_strncmp(sb, pos, str, size);
+#endif
+#ifdef CONFIG_ROMFS_ON_BLOCK
+       if (sb->s_bdev)
+               return romfs_blk_strncmp(sb, pos, str, size);
+#endif
+       return -EIO;
+}
diff --git a/fs/romfs/super.c b/fs/romfs/super.c
new file mode 100644 (file)
index 0000000..1e548a4
--- /dev/null
@@ -0,0 +1,648 @@
+/* Block- or MTD-based romfs
+ *
+ * Copyright Â© 2007 Red Hat, Inc. All Rights Reserved.
+ * Written by David Howells (dhowells@redhat.com)
+ *
+ * Derived from: ROMFS file system, Linux implementation
+ *
+ * Copyright Â© 1997-1999  Janos Farkas <chexum@shadow.banki.hu>
+ *
+ * Using parts of the minix filesystem
+ * Copyright Â© 1991, 1992  Linus Torvalds
+ *
+ * and parts of the affs filesystem additionally
+ * Copyright Â© 1993  Ray Burr
+ * Copyright Â© 1996  Hans-Joachim Widmaier
+ *
+ * Changes
+ *                                     Changed for 2.1.19 modules
+ *     Jan 1997                        Initial release
+ *     Jun 1997                        2.1.43+ changes
+ *                                     Proper page locking in readpage
+ *                                     Changed to work with 2.1.45+ fs
+ *     Jul 1997                        Fixed follow_link
+ *                     2.1.47
+ *                                     lookup shouldn't return -ENOENT
+ *                                     from Horst von Brand:
+ *                                       fail on wrong checksum
+ *                                       double unlock_super was possible
+ *                                       correct namelen for statfs
+ *                                     spotted by Bill Hawes:
+ *                                       readlink shouldn't iput()
+ *     Jun 1998        2.1.106         from Avery Pennarun: glibc scandir()
+ *                                       exposed a problem in readdir
+ *                     2.1.107         code-freeze spellchecker run
+ *     Aug 1998                        2.1.118+ VFS changes
+ *     Sep 1998        2.1.122         another VFS change (follow_link)
+ *     Apr 1999        2.2.7           no more EBADF checking in
+ *                                       lookup/readdir, use ERR_PTR
+ *     Jun 1999        2.3.6           d_alloc_root use changed
+ *                     2.3.9           clean up usage of ENOENT/negative
+ *                                       dentries in lookup
+ *                                     clean up page flags setting
+ *                                       (error, uptodate, locking) in
+ *                                       in readpage
+ *                                     use init_special_inode for
+ *                                       fifos/sockets (and streamline) in
+ *                                       read_inode, fix _ops table order
+ *     Aug 1999        2.3.16          __initfunc() => __init change
+ *     Oct 1999        2.3.24          page->owner hack obsoleted
+ *     Nov 1999        2.3.27          2.3.25+ page->offset => index change
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public Licence
+ * as published by the Free Software Foundation; either version
+ * 2 of the Licence, or (at your option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/string.h>
+#include <linux/fs.h>
+#include <linux/time.h>
+#include <linux/slab.h>
+#include <linux/init.h>
+#include <linux/blkdev.h>
+#include <linux/parser.h>
+#include <linux/mount.h>
+#include <linux/namei.h>
+#include <linux/statfs.h>
+#include <linux/mtd/super.h>
+#include <linux/ctype.h>
+#include <linux/highmem.h>
+#include <linux/pagemap.h>
+#include <linux/uaccess.h>
+#include "internal.h"
+
+static struct kmem_cache *romfs_inode_cachep;
+
+static const umode_t romfs_modemap[8] = {
+       0,                      /* hard link */
+       S_IFDIR  | 0644,        /* directory */
+       S_IFREG  | 0644,        /* regular file */
+       S_IFLNK  | 0777,        /* symlink */
+       S_IFBLK  | 0600,        /* blockdev */
+       S_IFCHR  | 0600,        /* chardev */
+       S_IFSOCK | 0644,        /* socket */
+       S_IFIFO  | 0644         /* FIFO */
+};
+
+static const unsigned char romfs_dtype_table[] = {
+       DT_UNKNOWN, DT_DIR, DT_REG, DT_LNK, DT_BLK, DT_CHR, DT_SOCK, DT_FIFO
+};
+
+static struct inode *romfs_iget(struct super_block *sb, unsigned long pos);
+
+/*
+ * read a page worth of data from the image
+ */
+static int romfs_readpage(struct file *file, struct page *page)
+{
+       struct inode *inode = page->mapping->host;
+       loff_t offset, size;
+       unsigned long fillsize, pos;
+       void *buf;
+       int ret;
+
+       buf = kmap(page);
+       if (!buf)
+               return -ENOMEM;
+
+       /* 32 bit warning -- but not for us :) */
+       offset = page_offset(page);
+       size = i_size_read(inode);
+       fillsize = 0;
+       ret = 0;
+       if (offset < size) {
+               size -= offset;
+               fillsize = size > PAGE_SIZE ? PAGE_SIZE : size;
+
+               pos = ROMFS_I(inode)->i_dataoffset + offset;
+
+               ret = romfs_dev_read(inode->i_sb, pos, buf, fillsize);
+               if (ret < 0) {
+                       SetPageError(page);
+                       fillsize = 0;
+                       ret = -EIO;
+               }
+       }
+
+       if (fillsize < PAGE_SIZE)
+               memset(buf + fillsize, 0, PAGE_SIZE - fillsize);
+       if (ret == 0)
+               SetPageUptodate(page);
+
+       flush_dcache_page(page);
+       kunmap(page);
+       unlock_page(page);
+       return ret;
+}
+
+static const struct address_space_operations romfs_aops = {
+       .readpage       = romfs_readpage
+};
+
+/*
+ * read the entries from a directory
+ */
+static int romfs_readdir(struct file *filp, void *dirent, filldir_t filldir)
+{
+       struct inode *i = filp->f_dentry->d_inode;
+       struct romfs_inode ri;
+       unsigned long offset, maxoff;
+       int j, ino, nextfh;
+       int stored = 0;
+       char fsname[ROMFS_MAXFN];       /* XXX dynamic? */
+       int ret;
+
+       maxoff = romfs_maxsize(i->i_sb);
+
+       offset = filp->f_pos;
+       if (!offset) {
+               offset = i->i_ino & ROMFH_MASK;
+               ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE);
+               if (ret < 0)
+                       goto out;
+               offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
+       }
+
+       /* Not really failsafe, but we are read-only... */
+       for (;;) {
+               if (!offset || offset >= maxoff) {
+                       offset = maxoff;
+                       filp->f_pos = offset;
+                       goto out;
+               }
+               filp->f_pos = offset;
+
+               /* Fetch inode info */
+               ret = romfs_dev_read(i->i_sb, offset, &ri, ROMFH_SIZE);
+               if (ret < 0)
+                       goto out;
+
+               j = romfs_dev_strnlen(i->i_sb, offset + ROMFH_SIZE,
+                                     sizeof(fsname) - 1);
+               if (j < 0)
+                       goto out;
+
+               ret = romfs_dev_read(i->i_sb, offset + ROMFH_SIZE, fsname, j);
+               if (ret < 0)
+                       goto out;
+               fsname[j] = '\0';
+
+               ino = offset;
+               nextfh = be32_to_cpu(ri.next);
+               if ((nextfh & ROMFH_TYPE) == ROMFH_HRD)
+                       ino = be32_to_cpu(ri.spec);
+               if (filldir(dirent, fsname, j, offset, ino,
+                           romfs_dtype_table[nextfh & ROMFH_TYPE]) < 0)
+                       goto out;
+
+               stored++;
+               offset = nextfh & ROMFH_MASK;
+       }
+
+out:
+       return stored;
+}
+
+/*
+ * look up an entry in a directory
+ */
+static struct dentry *romfs_lookup(struct inode *dir, struct dentry *dentry,
+                                  struct nameidata *nd)
+{
+       unsigned long offset, maxoff;
+       struct inode *inode;
+       struct romfs_inode ri;
+       const char *name;               /* got from dentry */
+       int len, ret;
+
+       offset = dir->i_ino & ROMFH_MASK;
+       ret = romfs_dev_read(dir->i_sb, offset, &ri, ROMFH_SIZE);
+       if (ret < 0)
+               goto error;
+
+       /* search all the file entries in the list starting from the one
+        * pointed to by the directory's special data */
+       maxoff = romfs_maxsize(dir->i_sb);
+       offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
+
+       name = dentry->d_name.name;
+       len = dentry->d_name.len;
+
+       for (;;) {
+               if (!offset || offset >= maxoff)
+                       goto out0;
+
+               ret = romfs_dev_read(dir->i_sb, offset, &ri, sizeof(ri));
+               if (ret < 0)
+                       goto error;
+
+               /* try to match the first 16 bytes of name */
+               ret = romfs_dev_strncmp(dir->i_sb, offset + ROMFH_SIZE, name,
+                                       len);
+               if (ret < 0)
+                       goto error;
+               if (ret == 1)
+                       break;
+
+               /* next entry */
+               offset = be32_to_cpu(ri.next) & ROMFH_MASK;
+       }
+
+       /* Hard link handling */
+       if ((be32_to_cpu(ri.next) & ROMFH_TYPE) == ROMFH_HRD)
+               offset = be32_to_cpu(ri.spec) & ROMFH_MASK;
+
+       inode = romfs_iget(dir->i_sb, offset);
+       if (IS_ERR(inode)) {
+               ret = PTR_ERR(inode);
+               goto error;
+       }
+       goto outi;
+
+       /*
+        * it's a bit funky, _lookup needs to return an error code
+        * (negative) or a NULL, both as a dentry.  ENOENT should not
+        * be returned, instead we need to create a negative dentry by
+        * d_add(dentry, NULL); and return 0 as no error.
+        * (Although as I see, it only matters on writable file
+        * systems).
+        */
+out0:
+       inode = NULL;
+outi:
+       d_add(dentry, inode);
+       ret = 0;
+error:
+       return ERR_PTR(ret);
+}
+
+static const struct file_operations romfs_dir_operations = {
+       .read           = generic_read_dir,
+       .readdir        = romfs_readdir,
+};
+
+static struct inode_operations romfs_dir_inode_operations = {
+       .lookup         = romfs_lookup,
+};
+
+/*
+ * get a romfs inode based on its position in the image (which doubles as the
+ * inode number)
+ */
+static struct inode *romfs_iget(struct super_block *sb, unsigned long pos)
+{
+       struct romfs_inode_info *inode;
+       struct romfs_inode ri;
+       struct inode *i;
+       unsigned long nlen;
+       unsigned nextfh, ret;
+       umode_t mode;
+
+       /* we might have to traverse a chain of "hard link" file entries to get
+        * to the actual file */
+       for (;;) {
+               ret = romfs_dev_read(sb, pos, &ri, sizeof(ri));
+               if (ret < 0)
+                       goto error;
+
+               /* XXX: do romfs_checksum here too (with name) */
+
+               nextfh = be32_to_cpu(ri.next);
+               if ((nextfh & ROMFH_TYPE) != ROMFH_HRD)
+                       break;
+
+               pos = be32_to_cpu(ri.spec) & ROMFH_MASK;
+       }
+
+       /* determine the length of the filename */
+       nlen = romfs_dev_strnlen(sb, pos + ROMFH_SIZE, ROMFS_MAXFN);
+       if (IS_ERR_VALUE(nlen))
+               goto eio;
+
+       /* get an inode for this image position */
+       i = iget_locked(sb, pos);
+       if (!i)
+               return ERR_PTR(-ENOMEM);
+
+       if (!(i->i_state & I_NEW))
+               return i;
+
+       /* precalculate the data offset */
+       inode = ROMFS_I(i);
+       inode->i_metasize = (ROMFH_SIZE + nlen + 1 + ROMFH_PAD) & ROMFH_MASK;
+       inode->i_dataoffset = pos + inode->i_metasize;
+
+       i->i_nlink = 1;         /* Hard to decide.. */
+       i->i_size = be32_to_cpu(ri.size);
+       i->i_mtime.tv_sec = i->i_atime.tv_sec = i->i_ctime.tv_sec = 0;
+       i->i_mtime.tv_nsec = i->i_atime.tv_nsec = i->i_ctime.tv_nsec = 0;
+
+       /* set up mode and ops */
+       mode = romfs_modemap[nextfh & ROMFH_TYPE];
+
+       switch (nextfh & ROMFH_TYPE) {
+       case ROMFH_DIR:
+               i->i_size = ROMFS_I(i)->i_metasize;
+               i->i_op = &romfs_dir_inode_operations;
+               i->i_fop = &romfs_dir_operations;
+               if (nextfh & ROMFH_EXEC)
+                       mode |= S_IXUGO;
+               break;
+       case ROMFH_REG:
+               i->i_fop = &romfs_ro_fops;
+               i->i_data.a_ops = &romfs_aops;
+               if (i->i_sb->s_mtd)
+                       i->i_data.backing_dev_info =
+                               i->i_sb->s_mtd->backing_dev_info;
+               if (nextfh & ROMFH_EXEC)
+                       mode |= S_IXUGO;
+               break;
+       case ROMFH_SYM:
+               i->i_op = &page_symlink_inode_operations;
+               i->i_data.a_ops = &romfs_aops;
+               mode |= S_IRWXUGO;
+               break;
+       default:
+               /* depending on MBZ for sock/fifos */
+               nextfh = be32_to_cpu(ri.spec);
+               init_special_inode(i, mode, MKDEV(nextfh >> 16,
+                                                 nextfh & 0xffff));
+               break;
+       }
+
+       i->i_mode = mode;
+
+       unlock_new_inode(i);
+       return i;
+
+eio:
+       ret = -EIO;
+error:
+       printk(KERN_ERR "ROMFS: read error for inode 0x%lx\n", pos);
+       return ERR_PTR(ret);
+}
+
+/*
+ * allocate a new inode
+ */
+static struct inode *romfs_alloc_inode(struct super_block *sb)
+{
+       struct romfs_inode_info *inode;
+       inode = kmem_cache_alloc(romfs_inode_cachep, GFP_KERNEL);
+       return inode ? &inode->vfs_inode : NULL;
+}
+
+/*
+ * return a spent inode to the slab cache
+ */
+static void romfs_destroy_inode(struct inode *inode)
+{
+       kmem_cache_free(romfs_inode_cachep, ROMFS_I(inode));
+}
+
+/*
+ * get filesystem statistics
+ */
+static int romfs_statfs(struct dentry *dentry, struct kstatfs *buf)
+{
+       buf->f_type = ROMFS_MAGIC;
+       buf->f_namelen = ROMFS_MAXFN;
+       buf->f_bsize = ROMBSIZE;
+       buf->f_bfree = buf->f_bavail = buf->f_ffree;
+       buf->f_blocks =
+               (romfs_maxsize(dentry->d_sb) + ROMBSIZE - 1) >> ROMBSBITS;
+       return 0;
+}
+
+/*
+ * remounting must involve read-only
+ */
+static int romfs_remount(struct super_block *sb, int *flags, char *data)
+{
+       *flags |= MS_RDONLY;
+       return 0;
+}
+
+static const struct super_operations romfs_super_ops = {
+       .alloc_inode    = romfs_alloc_inode,
+       .destroy_inode  = romfs_destroy_inode,
+       .statfs         = romfs_statfs,
+       .remount_fs     = romfs_remount,
+};
+
+/*
+ * checksum check on part of a romfs filesystem
+ */
+static __u32 romfs_checksum(const void *data, int size)
+{
+       const __be32 *ptr = data;
+       __u32 sum;
+
+       sum = 0;
+       size >>= 2;
+       while (size > 0) {
+               sum += be32_to_cpu(*ptr++);
+               size--;
+       }
+       return sum;
+}
+
+/*
+ * fill in the superblock
+ */
+static int romfs_fill_super(struct super_block *sb, void *data, int silent)
+{
+       struct romfs_super_block *rsb;
+       struct inode *root;
+       unsigned long pos, img_size;
+       const char *storage;
+       size_t len;
+       int ret;
+
+#ifdef CONFIG_BLOCK
+       if (!sb->s_mtd) {
+               sb_set_blocksize(sb, ROMBSIZE);
+       } else {
+               sb->s_blocksize = ROMBSIZE;
+               sb->s_blocksize_bits = blksize_bits(ROMBSIZE);
+       }
+#endif
+
+       sb->s_maxbytes = 0xFFFFFFFF;
+       sb->s_magic = ROMFS_MAGIC;
+       sb->s_flags |= MS_RDONLY | MS_NOATIME;
+       sb->s_op = &romfs_super_ops;
+
+       /* read the image superblock and check it */
+       rsb = kmalloc(512, GFP_KERNEL);
+       if (!rsb)
+               return -ENOMEM;
+
+       sb->s_fs_info = (void *) 512;
+       ret = romfs_dev_read(sb, 0, rsb, 512);
+       if (ret < 0)
+               goto error_rsb;
+
+       img_size = be32_to_cpu(rsb->size);
+
+       if (sb->s_mtd && img_size > sb->s_mtd->size)
+               goto error_rsb_inval;
+
+       sb->s_fs_info = (void *) img_size;
+
+       if (rsb->word0 != ROMSB_WORD0 || rsb->word1 != ROMSB_WORD1 ||
+           img_size < ROMFH_SIZE) {
+               if (!silent)
+                       printk(KERN_WARNING "VFS:"
+                              " Can't find a romfs filesystem on dev %s.\n",
+                              sb->s_id);
+               goto error_rsb_inval;
+       }
+
+       if (romfs_checksum(rsb, min_t(size_t, img_size, 512))) {
+               printk(KERN_ERR "ROMFS: bad initial checksum on dev %s.\n",
+                      sb->s_id);
+               goto error_rsb_inval;
+       }
+
+       storage = sb->s_mtd ? "MTD" : "the block layer";
+
+       len = strnlen(rsb->name, ROMFS_MAXFN);
+       if (!silent)
+               printk(KERN_NOTICE "ROMFS: Mounting image '%*.*s' through %s\n",
+                      (unsigned) len, (unsigned) len, rsb->name, storage);
+
+       kfree(rsb);
+       rsb = NULL;
+
+       /* find the root directory */
+       pos = (ROMFH_SIZE + len + 1 + ROMFH_PAD) & ROMFH_MASK;
+
+       root = romfs_iget(sb, pos);
+       if (!root)
+               goto error;
+
+       sb->s_root = d_alloc_root(root);
+       if (!sb->s_root)
+               goto error_i;
+
+       return 0;
+
+error_i:
+       iput(root);
+error:
+       return -EINVAL;
+error_rsb_inval:
+       ret = -EINVAL;
+error_rsb:
+       return ret;
+}
+
+/*
+ * get a superblock for mounting
+ */
+static int romfs_get_sb(struct file_system_type *fs_type,
+                       int flags, const char *dev_name,
+                       void *data, struct vfsmount *mnt)
+{
+       int ret = -EINVAL;
+
+#ifdef CONFIG_ROMFS_ON_MTD
+       ret = get_sb_mtd(fs_type, flags, dev_name, data, romfs_fill_super,
+                        mnt);
+#endif
+#ifdef CONFIG_ROMFS_ON_BLOCK
+       if (ret == -EINVAL)
+               ret = get_sb_bdev(fs_type, flags, dev_name, data,
+                                 romfs_fill_super, mnt);
+#endif
+       return ret;
+}
+
+/*
+ * destroy a romfs superblock in the appropriate manner
+ */
+static void romfs_kill_sb(struct super_block *sb)
+{
+#ifdef CONFIG_ROMFS_ON_MTD
+       if (sb->s_mtd) {
+               kill_mtd_super(sb);
+               return;
+       }
+#endif
+#ifdef CONFIG_ROMFS_ON_BLOCK
+       if (sb->s_bdev) {
+               kill_block_super(sb);
+               return;
+       }
+#endif
+}
+
+static struct file_system_type romfs_fs_type = {
+       .owner          = THIS_MODULE,
+       .name           = "romfs",
+       .get_sb         = romfs_get_sb,
+       .kill_sb        = romfs_kill_sb,
+       .fs_flags       = FS_REQUIRES_DEV,
+};
+
+/*
+ * inode storage initialiser
+ */
+static void romfs_i_init_once(void *_inode)
+{
+       struct romfs_inode_info *inode = _inode;
+
+       inode_init_once(&inode->vfs_inode);
+}
+
+/*
+ * romfs module initialisation
+ */
+static int __init init_romfs_fs(void)
+{
+       int ret;
+
+       printk(KERN_INFO "ROMFS MTD (C) 2007 Red Hat, Inc.\n");
+
+       romfs_inode_cachep =
+               kmem_cache_create("romfs_i",
+                                 sizeof(struct romfs_inode_info), 0,
+                                 SLAB_RECLAIM_ACCOUNT | SLAB_MEM_SPREAD,
+                                 romfs_i_init_once);
+
+       if (!romfs_inode_cachep) {
+               printk(KERN_ERR
+                      "ROMFS error: Failed to initialise inode cache\n");
+               return -ENOMEM;
+       }
+       ret = register_filesystem(&romfs_fs_type);
+       if (ret) {
+               printk(KERN_ERR "ROMFS error: Failed to register filesystem\n");
+               goto error_register;
+       }
+       return 0;
+
+error_register:
+       kmem_cache_destroy(romfs_inode_cachep);
+       return ret;
+}
+
+/*
+ * romfs module removal
+ */
+static void __exit exit_romfs_fs(void)
+{
+       unregister_filesystem(&romfs_fs_type);
+       kmem_cache_destroy(romfs_inode_cachep);
+}
+
+module_init(init_romfs_fs);
+module_exit(exit_romfs_fs);
+
+MODULE_DESCRIPTION("Direct-MTD Capable RomFS");
+MODULE_AUTHOR("Red Hat, Inc.");
+MODULE_LICENSE("GPL"); /* Actually dual-licensed, but it doesn't matter for */
index 3aa5d77c2cdb0053d3c15c5c5bc48b05b97d93a6..5675b63a0631fd46b54f5e3f6781b4b9b87c6936 100644 (file)
@@ -11,6 +11,7 @@
 #include <linux/module.h>
 #include <linux/uio.h>
 #include <linux/notifier.h>
+#include <linux/device.h>
 
 #include <linux/mtd/compatmac.h>
 #include <mtd/mtd-abi.h>
@@ -162,6 +163,20 @@ struct mtd_info {
        /* We probably shouldn't allow XIP if the unpoint isn't a NULL */
        void (*unpoint) (struct mtd_info *mtd, loff_t from, size_t len);
 
+       /* Allow NOMMU mmap() to directly map the device (if not NULL)
+        * - return the address to which the offset maps
+        * - return -ENOSYS to indicate refusal to do the mapping
+        */
+       unsigned long (*get_unmapped_area) (struct mtd_info *mtd,
+                                           unsigned long len,
+                                           unsigned long offset,
+                                           unsigned long flags);
+
+       /* Backing device capabilities for this device
+        * - provides mmap capabilities
+        */
+       struct backing_dev_info *backing_dev_info;
+
 
        int (*read) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf);
        int (*write) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf);
@@ -223,6 +238,7 @@ struct mtd_info {
        void *priv;
 
        struct module *owner;
+       struct device dev;
        int usecount;
 
        /* If the driver is something smart, like UBI, it may need to maintain
@@ -233,6 +249,11 @@ struct mtd_info {
        void (*put_device) (struct mtd_info *mtd);
 };
 
+static inline struct mtd_info *dev_to_mtd(struct device *dev)
+{
+       return dev ? container_of(dev, struct mtd_info, dev) : NULL;
+}
+
 static inline uint32_t mtd_div_by_eb(uint64_t sz, struct mtd_info *mtd)
 {
        if (mtd->erasesize_shift)
index db5b63da2a7e46d619a746233cf40b4a8397e4c9..7efb9be34662b4aeb905adb246e366b2f9a1a58f 100644 (file)
@@ -43,8 +43,8 @@ extern void nand_wait_ready(struct mtd_info *mtd);
  * is supported now. If you add a chip with bigger oobsize/page
  * adjust this accordingly.
  */
-#define NAND_MAX_OOBSIZE       64
-#define NAND_MAX_PAGESIZE      2048
+#define NAND_MAX_OOBSIZE       128
+#define NAND_MAX_PAGESIZE      4096
 
 /*
  * Constants for hardware specific CLE/ALE/NCE function
index a45dd831b3f8ec0c468edf0890a8217dd80de29b..7535a74083b92bc443231a728bd062497603c576 100644 (file)
@@ -76,4 +76,16 @@ int __devinit of_mtd_parse_partitions(struct device *dev,
                                       struct device_node *node,
                                       struct mtd_partition **pparts);
 
+#ifdef CONFIG_MTD_PARTITIONS
+static inline int mtd_has_partitions(void) { return 1; }
+#else
+static inline int mtd_has_partitions(void) { return 0; }
+#endif
+
+#ifdef CONFIG_MTD_CMDLINE_PARTS
+static inline int mtd_has_cmdlinepart(void) { return 1; }
+#else
+static inline int mtd_has_cmdlinepart(void) { return 0; }
+#endif
+
 #endif