diff options
Diffstat (limited to 'recipes/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff')
-rw-r--r-- | recipes/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff | 51503 |
1 files changed, 51503 insertions, 0 deletions
diff --git a/recipes/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff b/recipes/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff new file mode 100644 index 0000000000..6c3b3b9acb --- /dev/null +++ b/recipes/linux/linux-mtx-1-2.4.27/01-mtd-2004-01-27.diff @@ -0,0 +1,51503 @@ +diff -Nurb linux-mips-2.4.27/drivers/mtd/Config.in linux/drivers/mtd/Config.in +--- linux-mips-2.4.27/drivers/mtd/Config.in 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/Config.in 2004-11-19 10:25:11.607244176 +0100 +@@ -1,5 +1,5 @@ + +-# $Id: Config.in,v 1.74 2002/04/23 13:52:14 mag Exp $ ++# $Id: Config.in,v 1.75 2003/05/23 11:38:29 dwmw2 Exp $ + + mainmenu_option next_comment + comment 'Memory Technology Devices (MTD)' +@@ -30,6 +30,7 @@ + if [ "$CONFIG_NFTL" = "y" -o "$CONFIG_NFTL" = "m" ]; then + bool ' Write support for NFTL (BETA)' CONFIG_NFTL_RW + fi ++ dep_tristate ' INFTL (Inverse NAND Flash Translation Layer) support' CONFIG_INFTL $CONFIG_MTD + + source drivers/mtd/chips/Config.in + +diff -Nurb linux-mips-2.4.27/drivers/mtd/Makefile linux/drivers/mtd/Makefile +--- linux-mips-2.4.27/drivers/mtd/Makefile 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/Makefile 2004-11-19 10:25:11.608244024 +0100 +@@ -1,30 +1,7 @@ + # + # Makefile for the memory technology device drivers. + # +-# Note! Dependencies are done automagically by 'make dep', which also +-# removes any old dependencies. DON'T put your own dependencies here +-# unless it's something special (ie not a .c file). +-# +-# Note 2! The CFLAGS definitions are now inherited from the +-# parent makes.. +-# +-# $Id: Makefile,v 1.65 2002/03/22 07:10:34 dwmw2 Exp $ +- +- +-obj-y += chips/chipslink.o maps/mapslink.o \ +- devices/devlink.o nand/nandlink.o +-obj-m := +-obj-n := +-obj- := +- +-O_TARGET := mtdlink.o +- +-export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o mtdconcat.o +-list-multi := nftl.o +- +-mod-subdirs := +-subdir-y := chips maps devices nand +-subdir-m := $(subdir-y) ++# $Id: Makefile.common,v 1.2 2003/05/23 11:38:29 dwmw2 Exp $ + + # *** BIG UGLY NOTE *** + # +@@ -52,15 +29,44 @@ + + # 'Users' - code which presents functionality to userspace. + obj-$(CONFIG_MTD_CHAR) += mtdchar.o +-obj-$(CONFIG_MTD_BLOCK) += mtdblock.o +-obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o +-obj-$(CONFIG_FTL) += ftl.o +-obj-$(CONFIG_NFTL) += nftl.o ++obj-$(CONFIG_MTD_BLOCK) += mtdblock.o mtd_blkdevs.o ++obj-$(CONFIG_MTD_BLOCK_RO) += mtdblock_ro.o mtd_blkdevs.o ++obj-$(CONFIG_FTL) += ftl.o mtd_blkdevs.o ++obj-$(CONFIG_NFTL) += nftl.o mtd_blkdevs.o ++obj-$(CONFIG_INFTL) += inftl.o mtd_blkdevs.o + + nftl-objs := nftlcore.o nftlmount.o ++inftl-objs := inftlcore.o inftlmount.o ++ ++ifeq ($(PATCHLEVEL),4) ++ ++export-objs := mtdcore.o mtdpart.o redboot.o cmdlinepart.o afs.o \ ++ mtdconcat.o mtd_blkdevs-24.o ++ ++mtd_blkdevs-objs := mtd_blkdevs-24.o ++ ++obj-y += chips/chipslink.o maps/mapslink.o \ ++ devices/devlink.o nand/nandlink.o ++ ++O_TARGET := mtdlink.o ++ ++list-multi := nftl.o inftl.o mtd_blkdevs.o ++ ++mod-subdirs := ++subdir-y := chips maps devices nand ++subdir-m := $(subdir-y) + + include $(TOPDIR)/Rules.make + + nftl.o: $(nftl-objs) + $(LD) -r -o $@ $(nftl-objs) + ++inftl.o: $(inftl-objs) ++ $(LD) -r -o $@ $(inftl-objs) ++ ++mtd_blkdevs.o: $(mtd_blkdevs-objs) ++ $(LD) -r -o $@ $(mtd_blkdevs-objs) ++ ++else ++obj-y += chips/ maps/ devices/ nand/ ++endif +diff -Nurb linux-mips-2.4.27/drivers/mtd/afs.c linux/drivers/mtd/afs.c +--- linux-mips-2.4.27/drivers/mtd/afs.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/afs.c 2004-11-19 10:25:11.610243720 +0100 +@@ -21,7 +21,7 @@ + This is access code for flashes using ARM's flash partitioning + standards. + +- $Id: afs.c,v 1.8 2002/05/04 08:49:09 rmk Exp $ ++ $Id: afs.c,v 1.12 2003/06/13 15:31:06 rmk Exp $ + + ======================================================================*/ + +@@ -76,17 +76,19 @@ + return ret; + } + ++ ret = 1; ++ + /* + * Does it contain the magic number? + */ + if (fs.signature != 0xa0ffff9f) +- ret = 1; ++ ret = 0; + + /* + * Don't touch the SIB. + */ + if (fs.type == 2) +- ret = 1; ++ ret = 0; + + *iis_start = fs.image_info_base & mask; + *img_start = fs.image_start & mask; +@@ -96,14 +98,14 @@ + * be located after the footer structure. + */ + if (*iis_start >= ptr) +- ret = 1; ++ ret = 0; + + /* + * Check the start of this image. The image + * data can not be located after this block. + */ + if (*img_start > off) +- ret = 1; ++ ret = 0; + + return ret; + } +@@ -125,7 +127,9 @@ + return ret; + } + +-int parse_afs_partitions(struct mtd_info *mtd, struct mtd_partition **pparts) ++static int parse_afs_partitions(struct mtd_info *mtd, ++ struct mtd_partition **pparts, ++ unsigned long origin) + { + struct mtd_partition *parts; + u_int mask, off, idx, sz; +@@ -150,7 +154,7 @@ + ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask); + if (ret < 0) + break; +- if (ret == 1) ++ if (ret == 0) + continue; + + ret = afs_read_iis(mtd, &iis, iis_ptr); +@@ -183,7 +187,7 @@ + ret = afs_read_footer(mtd, &img_ptr, &iis_ptr, off, mask); + if (ret < 0) + break; +- if (ret == 1) ++ if (ret == 0) + continue; + + /* Read the image info block */ +@@ -227,7 +231,25 @@ + return idx ? idx : ret; + } + +-EXPORT_SYMBOL(parse_afs_partitions); ++static struct mtd_part_parser afs_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_afs_partitions, ++ .name = "afs", ++}; ++ ++static int __init afs_parser_init(void) ++{ ++ return register_mtd_parser(&afs_parser); ++} ++ ++static void __exit afs_parser_exit(void) ++{ ++ deregister_mtd_parser(&afs_parser); ++} ++ ++module_init(afs_parser_init); ++module_exit(afs_parser_exit); ++ + + MODULE_AUTHOR("ARM Ltd"); + MODULE_DESCRIPTION("ARM Firmware Suite partition parser"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/Config.in linux/drivers/mtd/chips/Config.in +--- linux-mips-2.4.27/drivers/mtd/chips/Config.in 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/Config.in 2004-11-19 10:25:11.712228216 +0100 +@@ -1,6 +1,6 @@ + # drivers/mtd/chips/Config.in + +-# $Id: Config.in,v 1.16 2002/09/03 13:30:43 joern Exp $ ++# $Id: Config.in,v 1.17 2003/09/25 14:40:34 thayne Exp $ + + mainmenu_option next_comment + +@@ -11,13 +11,12 @@ + + if [ "$CONFIG_MTD_CFI" = "y" -o "$CONFIG_MTD_JEDECPROBE" = "y" ]; then + define_bool CONFIG_MTD_GEN_PROBE y +-else +- if [ "$CONFIG_MTD_CFI" = "m" -o "$CONFIG_MTD_JEDECPROBE" = "m" ]; then ++elif [ "$CONFIG_MTD_CFI" = "m" -o "$CONFIG_MTD_JEDECPROBE" = "m" ]; then + define_bool CONFIG_MTD_GEN_PROBE m +- else ++else + define_bool CONFIG_MTD_GEN_PROBE n +- fi + fi ++ + if [ "$CONFIG_MTD_GEN_PROBE" = "y" -o "$CONFIG_MTD_GEN_PROBE" = "m" ]; then + bool ' Flash chip driver advanced configuration options' CONFIG_MTD_CFI_ADV_OPTIONS + if [ "$CONFIG_MTD_CFI_ADV_OPTIONS" = "y" ]; then +@@ -44,8 +43,27 @@ + fi + dep_tristate ' Support for Intel/Sharp flash chips' CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_GEN_PROBE + dep_tristate ' Support for AMD/Fujitsu flash chips' CONFIG_MTD_CFI_AMDSTD $CONFIG_MTD_GEN_PROBE ++if [ "$CONFIG_MTD_CFI_AMDSTD" = "y" -o "$CONFIG_MTD_CFI_AMDSTD" = "m" ]; then ++ bool ' Retry failed commands (erase/program)' CONFIG_MTD_CFI_AMDSTD_RETRY n ++ if [ "$CONFIG_MTD_CFI_AMDSTD_RETRY" = "y" ]; then ++ int ' Max retries of failed commands (erase/program)' CONFIG_MTD_CFI_AMDSTD_RETRY_MAX 0 ++ fi ++fi ++ + dep_tristate ' Support for ST (Advanced Architecture) flash chips' CONFIG_MTD_CFI_STAA $CONFIG_MTD_GEN_PROBE + ++if [ "$CONFIG_MTD_CFI_INTELEXT" = "y" \ ++ -o "$CONFIG_MTD_CFI_AMDSTD" = "y" \ ++ -o "$CONFIG_MTD_CFI_STAA" = "y" ]; then ++ define_bool CONFIG_MTD_CFI_UTIL y ++elif [ "$CONFIG_MTD_CFI_INTELEXT" = "m" \ ++ -o "$CONFIG_MTD_CFI_AMDSTD" = "m" \ ++ -o "$CONFIG_MTD_CFI_STAA" = "m" ]; then ++ define_bool CONFIG_MTD_CFI_UTIL m ++else ++ define_bool CONFIG_MTD_CFI_UTIL n ++fi ++ + dep_tristate ' Support for RAM chips in bus mapping' CONFIG_MTD_RAM $CONFIG_MTD + dep_tristate ' Support for ROM chips in bus mapping' CONFIG_MTD_ROM $CONFIG_MTD + dep_tristate ' Support for absent chips in bus mapping' CONFIG_MTD_ABSENT $CONFIG_MTD +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/Makefile linux/drivers/mtd/chips/Makefile +--- linux-mips-2.4.27/drivers/mtd/chips/Makefile 2003-07-05 05:23:38.000000000 +0200 ++++ linux/drivers/mtd/chips/Makefile 2004-11-19 10:25:11.714227912 +0100 +@@ -1,11 +1,12 @@ + # + # linux/drivers/chips/Makefile + # +-# $Id: Makefile,v 1.8 2002/01/10 20:27:40 eric Exp $ ++# $Id: Makefile.common,v 1.3 2003/09/25 14:40:34 thayne Exp $ + ++ifeq ($(PATCHLEVEL),4) + O_TARGET := chipslink.o +- +-export-objs := chipreg.o gen_probe.o ++export-objs := chipreg.o gen_probe.o cfi_util.o ++endif + + # *** BIG UGLY NOTE *** + # +@@ -17,6 +18,7 @@ + obj-$(CONFIG_MTD) += chipreg.o + obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o + obj-$(CONFIG_MTD_CFI) += cfi_probe.o ++obj-$(CONFIG_MTD_CFI_UTIL) += cfi_util.o + obj-$(CONFIG_MTD_CFI_STAA) += cfi_cmdset_0020.o + obj-$(CONFIG_MTD_CFI_AMDSTD) += cfi_cmdset_0002.o + obj-$(CONFIG_MTD_CFI_INTELEXT) += cfi_cmdset_0001.o +@@ -28,4 +30,4 @@ + obj-$(CONFIG_MTD_SHARP) += sharp.o + obj-$(CONFIG_MTD_ABSENT) += map_absent.o + +-include $(TOPDIR)/Rules.make ++-include $(TOPDIR)/Rules.make +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/amd_flash.c linux/drivers/mtd/chips/amd_flash.c +--- linux-mips-2.4.27/drivers/mtd/chips/amd_flash.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/amd_flash.c 2004-11-19 10:25:11.722226696 +0100 +@@ -3,7 +3,7 @@ + * + * Author: Jonas Holmberg <jonas.holmberg@axis.com> + * +- * $Id: amd_flash.c,v 1.19 2003/01/24 13:30:11 dwmw2 Exp $ ++ * $Id: amd_flash.c,v 1.23 2003/06/12 09:24:13 dwmw2 Exp $ + * + * Copyright (c) 2001 Axis Communications AB + * +@@ -19,6 +19,7 @@ + #include <linux/slab.h> + #include <linux/delay.h> + #include <linux/interrupt.h> ++#include <linux/init.h> + #include <linux/mtd/map.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/flashchip.h> +@@ -125,10 +126,10 @@ + + + static struct mtd_chip_driver amd_flash_chipdrv = { +- probe: amd_flash_probe, +- destroy: amd_flash_destroy, +- name: "amd_flash", +- module: THIS_MODULE ++ .probe = amd_flash_probe, ++ .destroy = amd_flash_destroy, ++ .name = "amd_flash", ++ .module = THIS_MODULE + }; + + +@@ -140,11 +141,11 @@ + static inline __u32 wide_read(struct map_info *map, __u32 addr) + { + if (map->buswidth == 1) { +- return map->read8(map, addr); ++ return map_read8(map, addr); + } else if (map->buswidth == 2) { +- return map->read16(map, addr); ++ return map_read16(map, addr); + } else if (map->buswidth == 4) { +- return map->read32(map, addr); ++ return map_read32(map, addr); + } + + return 0; +@@ -153,11 +154,11 @@ + static inline void wide_write(struct map_info *map, __u32 val, __u32 addr) + { + if (map->buswidth == 1) { +- map->write8(map, val, addr); ++ map_write8(map, val, addr); + } else if (map->buswidth == 2) { +- map->write16(map, val, addr); ++ map_write16(map, val, addr); + } else if (map->buswidth == 4) { +- map->write32(map, val, addr); ++ map_write32(map, val, addr); + } + } + +@@ -424,231 +425,228 @@ + + static struct mtd_info *amd_flash_probe(struct map_info *map) + { +- /* Keep this table on the stack so that it gets deallocated after the +- * probe is done. +- */ +- const struct amd_flash_info table[] = { ++ static const struct amd_flash_info table[] = { + { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DT, +- name: "AMD AM29LV160DT", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DT, ++ .name = "AMD AM29LV160DT", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DB, +- name: "AMD AM29LV160DB", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DB, ++ .name = "AMD AM29LV160DB", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT160, +- name: "Toshiba TC58FVT160", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT160, ++ .name = "Toshiba TC58FVT160", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160TE, +- name: "Fujitsu MBM29LV160TE", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160TE, ++ .name = "Fujitsu MBM29LV160TE", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB160, +- name: "Toshiba TC58FVB160", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB160, ++ .name = "Toshiba TC58FVB160", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160BE, +- name: "Fujitsu MBM29LV160BE", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160BE, ++ .name = "Fujitsu MBM29LV160BE", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BB, ++ .name = "AMD AM29LV800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BB, +- name: "AMD AM29F800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BB, ++ .name = "AMD AM29F800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BT, +- name: "AMD AM29LV800BT", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BT, ++ .name = "AMD AM29LV800BT", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BT, +- name: "AMD AM29F800BT", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BT, ++ .name = "AMD AM29F800BT", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BB, ++ .name = "AMD AM29LV800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV800BB, +- name: "Fujitsu MBM29LV800BB", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 15 } ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV800BB, ++ .name = "Fujitsu MBM29LV800BB", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W800T, +- name: "ST M29W800T", +- size: 0x00100000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x0F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x0F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x0FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W800T, ++ .name = "ST M29W800T", ++ .size = 0x00100000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DT, +- name: "ST M29W160DT", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x1F8000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x1FC000, erasesize: 0x04000, numblocks: 1 } ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W160DT, ++ .name = "ST M29W160DT", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DB, +- name: "ST M29W160DB", +- size: 0x00200000, +- numeraseregions: 4, +- regions: { +- { offset: 0x000000, erasesize: 0x04000, numblocks: 1 }, +- { offset: 0x004000, erasesize: 0x02000, numblocks: 2 }, +- { offset: 0x008000, erasesize: 0x08000, numblocks: 1 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W160DB, ++ .name = "ST M29W160DB", ++ .size = 0x00200000, ++ .numeraseregions = 4, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, ++ { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, ++ { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29BDS323D, +- name: "AMD AM29BDS323D", +- size: 0x00400000, +- numeraseregions: 3, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 48 }, +- { offset: 0x300000, erasesize: 0x10000, numblocks: 15 }, +- { offset: 0x3f0000, erasesize: 0x02000, numblocks: 8 }, ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29BDS323D, ++ .name = "AMD AM29BDS323D", ++ .size = 0x00400000, ++ .numeraseregions = 3, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 }, ++ { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 }, ++ { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29BDS643D, +- name: "AMD AM29BDS643D", +- size: 0x00800000, +- numeraseregions: 3, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 96 }, +- { offset: 0x600000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x7f0000, erasesize: 0x02000, numblocks: 8 }, ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29BDS643D, ++ .name = "AMD AM29BDS643D", ++ .size = 0x00800000, ++ .numeraseregions = 3, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 }, ++ { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 }, + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49xV16x, +- name: "Atmel AT49xV16x", +- size: 0x00200000, +- numeraseregions: 2, +- regions: { +- { offset: 0x000000, erasesize: 0x02000, numblocks: 8 }, +- { offset: 0x010000, erasesize: 0x10000, numblocks: 31 } ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49xV16x, ++ .name = "Atmel AT49xV16x", ++ .size = 0x00200000, ++ .numeraseregions = 2, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x02000, .numblocks = 8 }, ++ { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49xV16xT, +- name: "Atmel AT49xV16xT", +- size: 0x00200000, +- numeraseregions: 2, +- regions: { +- { offset: 0x000000, erasesize: 0x10000, numblocks: 31 }, +- { offset: 0x1F0000, erasesize: 0x02000, numblocks: 8 } ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49xV16xT, ++ .name = "Atmel AT49xV16xT", ++ .size = 0x00200000, ++ .numeraseregions = 2, ++ .regions = { ++ { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, ++ { .offset = 0x1F0000, .erasesize = 0x02000, .numblocks = 8 } + } + } + }; +@@ -822,7 +820,7 @@ + + chip->state = FL_READY; + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); +@@ -984,7 +982,7 @@ + u_char tmp_buf[4]; + __u32 datum; + +- map->copy_from(map, tmp_buf, ++ map_copy_from(map, tmp_buf, + bus_ofs + private->chips[chipnum].start, + map->buswidth); + while (len && i < map->buswidth) +@@ -1057,7 +1055,7 @@ + u_char tmp_buf[2]; + __u32 datum; + +- map->copy_from(map, tmp_buf, ++ map_copy_from(map, tmp_buf, + ofs + private->chips[chipnum].start, + map->buswidth); + while (len--) { +@@ -1178,7 +1176,7 @@ + __u8 verify; + + for (address = adr; address < (adr + size); address++) { +- if ((verify = map->read8(map, address)) != 0xFF) { ++ if ((verify = map_read8(map, address)) != 0xFF) { + error = 1; + break; + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0001.c linux/drivers/mtd/chips/cfi_cmdset_0001.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0001.c 2003-07-05 05:23:38.000000000 +0200 ++++ linux/drivers/mtd/chips/cfi_cmdset_0001.c 2004-11-19 10:25:11.733225024 +0100 +@@ -4,7 +4,7 @@ + * + * (C) 2000 Red Hat. GPL'd + * +- * $Id: cfi_cmdset_0001.c,v 1.114 2003/03/18 12:28:40 dwmw2 Exp $ ++ * $Id: cfi_cmdset_0001.c,v 1.132 2003/11/04 19:34:22 thayne Exp $ + * + * + * 10/10/2000 Nicolas Pitre <nico@cam.org> +@@ -21,6 +21,7 @@ + #include <linux/types.h> + #include <linux/kernel.h> + #include <linux/sched.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <asm/byteorder.h> + +@@ -29,10 +30,14 @@ + #include <linux/delay.h> + #include <linux/interrupt.h> + #include <linux/mtd/map.h> +-#include <linux/mtd/cfi.h> ++#include <linux/mtd/mtd.h> + #include <linux/mtd/compatmac.h> ++#include <linux/mtd/cfi.h> ++ ++/* #define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE */ + +-// debugging, turns off buffer write mode #define FORCE_WORD_WRITE ++// debugging, turns off buffer write mode if set to 1 ++#define FORCE_WORD_WRITE 0 + + static int cfi_intelext_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int cfi_intelext_read_user_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +@@ -52,16 +57,21 @@ + + static struct mtd_info *cfi_intelext_setup (struct map_info *); + +-static int do_point (struct mtd_info *mtd, loff_t from, size_t len, ++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char **mtdbuf); +-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, ++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, + size_t len); + ++ ++/* ++ * *********** SETUP AND PROBE BITS *********** ++ */ ++ + static struct mtd_chip_driver cfi_intelext_chipdrv = { +- probe: NULL, /* Not usable directly */ +- destroy: cfi_intelext_destroy, +- name: "cfi_cmdset_0001", +- module: THIS_MODULE ++ .probe = NULL, /* Not usable directly */ ++ .destroy = cfi_intelext_destroy, ++ .name = "cfi_cmdset_0001", ++ .module = THIS_MODULE + }; + + /* #define DEBUG_LOCK_BITS */ +@@ -102,13 +112,63 @@ + } + + printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", +- extp->VccOptimal >> 8, extp->VccOptimal & 0xf); ++ extp->VccOptimal >> 4, extp->VccOptimal & 0xf); + if (extp->VppOptimal) + printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", +- extp->VppOptimal >> 8, extp->VppOptimal & 0xf); ++ extp->VppOptimal >> 4, extp->VppOptimal & 0xf); + } + #endif + ++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE ++/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ ++static void fixup_intel_strataflash(struct map_info *map, void* param) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv; ++ ++ printk(KERN_WARNING "cfi_cmdset_0001: Suspend " ++ "erase on write disabled.\n"); ++ extp->SuspendCmdSupport &= ~1; ++} ++#endif ++ ++static void fixup_st_m28w320ct(struct map_info *map, void* param) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ cfi->cfiq->BufWriteTimeoutTyp = 0; /* Not supported */ ++ cfi->cfiq->BufWriteTimeoutMax = 0; /* Not supported */ ++} ++ ++static void fixup_st_m28w320cb(struct map_info *map, void* param) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ /* Note this is done after the region info is endian swapped */ ++ cfi->cfiq->EraseRegionInfo[1] = ++ (cfi->cfiq->EraseRegionInfo[1] & 0xffff0000) | 0x3e; ++}; ++ ++static struct cfi_fixup fixup_table[] = { ++#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE ++ { ++ CFI_MFR_ANY, CFI_ID_ANY, ++ fixup_intel_strataflash, NULL ++ }, ++#endif ++ { ++ 0x0020, /* STMicroelectronics */ ++ 0x00ba, /* M28W320CT */ ++ fixup_st_m28w320ct, NULL ++ }, { ++ 0x0020, /* STMicroelectronics */ ++ 0x00bb, /* M28W320CB */ ++ fixup_st_m28w320cb, NULL ++ }, { ++ 0, 0, NULL, NULL ++ } ++}; ++ + /* This routine is made available to other mtd code via + * inter_module_register. It must only be accessed through + * inter_module_get which will bump the use count of this module. The +@@ -120,7 +180,6 @@ + { + struct cfi_private *cfi = map->fldrv_priv; + int i; +- __u32 base = cfi->chips[0].start; + + if (cfi->cfi_mode == CFI_MODE_CFI) { + /* +@@ -130,59 +189,29 @@ + */ + __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + struct cfi_pri_intelext *extp; +- int ofs_factor = cfi->interleave * cfi->device_type; +- +- //printk(" Intel/Sharp Extended Query Table at 0x%4.4X\n", adr); +- if (!adr) +- return NULL; + +- /* Switch it into Query Mode */ +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); +- +- extp = kmalloc(sizeof(*extp), GFP_KERNEL); +- if (!extp) { +- printk(KERN_ERR "Failed to allocate memory\n"); ++ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "Intel/Sharp"); ++ if (!extp) + return NULL; +- } +- +- /* Read in the Extended Query Table */ +- for (i=0; i<sizeof(*extp); i++) { +- ((unsigned char *)extp)[i] = +- cfi_read_query(map, (base+((adr+i)*ofs_factor))); +- } +- +- if (extp->MajorVersion != '1' || +- (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { +- printk(KERN_WARNING " Unknown IntelExt Extended Query " +- "version %c.%c.\n", extp->MajorVersion, +- extp->MinorVersion); +- kfree(extp); +- return NULL; +- } + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); + extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); + extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); + ++ /* Install our own private info structure */ ++ cfi->cmdset_priv = extp; ++ ++ cfi_fixup(map, fixup_table); ++ + #ifdef DEBUG_CFI_FEATURES + /* Tell the user about it in lots of lovely detail */ + cfi_tell_features(extp); + #endif + + if(extp->SuspendCmdSupport & 1) { +-//#define CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE +-#ifdef CMDSET0001_DISABLE_ERASE_SUSPEND_ON_WRITE +-/* Some Intel Strata Flash prior to FPO revision C has bugs in this area */ +- printk(KERN_WARNING "cfi_cmdset_0001: Suspend " +- "erase on write disabled.\n"); +- extp->SuspendCmdSupport &= ~1; +-#else + printk(KERN_NOTICE "cfi_cmdset_0001: Erase suspend on write enabled\n"); +-#endif + } +- /* Install our own private info structure */ +- cfi->cmdset_priv = extp; + } + + for (i=0; i< cfi->numchips; i++) { +@@ -194,8 +223,6 @@ + + map->fldrv = &cfi_intelext_chipdrv; + +- /* Make sure it's in read mode */ +- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); + return cfi_intelext_setup(map); + } + +@@ -261,20 +288,16 @@ + mtd->erase = cfi_intelext_erase_varsize; + mtd->read = cfi_intelext_read; + +- if(map->point && map->unpoint){ +- mtd->point = do_point; +- mtd->unpoint = do_unpoint; ++ if (map_is_linear(map)) { ++ mtd->point = cfi_intelext_point; ++ mtd->unpoint = cfi_intelext_unpoint; + } + +-#ifndef FORCE_WORD_WRITE +- if ( cfi->cfiq->BufWriteTimeoutTyp ) { +- printk("Using buffer write method\n" ); ++ if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { ++ printk(KERN_INFO "Using buffer write method\n" ); + mtd->write = cfi_intelext_write_buffers; + } else { +-#else +- { +-#endif +- printk("Using word write method\n" ); ++ printk(KERN_INFO "Using word write method\n" ); + mtd->write = cfi_intelext_write_words; + } + mtd->read_user_prot_reg = cfi_intelext_read_user_prot_reg; +@@ -286,8 +309,8 @@ + mtd->resume = cfi_intelext_resume; + mtd->flags = MTD_CAP_NORFLASH; + map->fldrv = &cfi_intelext_chipdrv; +- MOD_INC_USE_COUNT; + mtd->name = map->name; ++ __module_get(THIS_MODULE); + return mtd; + + setup_err: +@@ -301,78 +324,170 @@ + return NULL; + } + +-static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) ++/* ++ * *********** CHIP ACCESS FUNCTIONS *********** ++ */ ++ ++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) + { +- cfi_word status, status_OK; +- unsigned long timeo; + DECLARE_WAITQUEUE(wait, current); +- unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_priv; ++ cfi_word status, status_OK = CMD(0x80); ++ unsigned long timeo; ++ struct cfi_pri_intelext *cfip = (struct cfi_pri_intelext *)cfi->cmdset_priv; + +- adr += chip->start; +- +- /* Ensure cmd read/writes are aligned. */ +- cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); +- +- /* Let's determine this according to the interleave only once */ +- status_OK = CMD(0x80); +- ++ resettime: + timeo = jiffies + HZ; + retry: +- spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * If it's in FL_ERASING state, suspend it and make it talk now. +- */ + switch (chip->state) { + +- case FL_READY: +- case FL_POINT: ++ case FL_STATUS: ++ for (;;) { ++ status = cfi_read(map, adr); ++ if ((status & status_OK) == status_OK) + break; + ++ if (time_after(jiffies, timeo)) { ++ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n", ++ (long long)status); ++ spin_unlock(chip->mutex); ++ return -EIO; ++ } ++ spin_unlock(chip->mutex); ++ cfi_udelay(1); ++ spin_lock(chip->mutex); ++ /* Someone else might have been playing with it. */ ++ goto retry; ++ } ++ ++ case FL_READY: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->state = FL_STATUS; ++ return 0; + +- case FL_STATUS: +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) { +- cfi_write(map, CMD(0xff), cmd_addr); +- chip->state = FL_READY; ++ case FL_ERASING: ++ if (!(cfip->FeatureSupport & 2) || ++ !(mode == FL_READY || mode == FL_POINT || ++ (mode == FL_WRITING && (cfip->SuspendCmdSupport & 1)))) ++ goto sleep; ++ ++ ++ /* Erase suspend */ ++ cfi_write(map, CMD(0xB0), adr); ++ ++ /* If the flash has finished erasing, then 'erase suspend' ++ * appears to make some (28F320) flash devices switch to ++ * 'read' mode. Make sure that we switch to 'read status' ++ * mode so we get the right data. --rmk ++ */ ++ cfi_write(map, CMD(0x70), adr); ++ chip->oldstate = FL_ERASING; ++ chip->state = FL_ERASE_SUSPENDING; ++ chip->erase_suspended = 1; ++ for (;;) { ++ status = cfi_read(map, adr); ++ if ((status & status_OK) == status_OK) + break; +- } + +- /* Urgh. Chip not yet ready to talk to us. */ + if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status); ++ /* Urgh. Resume and pretend we weren't here. */ ++ cfi_write(map, CMD(0xd0), adr); ++ /* Make sure we're in 'read status' mode if it had finished */ ++ cfi_write(map, CMD(0x70), adr); ++ chip->state = FL_ERASING; ++ chip->oldstate = FL_READY; ++ printk(KERN_ERR "Chip not ready after erase " ++ "suspended: status = 0x%x\n", status); + return -EIO; + } + +- /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock(chip->mutex); + cfi_udelay(1); +- goto retry; ++ spin_lock(chip->mutex); ++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. ++ So we can just loop here. */ ++ } ++ chip->state = FL_STATUS; ++ return 0; ++ ++ case FL_POINT: ++ /* Only if there's no operation suspended... */ ++ if (mode == FL_READY && chip->oldstate == FL_READY) ++ return 0; + + default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ ++ sleep: + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ spin_lock(chip->mutex); ++ goto resettime; + } ++} ++ ++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ switch(chip->oldstate) { ++ case FL_ERASING: ++ chip->state = chip->oldstate; ++ /* What if one interleaved chip has finished and the ++ other hasn't? The old code would leave the finished ++ one in READY mode. That's bad, and caused -EROFS ++ errors to be returned from do_erase_oneblock because ++ that's the only bit it checked for at the time. ++ As the state machine appears to explicitly allow ++ sending the 0x70 (Read Status) command to an erasing ++ chip and expecting it to be ignored, that's what we ++ do. */ ++ cfi_write(map, CMD(0xd0), adr); ++ cfi_write(map, CMD(0x70), adr); ++ chip->oldstate = FL_READY; ++ chip->state = FL_ERASING; ++ break; ++ ++ case FL_READY: ++ case FL_STATUS: ++ /* We should really make set_vpp() count, rather than doing this */ ++ DISABLE_VPP(map); ++ break; ++ default: ++ printk(KERN_ERR "put_chip() called with oldstate %d!!\n", chip->oldstate); ++ } ++ wake_up(&chip->wq); ++} ++ ++static int do_point_onechip (struct map_info *map, struct flchip *chip, loff_t adr, size_t len) ++{ ++ unsigned long cmd_addr; ++ struct cfi_private *cfi = map->fldrv_priv; ++ int ret = 0; ++ ++ adr += chip->start; ++ ++ /* Ensure cmd read/writes are aligned. */ ++ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); ++ ++ spin_lock(chip->mutex); ++ ++ ret = get_chip(map, chip, cmd_addr, FL_POINT); ++ ++ if (!ret) { ++ if (chip->state != FL_POINT && chip->state != FL_READY) ++ cfi_write(map, CMD(0xff), cmd_addr); + + chip->state = FL_POINT; + chip->ref_point_counter++; ++ } + spin_unlock(chip->mutex); +- return 0; ++ ++ return ret; + } +-static int do_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) ++ ++static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +@@ -380,12 +495,10 @@ + int chipnum; + int ret = 0; + +- if (from + len > mtd->size) ++ if (!map->virt || (from + len > mtd->size)) + return -EINVAL; + +- *mtdbuf = map->point(map, from, len); +- if(*mtdbuf == NULL) +- return -EINVAL; /* can not point this region */ ++ *mtdbuf = (void *)map->virt + from; + *retlen = 0; + + /* Now lock the chip(s) to POINT state */ +@@ -418,14 +531,13 @@ + return 0; + } + +-static void do_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) ++static void cfi_intelext_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long ofs; + int chipnum; + +- map->unpoint(map, addr, from, len); + /* Now unlock the chip(s) POINT state */ + + /* ofs: offset within the first chip that the first read should start */ +@@ -446,13 +558,14 @@ + thislen = len; + + spin_lock(chip->mutex); +- if(chip->state == FL_POINT){ ++ if (chip->state == FL_POINT) { + chip->ref_point_counter--; + if(chip->ref_point_counter == 0) + chip->state = FL_READY; + } else +- printk("Warning: unpoint called on non pointed region\n"); /* Should this give an error? */ +- wake_up(&chip->wq); ++ printk(KERN_ERR "Warning: unpoint called on non pointed region\n"); /* Should this give an error? */ ++ ++ put_chip(map, chip, chip->start); + spin_unlock(chip->mutex); + + len -= thislen; +@@ -463,136 +576,32 @@ + + static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) + { +- cfi_word status, status_OK; +- unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); +- int suspended = 0; + unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_priv; ++ int ret; + + adr += chip->start; + + /* Ensure cmd read/writes are aligned. */ + cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); + +- /* Let's determine this according to the interleave only once */ +- status_OK = CMD(0x80); +- +- timeo = jiffies + HZ; +- retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * If it's in FL_ERASING state, suspend it and make it talk now. +- */ +- switch (chip->state) { +- case FL_ERASING: +- if (!cfi->cmdset_priv || +- !(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) +- goto sleep; /* We don't support erase suspend */ +- +- cfi_write (map, CMD(0xb0), cmd_addr); +- /* If the flash has finished erasing, then 'erase suspend' +- * appears to make some (28F320) flash devices switch to +- * 'read' mode. Make sure that we switch to 'read status' +- * mode so we get the right data. --rmk +- */ +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->oldstate = FL_ERASING; +- chip->state = FL_ERASE_SUSPENDING; +- // printk("Erase suspending at 0x%lx\n", cmd_addr); +- for (;;) { +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) +- break; +- +- if (time_after(jiffies, timeo)) { +- /* Urgh */ +- cfi_write(map, CMD(0xd0), cmd_addr); +- /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->state = FL_ERASING; +- spin_unlock(chip->mutex); +- printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%llx\n", (__u64)status); +- return -EIO; +- } +- ++ ret = get_chip(map, chip, cmd_addr, FL_READY); ++ if (ret) { + spin_unlock(chip->mutex); +- cfi_udelay(1); +- spin_lock(chip->mutex); ++ return ret; + } + +- suspended = 1; ++ if (chip->state != FL_POINT && chip->state != FL_READY) { + cfi_write(map, CMD(0xff), cmd_addr); +- chip->state = FL_READY; +- break; +- +-#if 0 +- case FL_WRITING: +- /* Not quite yet */ +-#endif +- +- case FL_READY: +- case FL_POINT: +- break; +- +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_addr); +- chip->state = FL_STATUS; + +- case FL_STATUS: +- status = cfi_read(map, cmd_addr); +- if ((status & status_OK) == status_OK) { +- cfi_write(map, CMD(0xff), cmd_addr); + chip->state = FL_READY; +- break; + } + +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %llx\n", (__u64)status); +- return -EIO; +- } ++ map_copy_from(map, buf, adr, len); + +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; ++ put_chip(map, chip, cmd_addr); + +- default: +- sleep: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; +- } +- +- map->copy_from(map, buf, adr, len); +- +- if (suspended) { +- chip->state = chip->oldstate; +- /* What if one interleaved chip has finished and the +- other hasn't? The old code would leave the finished +- one in READY mode. That's bad, and caused -EROFS +- errors to be returned from do_erase_oneblock because +- that's the only bit it checked for at the time. +- As the state machine appears to explicitly allow +- sending the 0x70 (Read Status) command to an erasing +- chip and expecting it to be ignored, that's what we +- do. */ +- cfi_write(map, CMD(0xd0), cmd_addr); +- cfi_write(map, CMD(0x70), cmd_addr); +- } +- +- wake_up(&chip->wq); + spin_unlock(chip->mutex); + return 0; + } +@@ -640,70 +649,52 @@ + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp=cfi->cmdset_priv; +- int ofs_factor = cfi->interleave * cfi->device_type; +- int count=len; ++ struct cfi_pri_intelext *extp = cfi->cmdset_priv; + struct flchip *chip; +- int chip_num,offst; +- unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); ++ int ofs_factor = cfi->interleave * cfi->device_type; ++ int count = len; ++ int chip_num, offst; ++ int ret; + +- chip=0; +- /* Calculate which chip & protection register offset we need */ +- chip_num=((unsigned int)from/reg_sz); +- offst=from-(reg_sz*chip_num)+base_offst; ++ chip_num = ((unsigned int)from/reg_sz); ++ offst = from - (reg_sz*chip_num)+base_offst; + +- while(count){ ++ while (count) { ++ /* Calculate which chip & protection register offset we need */ + +- if(chip_num>=cfi->numchips) ++ if (chip_num >= cfi->numchips) + goto out; + +- /* Make sure that the chip is in the right state */ ++ chip = &cfi->chips[chip_num]; + +- timeo = jiffies + HZ; +- chip=&cfi->chips[chip_num]; +- retry: + spin_lock(chip->mutex); +- +- switch (chip->state) { +- case FL_READY: +- case FL_STATUS: +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- break; +- +- default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return (len-count)?:ret; + } + +- /* Now read the data required from this flash */ ++ if (chip->state != FL_JEDEC_QUERY) { ++ cfi_write(map, CMD(0x90), chip->start); ++ chip->state = FL_JEDEC_QUERY; ++ } + +- cfi_send_gen_cmd(0x90, 0x55,chip->start, map, cfi, cfi->device_type, NULL); +- while(count && ((offst-base_offst)<reg_sz)){ +- *buf=map->read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); ++ while (count && ((offst-base_offst) < reg_sz)) { ++ *buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); + buf++; + offst++; + count--; + } + +- chip->state=FL_CFI_QUERY; ++ put_chip(map, chip, chip->start); + spin_unlock(chip->mutex); ++ + /* Move on to the next chip */ + chip_num++; +- offst=base_offst; +- ++ offst = base_offst; + } + + out: +- wake_up(&chip->wq); + return len-count; + } + +@@ -749,103 +740,20 @@ + static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum) + { + struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp = cfi->cmdset_priv; +- cfi_word status, status_OK; +- unsigned long timeo; +- DECLARE_WAITQUEUE(wait, current); +- int z, suspended=0, ret=0; +- +- adr += chip->start; +- +- /* Let's determine this according to the interleave only once */ +- status_OK = CMD(0x80); +- +- timeo = jiffies + HZ; +- retry: +- spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * Later, we can actually think about interrupting it +- * if it's in FL_ERASING state. +- * Not just yet, though. +- */ +- switch (chip->state) { +- case FL_READY: +- break; +- +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in read\n"); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- case FL_ERASING: +- if (!extp || +- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1))) +- goto sleep; /* We don't support erase suspend */ +- +- cfi_write (map, CMD(0xb0), adr); +- +- /* If the flash has finished erasing, then 'erase suspend' +- * appears to make some (28F320) flash devices switch to +- * 'read' mode. Make sure that we switch to 'read status' +- * mode so we get the right data. --rmk +- */ +- cfi_write(map, CMD(0x70), adr); +- chip->oldstate = FL_ERASING; +- chip->state = FL_ERASE_SUSPENDING; +- for (;;) { +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; ++ cfi_word status, status_OK; ++ unsigned long timeo; ++ int z, ret=0; + +- if (time_after(jiffies, timeo)) { +- /* Urgh */ +- cfi_write(map, CMD(0xd0), adr); +- /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_ERASING; +- spin_unlock(chip->mutex); +- printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%x\n", status); +- return -EIO; +- } ++ adr += chip->start; + +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- spin_lock(chip->mutex); +- } +- suspended = 1; +- chip->state = FL_STATUS; +- break; ++ /* Let's determine this according to the interleave only once */ ++ status_OK = CMD(0x80); + +- default: +- sleep: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return ret; + } + + ENABLE_VPP(map); +@@ -862,6 +770,8 @@ + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); +@@ -879,7 +789,6 @@ + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; +- DISABLE_VPP(map); + printk(KERN_ERR "waiting for chip to be ready timed out in word write\n"); + ret = -EIO; + goto out; +@@ -908,27 +817,11 @@ + /* put back into read status register mode */ + cfi_write(map, CMD(0x70), adr); + ret = -EROFS; +- goto out; + } + out: +- if (suspended) { +- chip->state = chip->oldstate; +- /* What if one interleaved chip has finished and the +- other hasn't? The old code would leave the finished +- one in READY mode. That's bad, and caused -EROFS +- errors to be returned from do_erase_oneblock because +- that's the only bit it checked for at the time. +- As the state machine appears to explicitly allow +- sending the 0x70 (Read Status) command to an erasing +- chip and expecting it to be ignored, that's what we +- do. */ +- cfi_write(map, CMD(0xd0), adr); +- cfi_write(map, CMD(0x70), adr); +- } else +- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */ +- +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + spin_unlock(chip->mutex); ++ + return ret; + } + +@@ -1059,11 +952,9 @@ + unsigned long adr, const u_char *buf, int len) + { + struct cfi_private *cfi = map->fldrv_priv; +- struct cfi_pri_intelext *extp = cfi->cmdset_priv; + cfi_word status, status_OK; + unsigned long cmd_adr, timeo; +- DECLARE_WAITQUEUE(wait, current); +- int wbufsize, z, suspended=0, ret=0; ++ int wbufsize, z, ret=0, bytes, words; + + wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + adr += chip->start; +@@ -1072,91 +963,18 @@ + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + +- timeo = jiffies + HZ; +- retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. +- * Later, we can actually think about interrupting it +- * if it's in FL_ERASING state. +- * Not just yet, though. +- */ +- switch (chip->state) { +- case FL_READY: +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- cfi_write(map, CMD(0x70), cmd_adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, cmd_adr); +- if ((status & status_OK) == status_OK) +- break; +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { ++ ret = get_chip(map, chip, cmd_adr, FL_WRITING); ++ if (ret) { + spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in buffer write\n"); +- return -EIO; ++ return ret; + } + +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- case FL_ERASING: +- if (!extp || +- !((extp->FeatureSupport & 2) && (extp->SuspendCmdSupport & 1))) +- goto sleep; /* We don't support erase suspend */ +- +- cfi_write (map, CMD(0xb0), adr); +- +- /* If the flash has finished erasing, then 'erase suspend' +- * appears to make some (28F320) flash devices switch to +- * 'read' mode. Make sure that we switch to 'read status' +- * mode so we get the right data. --rmk +- */ +- cfi_write(map, CMD(0x70), adr); +- chip->oldstate = FL_ERASING; +- chip->state = FL_ERASE_SUSPENDING; +- for (;;) { +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- if (time_after(jiffies, timeo)) { +- /* Urgh */ +- cfi_write(map, CMD(0xd0), adr); +- /* make sure we're in 'read status' mode */ +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_ERASING; +- spin_unlock(chip->mutex); +- printk(KERN_ERR "Chip not ready after erase " +- "suspended: status = 0x%x\n", status); +- return -EIO; +- } ++ if (chip->state != FL_STATUS) ++ cfi_write(map, CMD(0x70), cmd_adr); + +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- spin_lock(chip->mutex); +- } +- suspended = 1; +- chip->state = FL_STATUS; +- break; ++ status = cfi_read(map, cmd_adr); + +- default: +- sleep: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; +- } +- /* We know we're now in FL_STATUS mode, and 'status' is current */ + /* §4.8 of the 28FxxxJ3A datasheet says "Any time SR.4 and/or SR.5 is set + [...], the device will not accept any more Write to Buffer commands". + So we must check here and reset those bits if they're set. Otherwise +@@ -1185,7 +1003,6 @@ + /* Argh. Not ready for write to buffer */ + cfi_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; +- DISABLE_VPP(map); + printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %llx, status = %llx\n", (__u64)status, (__u64)cfi_read(map, cmd_adr)); + /* Odd. Clear status bits */ + cfi_write(map, CMD(0x50), cmd_adr); +@@ -1196,20 +1013,42 @@ + } + + /* Write length of data to come */ +- cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); ++ bytes = len & (CFIDEV_BUSWIDTH-1); ++ words = len / CFIDEV_BUSWIDTH; ++ cfi_write(map, CMD(words - !bytes), cmd_adr ); + + /* Write data */ +- for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { ++ z = 0; ++ while(z < words * CFIDEV_BUSWIDTH) { + if (cfi_buswidth_is_1()) { +- map->write8 (map, *((__u8*)buf)++, adr+z); ++ map_write8 (map, *((__u8*)buf)++, adr+z); + } else if (cfi_buswidth_is_2()) { +- map->write16 (map, *((__u16*)buf)++, adr+z); ++ map_write16 (map, *((__u16*)buf)++, adr+z); + } else if (cfi_buswidth_is_4()) { +- map->write32 (map, *((__u32*)buf)++, adr+z); ++ map_write32 (map, *((__u32*)buf)++, adr+z); + } else if (cfi_buswidth_is_8()) { +- map->write64 (map, *((__u64*)buf)++, adr+z); ++ map_write64 (map, *((__u64*)buf)++, adr+z); ++ } else { ++ ret = -EINVAL; ++ goto out; ++ } ++ z += CFIDEV_BUSWIDTH; ++ } ++ if (bytes) { ++ int i = 0, n = 0; ++ u_char tmp_buf[8], *tmp_p = tmp_buf; ++ ++ while (bytes--) ++ tmp_buf[i++] = buf[n++]; ++ while (i < CFIDEV_BUSWIDTH) ++ tmp_buf[i++] = 0xff; ++ if (cfi_buswidth_is_2()) { ++ map_write16 (map, *((__u16*)tmp_p)++, adr+z); ++ } else if (cfi_buswidth_is_4()) { ++ map_write32 (map, *((__u32*)tmp_p)++, adr+z); ++ } else if (cfi_buswidth_is_8()) { ++ map_write64 (map, *((__u64*)tmp_p)++, adr+z); + } else { +- DISABLE_VPP(map); + ret = -EINVAL; + goto out; + } +@@ -1227,6 +1066,7 @@ + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock(chip->mutex); +@@ -1244,7 +1084,6 @@ + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + chip->state = FL_STATUS; +- DISABLE_VPP(map); + printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); + ret = -EIO; + goto out; +@@ -1266,6 +1105,7 @@ + + /* Done and happy. */ + chip->state = FL_STATUS; ++ + /* check for lock bit */ + if (status & CMD(0x02)) { + /* clear status */ +@@ -1273,26 +1113,10 @@ + /* put back into read status register mode */ + cfi_write(map, CMD(0x70), adr); + ret = -EROFS; +- goto out; + } +- out: +- if (suspended) { +- chip->state = chip->oldstate; +- /* What if one interleaved chip has finished and the +- other hasn't? The old code would leave the finished +- one in READY mode. That's bad, and caused -EROFS +- errors to be returned from do_erase_oneblock because +- that's the only bit it checked for at the time. +- As the state machine appears to explicitly allow +- sending the 0x70 (Read Status) command to an erasing +- chip and expecting it to be ignored, that's what we +- do. */ +- cfi_write(map, CMD(0xd0), adr); +- cfi_write(map, CMD(0x70), adr); +- } else +- DISABLE_VPP(map); /* must not clear the VPP if there is a suspended erase to be resumed */ + +- wake_up(&chip->wq); ++ out: ++ put_chip(map, chip, cmd_adr); + spin_unlock(chip->mutex); + return ret; + } +@@ -1336,12 +1160,12 @@ + } + + /* Write buffer is worth it only if more than one word to write... */ +- while(len > CFIDEV_BUSWIDTH) { ++ while(len) { + /* We must not cross write block boundaries */ + int size = wbufsize - (ofs & (wbufsize-1)); + + if (size > len) +- size = len & ~(CFIDEV_BUSWIDTH-1); ++ size = len; + ret = do_write_buffer(map, &cfi->chips[chipnum], + ofs, buf, size); + if (ret) +@@ -1359,17 +1183,6 @@ + return 0; + } + } +- +- /* ... and write the remaining bytes */ +- if (len > 0) { +- size_t local_retlen; +- ret = cfi_intelext_write_words(mtd, ofs + (chipnum << cfi->chipshift), +- len, &local_retlen, buf); +- if (ret) +- return ret; +- (*retlen) += local_retlen; +- } +- + return 0; + } + +@@ -1479,45 +1292,12 @@ + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + +- timeo = jiffies + HZ; +-retry: ++ retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. */ +- switch (chip->state) { +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- case FL_READY: +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ ret = get_chip(map, chip, adr, FL_ERASING); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return ret; + } + + ENABLE_VPP(map); +@@ -1528,7 +1308,7 @@ + cfi_write(map, CMD(0x20), adr); + cfi_write(map, CMD(0xD0), adr); + chip->state = FL_ERASING; +- chip->oldstate = 0; ++ chip->erase_suspended = 0; + + spin_unlock(chip->mutex); + set_current_state(TASK_UNINTERRUPTIBLE); +@@ -1550,11 +1330,11 @@ + spin_lock(chip->mutex); + continue; + } +- if (chip->oldstate) { ++ if (chip->erase_suspended) { + /* This erase was suspended and resumed. + Adjust the timeout */ + timeo = jiffies + (HZ*20); /* FIXME */ +- chip->oldstate = 0; ++ chip->erase_suspended = 0; + } + + status = cfi_read(map, adr); +@@ -1658,39 +1438,22 @@ + int i; + struct flchip *chip; + int ret = 0; +- DECLARE_WAITQUEUE(wait, current); + + for (i=0; !ret && i<cfi->numchips; i++) { + chip = &cfi->chips[i]; + +- retry: + spin_lock(chip->mutex); ++ ret = get_chip(map, chip, chip->start, FL_SYNCING); + +- switch(chip->state) { +- case FL_READY: +- case FL_STATUS: +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: ++ if (!ret) { + chip->oldstate = chip->state; + chip->state = FL_SYNCING; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ +- case FL_SYNCING: +- spin_unlock(chip->mutex); +- break; +- +- default: +- /* Not an idle state */ +- add_wait_queue(&chip->wq, &wait); +- +- spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- +- goto retry; + } ++ spin_unlock(chip->mutex); + } + + /* Unlock the chips again */ +@@ -1731,52 +1494,18 @@ + struct cfi_private *cfi = map->fldrv_priv; + cfi_word status, status_OK; + unsigned long timeo = jiffies + HZ; +- DECLARE_WAITQUEUE(wait, current); ++ int ret; + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + +- timeo = jiffies + HZ; +-retry: + spin_lock(chip->mutex); +- +- /* Check that the chip's ready to talk to us. */ +- switch (chip->state) { +- case FL_CFI_QUERY: +- case FL_JEDEC_QUERY: +- case FL_READY: +- cfi_write(map, CMD(0x70), adr); +- chip->state = FL_STATUS; +- +- case FL_STATUS: +- status = cfi_read(map, adr); +- if ((status & status_OK) == status_OK) +- break; +- +- /* Urgh. Chip not yet ready to talk to us. */ +- if (time_after(jiffies, timeo)) { +- spin_unlock(chip->mutex); +- printk(KERN_ERR "%s: waiting for chip to be ready timed out\n", __FUNCTION__); +- return -EIO; +- } +- +- /* Latency issues. Drop the lock, wait a while and retry */ +- spin_unlock(chip->mutex); +- cfi_udelay(1); +- goto retry; +- +- default: +- /* Stick ourselves on a wait queue to be woken when +- someone changes the status */ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ ret = get_chip(map, chip, adr, FL_LOCKING); ++ if (ret) { + spin_unlock(chip->mutex); +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +- timeo = jiffies + HZ; +- goto retry; ++ return ret; + } + + ENABLE_VPP(map); +@@ -1823,8 +1552,7 @@ + + /* Done and happy. */ + chip->state = FL_STATUS; +- DISABLE_VPP(map); +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + spin_unlock(chip->mutex); + return 0; + } +@@ -1889,22 +1617,23 @@ + + spin_lock(chip->mutex); + +- switch(chip->state) { ++ switch (chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: ++ if (chip->oldstate == FL_READY) { + chip->oldstate = chip->state; + chip->state = FL_PM_SUSPENDED; + /* No need to wake_up() on this state change - + * as the whole point is that nobody can do anything + * with the chip now anyway. + */ +- case FL_PM_SUSPENDED: ++ } + break; +- + default: + ret = -EAGAIN; ++ case FL_PM_SUSPENDED: + break; + } + spin_unlock(chip->mutex); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0002.c linux/drivers/mtd/chips/cfi_cmdset_0002.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0002.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/cfi_cmdset_0002.c 2004-11-19 10:25:11.745223200 +0100 +@@ -6,16 +6,22 @@ + * + * 2_by_8 routines added by Simon Munton + * ++ * 4_by_16 work by Carolyn J. Smith ++ * ++ * Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com ++ * + * This code is GPL + * +- * $Id: cfi_cmdset_0002.c,v 1.62 2003/01/24 23:30:13 dwmw2 Exp $ ++ * $Id: cfi_cmdset_0002.c,v 1.94 2004/01/27 10:16:20 dvrabel Exp $ + * + */ + ++#include <linux/config.h> + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> + #include <linux/sched.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <asm/byteorder.h> + +@@ -23,16 +29,50 @@ + #include <linux/slab.h> + #include <linux/delay.h> + #include <linux/interrupt.h> ++#include <linux/mtd/compatmac.h> + #include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> + #include <linux/mtd/cfi.h> + + #define AMD_BOOTLOC_BUG ++#define FORCE_WORD_WRITE 0 ++ ++ ++/* ++ * This is an attempt to coalesce the retry logic in one place - that way ++ * there aren't #ifdefs scattered throughout. ++ */ ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ ++#ifndef CONFIG_MTD_CFI_AMDSTD_RETRY_MAX ++#define CONFIG_MTD_CFI_AMDSTD_RETRY_MAX 0 ++#endif ++ ++#define RETRY_CMD_LABEL retry_cmd: do {} while (0) ++#define HANDLE_WACKY_STATE() handle_wacky_state(__func__, retry_cmd_cnt, adr, datum, prev_oldstatus, prev_status, oldstatus, status) ++static int retry_cmd_max = CONFIG_MTD_CFI_AMDSTD_RETRY_MAX; ++#define DECLARE_RETRY_CMD_CNT() int retry_cmd_cnt = 0 ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++#define CHECK_RETRIES() do { if (++retry_cmd_cnt <= retry_cmd_max) goto retry_cmd; } while (0) ++#endif ++ ++#else ++ ++#define RETRY_CMD_LABEL do {} while (0) ++#define HANDLE_WACKY_STATE() handle_wacky_state(__func__, adr, datum, prev_oldstatus, prev_status, oldstatus, status) ++#define DECLARE_RETRY_CMD_CNT() ++#define CHECK_RETRIES() ++ ++#endif /* !defined(CONFIG_MTD_CFI_AMDSTD_RETRY) */ ++ + + static int cfi_amdstd_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +-static int cfi_amdstd_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); ++static int cfi_amdstd_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); ++static int cfi_amdstd_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); + static int cfi_amdstd_erase_chip(struct mtd_info *, struct erase_info *); +-static int cfi_amdstd_erase_onesize(struct mtd_info *, struct erase_info *); + static int cfi_amdstd_erase_varsize(struct mtd_info *, struct erase_info *); ++static int cfi_amdstd_lock_varsize(struct mtd_info *, loff_t, size_t); ++static int cfi_amdstd_unlock_varsize(struct mtd_info *, loff_t, size_t); + static void cfi_amdstd_sync (struct mtd_info *); + static int cfi_amdstd_suspend (struct mtd_info *); + static void cfi_amdstd_resume (struct mtd_info *); +@@ -45,55 +85,136 @@ + + + static struct mtd_chip_driver cfi_amdstd_chipdrv = { +- probe: NULL, /* Not usable directly */ +- destroy: cfi_amdstd_destroy, +- name: "cfi_cmdset_0002", +- module: THIS_MODULE ++ .probe = NULL, /* Not usable directly */ ++ .destroy = cfi_amdstd_destroy, ++ .name = "cfi_cmdset_0002", ++ .module = THIS_MODULE + }; + +-struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) ++ ++/* #define DEBUG_LOCK_BITS */ ++/* #define DEBUG_CFI_FEATURES */ ++ ++ ++#ifdef DEBUG_CFI_FEATURES ++static void cfi_tell_features(struct cfi_pri_amdstd *extp) + { +- struct cfi_private *cfi = map->fldrv_priv; +- unsigned char bootloc; +- int ofs_factor = cfi->interleave * cfi->device_type; +- int i; +- __u8 major, minor; +- __u32 base = cfi->chips[0].start; ++ const char* erase_suspend[3] = { ++ "Not supported", "Read only", "Read/write" ++ }; ++ const char* top_bottom[6] = { ++ "No WP", "8x8KiB sectors at top & bottom, no WP", ++ "Bottom boot", "Top boot", ++ "Uniform, Bottom WP", "Uniform, Top WP" ++ }; ++ ++ printk(" Silicon revision: %d\n", extp->SiliconRevision >> 1); ++ printk(" Address sensitive unlock: %s\n", ++ (extp->SiliconRevision & 1) ? "Not required" : "Required"); + +- if (cfi->cfi_mode==CFI_MODE_CFI){ +- __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; ++ if (extp->EraseSuspend < ARRAY_SIZE(erase_suspend)) ++ printk(" Erase Suspend: %s\n", erase_suspend[extp->EraseSuspend]); ++ else ++ printk(" Erase Suspend: Unknown value %d\n", extp->EraseSuspend); + +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); ++ if (extp->BlkProt == 0) ++ printk(" Block protection: Not supported\n"); ++ else ++ printk(" Block protection: %d sectors per group\n", extp->BlkProt); ++ ++ ++ printk(" Temporary block unprotect: %s\n", ++ extp->TmpBlkUnprotect ? "Supported" : "Not supported"); ++ printk(" Block protect/unprotect scheme: %d\n", extp->BlkProtUnprot); ++ printk(" Number of simultaneous operations: %d\n", extp->SimultaneousOps); ++ printk(" Burst mode: %s\n", ++ extp->BurstMode ? "Supported" : "Not supported"); ++ if (extp->PageMode == 0) ++ printk(" Page mode: Not supported\n"); ++ else ++ printk(" Page mode: %d word page\n", extp->PageMode << 2); + +- major = cfi_read_query(map, base + (adr+3)*ofs_factor); +- minor = cfi_read_query(map, base + (adr+4)*ofs_factor); ++ printk(" Vpp Supply Minimum Program/Erase Voltage: %d.%d V\n", ++ extp->VppMin >> 4, extp->VppMin & 0xf); ++ printk(" Vpp Supply Maximum Program/Erase Voltage: %d.%d V\n", ++ extp->VppMax >> 4, extp->VppMax & 0xf); + +- printk(KERN_NOTICE " Amd/Fujitsu Extended Query Table v%c.%c at 0x%4.4X\n", +- major, minor, adr); +- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); +- +- cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); +- cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); +- cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); +- cfi->mfr = cfi_read_query(map, base); +- cfi->id = cfi_read_query(map, base + ofs_factor); ++ if (extp->TopBottom < ARRAY_SIZE(top_bottom)) ++ printk(" Top/Bottom Boot Block: %s\n", top_bottom[extp->TopBottom]); ++ else ++ printk(" Top/Bottom Boot Block: Unknown value %d\n", extp->TopBottom); ++} ++#endif + +- /* Wheee. Bring me the head of someone at AMD. */ + #ifdef AMD_BOOTLOC_BUG ++/* Wheee. Bring me the head of someone at AMD. */ ++static void fixup_amd_bootblock(struct map_info *map, void* param) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_pri_amdstd *extp = cfi->cmdset_priv; ++ __u8 major = extp->MajorVersion; ++ __u8 minor = extp->MinorVersion; ++ + if (((major << 8) | minor) < 0x3131) { + /* CFI version 1.0 => don't trust bootloc */ + if (cfi->id & 0x80) { + printk(KERN_WARNING "%s: JEDEC Device ID is 0x%02X. Assuming broken CFI table.\n", map->name, cfi->id); +- bootloc = 3; /* top boot */ ++ extp->TopBottom = 3; /* top boot */ + } else { +- bootloc = 2; /* bottom boot */ ++ extp->TopBottom = 2; /* bottom boot */ + } +- } else ++ } ++} + #endif ++ ++static struct cfi_fixup fixup_table[] = { ++#ifdef AMD_BOOTLOC_BUG + { +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); +- bootloc = cfi_read_query(map, base + (adr+15)*ofs_factor); ++ 0x0001, /* AMD */ ++ CFI_ID_ANY, ++ fixup_amd_bootblock, NULL ++ }, ++#endif ++ { 0, 0, NULL, NULL } ++}; ++ ++ ++struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ unsigned char bootloc; ++ int i; ++ ++ if (cfi->cfi_mode==CFI_MODE_CFI){ ++ /* ++ * It's a real CFI chip, not one for which the probe ++ * routine faked a CFI structure. So we read the feature ++ * table from it. ++ */ ++ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; ++ struct cfi_pri_amdstd *extp; ++ ++ extp = (struct cfi_pri_amdstd*)cfi_read_pri(map, adr, sizeof(*extp), "Amd/Fujitsu"); ++ if (!extp) ++ return NULL; ++ ++ /* Install our own private info structure */ ++ cfi->cmdset_priv = extp; ++ ++ cfi_fixup(map, fixup_table); ++ ++#ifdef DEBUG_CFI_FEATURES ++ /* Tell the user about it in lots of lovely detail */ ++ cfi_tell_features(extp); ++#endif ++ ++ bootloc = extp->TopBottom; ++ if ((bootloc != 2) && (bootloc != 3)) { ++ printk(KERN_WARNING "%s: CFI does not contain boot " ++ "bank location. Assuming top.\n", map->name); ++ bootloc = 2; + } ++ + if (bootloc == 3 && cfi->cfiq->NumEraseRegions > 1) { + printk(KERN_WARNING "%s: Swapping erase regions for broken CFI table.\n", map->name); + +@@ -106,6 +227,11 @@ + cfi->cfiq->EraseRegionInfo[j] = swap; + } + } ++ /* ++ * These might already be setup (more correctly) by ++ * jedec_probe.c - still need it for cfi_probe.c path. ++ */ ++ if ( ! (cfi->addr_unlock1 && cfi->addr_unlock2) ) { + switch (cfi->device_type) { + case CFI_DEVICETYPE_X8: + cfi->addr_unlock1 = 0x555; +@@ -125,9 +251,13 @@ + cfi->addr_unlock2 = 0xaaa; + break; + default: +- printk(KERN_NOTICE "Eep. Unknown cfi_cmdset_0002 device type %d\n", cfi->device_type); ++ printk(KERN_WARNING ++ "MTD %s(): Unsupported device type %d\n", ++ __func__, cfi->device_type); + return NULL; + } ++ } ++ + } /* CFI mode */ + + for (i=0; i< cfi->numchips; i++) { +@@ -138,15 +268,17 @@ + + map->fldrv = &cfi_amdstd_chipdrv; + +- cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); + return cfi_amdstd_setup(map); + } + ++ + static struct mtd_info *cfi_amdstd_setup(struct map_info *map) + { + struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; + unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; ++ unsigned long offset = 0; ++ int i,j; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + printk(KERN_NOTICE "number of %s chips: %d\n", +@@ -163,15 +295,9 @@ + /* Also select the correct geometry setup too */ + mtd->size = devsize * cfi->numchips; + +- if (cfi->cfiq->NumEraseRegions == 1) { +- /* No need to muck about with multiple erase sizes */ +- mtd->erasesize = ((cfi->cfiq->EraseRegionInfo[0] >> 8) & ~0xff) * cfi->interleave; +- } else { +- unsigned long offset = 0; +- int i,j; +- + mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; +- mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) * mtd->numeraseregions, GFP_KERNEL); ++ mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) ++ * mtd->numeraseregions, GFP_KERNEL); + if (!mtd->eraseregions) { + printk(KERN_WARNING "Failed to allocate memory for MTD erase region info\n"); + goto setup_err; +@@ -206,39 +332,52 @@ + mtd->eraseregions[i].numblocks); + } + #endif +- } + + switch (CFIDEV_BUSWIDTH) + { + case 1: + case 2: + case 4: +-#if 1 +- if (mtd->numeraseregions > 1) +- mtd->erase = cfi_amdstd_erase_varsize; +- else ++#ifdef CFI_WORD_64 ++ case 8: + #endif +- if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) ++ if (mtd->numeraseregions == 1 ++ && ((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) { + mtd->erase = cfi_amdstd_erase_chip; +- else +- mtd->erase = cfi_amdstd_erase_onesize; ++ } else { ++ mtd->erase = cfi_amdstd_erase_varsize; ++ mtd->lock = cfi_amdstd_lock_varsize; ++ mtd->unlock = cfi_amdstd_unlock_varsize; ++ } ++ ++ if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { ++ DEBUG(MTD_DEBUG_LEVEL1, "Using buffer write method\n" ); ++ mtd->write = cfi_amdstd_write_buffers; ++ } else { ++ DEBUG(MTD_DEBUG_LEVEL1, "Using word write method\n" ); ++ mtd->write = cfi_amdstd_write_words; ++ } ++ + mtd->read = cfi_amdstd_read; +- mtd->write = cfi_amdstd_write; + break; + + default: +- printk(KERN_WARNING "Unsupported buswidth\n"); ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); + goto setup_err; + break; + } + if (cfi->fast_prog) { +- /* In cfi_amdstd_write() we frob the protection stuff ++ /* In cfi_amdstd_write_words() we frob the protection stuff + without paying any attention to the state machine. + This upsets in-progress erases. So we turn this flag + off for now till the code gets fixed. */ + printk(KERN_NOTICE "cfi_cmdset_0002: Disabling fast programming due to code brokenness.\n"); + cfi->fast_prog = 0; + } ++ /* FIXME: erase-suspend-program is broken. See ++ http://lists.infradead.org/pipermail/linux-mtd/2003-December/009001.html */ ++ printk(KERN_NOTICE "cfi_cmdset_0002: Disabling erase-suspend-program due to code brokenness.\n"); + + + /* does this chip have a secsi area? */ +@@ -266,7 +405,7 @@ + mtd->flags = MTD_CAP_NORFLASH; + map->fldrv = &cfi_amdstd_chipdrv; + mtd->name = map->name; +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + + setup_err: +@@ -280,46 +419,210 @@ + return NULL; + } + +-static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) ++ ++/* This is more work to coalesce the retry #ifdefs in one location */ ++static inline void handle_wacky_state(const char *func, ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ int retry_cmd_cnt, ++#endif ++ unsigned long adr, ++ cfi_word datum, ++ cfi_word prev_oldstatus, ++ cfi_word prev_status, ++ cfi_word oldstatus, ++ cfi_word status) ++{ ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ if ( retry_cmd_cnt == retry_cmd_max ) { ++#endif ++ printk(KERN_WARNING ++ "MTD %s(): Wacky! Unable to decode failure status\n" ++ "Possible buggy device - try " ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ "increasing retry_cmd_max from %d\n" ++#else ++ "enabling CONFIG_MTD_CFI_AMDSTD_RETRY\n" ++ "in your kernel config and setting driver retry_cmd_max\n" ++#endif ++ , func ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ , retry_cmd_max ++#endif ++ ); ++ ++ printk(KERN_WARNING ++ "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", ++ func, adr, datum, ++ prev_oldstatus, prev_status, ++ oldstatus, status); ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++ } ++#endif ++} ++ ++ ++static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) + { + DECLARE_WAITQUEUE(wait, current); +- unsigned long timeo = jiffies + HZ; ++ struct cfi_private *cfi = map->fldrv_priv; ++ cfi_word status, oldstatus; ++ cfi_word dq6 = CMD(1<<6); ++ cfi_word dq2 = CMD(1<<2); ++ unsigned long timeo; ++ struct cfi_pri_amdstd *cfip = (struct cfi_pri_amdstd *)cfi->cmdset_priv; + ++ resettime: ++ timeo = jiffies + HZ; + retry: ++ switch (chip->state) { ++ ++ case FL_STATUS: ++ for (;;) { ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ if (((oldstatus ^ status) & (dq6 | dq2)) == 0) ++ break; ++ ++ if (time_after(jiffies, timeo)) { ++ printk(KERN_ERR "Waiting for chip to be ready timed out. Status %llx\n", ++ (long long)status); ++ cfi_spin_unlock(chip->mutex); ++ return -EIO; ++ } ++ cfi_spin_unlock(chip->mutex); ++ cfi_udelay(1); + cfi_spin_lock(chip->mutex); ++ /* Someone else might have been playing with it. */ ++ goto retry; ++ } + +- if (chip->state != FL_READY){ +-#if 0 +- printk(KERN_DEBUG "Waiting for chip to read, status = %d\n", chip->state); +-#endif +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ case FL_READY: ++ case FL_CFI_QUERY: ++ case FL_JEDEC_QUERY: ++ return 0; ++ ++ case FL_ERASING: ++ if (mode == FL_WRITING) /* FIXME: Erase-suspend-program appears broken. */ ++ goto sleep; ++ ++ if (!(mode == FL_READY || mode == FL_POINT ++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x2)) ++ || (mode == FL_WRITING && (cfip->EraseSuspend & 0x1)))) ++ goto sleep; ++ ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ if ((oldstatus ^ status) & dq2) { ++ printk(KERN_ERR "Can't suspend erase -- block in progress\n"); ++ goto sleep; ++ } ++ ++ /* Erase suspend */ ++ /* FIXME - is there a way to verify suspend? */ ++ cfi_write(map, CMD(0xB0), chip->in_progress_block_addr); ++ chip->oldstate = FL_ERASING; ++ chip->state = FL_ERASE_SUSPENDING; ++ chip->erase_suspended = 1; ++ for (;;) { ++ oldstatus = cfi_read(map, chip->in_progress_block_addr); ++ status = cfi_read(map, chip->in_progress_block_addr); ++ if (((oldstatus ^ status) & dq6) == 0) ++ break; ++ ++ if (time_after(jiffies, timeo)) { ++ /* Urgh. Resume and pretend we weren't here. */ ++ /* FIXME - is there a way to verify resume? */ ++ cfi_write(map, CMD(0x30), chip->in_progress_block_addr); ++ chip->state = FL_ERASING; ++ chip->oldstate = FL_READY; ++ printk(KERN_ERR "Chip not ready after erase " ++ "suspended: status = 0x%x\n", status); ++ return -EIO; ++ } + + cfi_spin_unlock(chip->mutex); ++ cfi_udelay(1); ++ cfi_spin_lock(chip->mutex); ++ /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. ++ So we can just loop here. */ ++ } ++ chip->state = FL_READY; ++ return 0; ++ ++ case FL_POINT: ++ /* Only if there's no operation suspended... */ ++ if (mode == FL_READY && chip->oldstate == FL_READY) ++ return 0; + ++ default: ++ sleep: ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ cfi_spin_unlock(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; ++ cfi_spin_lock(chip->mutex); ++ goto resettime; ++ } ++} + +- goto retry; ++ ++static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ ++ switch(chip->oldstate) { ++ case FL_ERASING: ++ chip->state = chip->oldstate; ++ cfi_write(map, CMD(0x30), chip->in_progress_block_addr); ++ chip->oldstate = FL_READY; ++ chip->state = FL_ERASING; ++ break; ++ ++ case FL_READY: ++ case FL_STATUS: ++ /* We should really make set_vpp() count, rather than doing this */ ++ DISABLE_VPP(map); ++ break; ++ default: ++ printk(KERN_ERR "MTD: put_chip() called with oldstate %d!!\n", chip->oldstate); + } ++ wake_up(&chip->wq); ++} ++ ++ ++static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) ++{ ++ unsigned long cmd_addr; ++ struct cfi_private *cfi = map->fldrv_priv; ++ int ret; + + adr += chip->start; + ++ /* Ensure cmd read/writes are aligned. */ ++ cmd_addr = adr & ~(CFIDEV_BUSWIDTH-1); ++ ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, cmd_addr, FL_READY); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } ++ ++ if (chip->state != FL_POINT && chip->state != FL_READY) { ++ cfi_write(map, CMD(0xf0), cmd_addr); + chip->state = FL_READY; ++ } + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); ++ put_chip(map, chip, cmd_addr); + ++ cfi_spin_unlock(chip->mutex); + return 0; + } + ++ + static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) + { + struct map_info *map = mtd->priv; +@@ -361,6 +664,7 @@ + return ret; + } + ++ + static inline int do_read_secsi_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) + { + DECLARE_WAITQUEUE(wait, current); +@@ -394,12 +698,14 @@ + + chip->state = FL_READY; + ++ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x88, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + ++ /* should these be CFI_DEVICETYPE_X8 instead of cfi->device_type? */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, cfi->device_type, NULL); + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); +@@ -454,125 +760,241 @@ + return ret; + } + +-static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast) ++ ++static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, cfi_word datum, int fast) + { +- unsigned long timeo = jiffies + HZ; +- unsigned int oldstatus, status; +- unsigned int dq6, dq5; + struct cfi_private *cfi = map->fldrv_priv; +- DECLARE_WAITQUEUE(wait, current); ++ unsigned long timeo = jiffies + HZ; ++ cfi_word oldstatus, status, prev_oldstatus, prev_status; ++ cfi_word dq6 = CMD(1<<6); ++ /* ++ * We use a 1ms + 1 jiffies generic timeout for writes (most devices ++ * have a max write time of a few hundreds usec). However, we should ++ * use the maximum timeout value given by the chip at probe time ++ * instead. Unfortunately, struct flchip does have a field for ++ * maximum timeout, only for typical which can be far too short ++ * depending of the conditions. The ' + 1' is to avoid having a ++ * timeout of 0 jiffies if HZ is smaller than 1000. ++ */ ++ unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; + int ret = 0; ++ int ta = 0; ++ DECLARE_RETRY_CMD_CNT(); + +- retry: +- cfi_spin_lock(chip->mutex); +- +- if (chip->state != FL_READY) { +-#if 0 +- printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state); +-#endif +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ adr += chip->start; + ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + cfi_spin_unlock(chip->mutex); +- +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +-#if 0 +- printk(KERN_DEBUG "Wake up to write:\n"); +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; +- +- goto retry; ++ return ret; + } + +- chip->state = FL_WRITING; ++ RETRY_CMD_LABEL; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n", ++ __func__, adr, datum ); ++ ++ /* ++ * Check for a NOP for the case when the datum to write is already ++ * present - it saves time and works around buggy chips that corrupt ++ * data at other locations when 0xff is written to a location that ++ * already contains 0xff. ++ */ ++ status = cfi_read(map, adr); ++ if (status == datum) { ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): NOP 0x%.8x == 0x%.8x\n", ++ __func__, status, datum ); ++ goto op_done; ++ } + +- adr += chip->start; + ENABLE_VPP(map); + if (fast) { /* Unlock bypass */ + cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL); +- } +- else { ++ } else { ++ /* ++ * The CFI_DEVICETYPE_X8 argument is needed even when ++ * cfi->device_type != CFI_DEVICETYPE_X8. The addresses for ++ * command sequences don't scale even when the device is ++ * wider. This is the case for many of the cfi_send_gen_cmd() ++ * below. I'm not sure, however, why some use ++ * cfi->device_type. ++ */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + } +- + cfi_write(map, datum, adr); ++ chip->state = FL_WRITING; + + cfi_spin_unlock(chip->mutex); + cfi_udelay(chip->word_write_time); + cfi_spin_lock(chip->mutex); + +- /* Polling toggle bits instead of reading back many times +- This ensures that write operation is really completed, +- or tells us why it failed. */ +- dq6 = CMD(1<<6); +- dq5 = CMD(1<<5); +- timeo = jiffies + (HZ/1000); /* setting timeout to 1ms for now */ ++ /* ++ * Polling toggle bits instead of reading back many times This ensures ++ * that write operation is really completed, or tells us why it ++ * failed. ++ * ++ * It may appear that the polling and decoding of error state might be ++ * simplified. Don't do it unless you really know what you are doing. ++ * ++ * You must remember that JESD21-C 3.5.3 states that the status must ++ * be read back an _additional_ two times before a failure is ++ * determined. This is because these devices have internal state ++ * machines that are asynchronous to the external data bus. During an ++ * erase or write the read-back status of the polling bits might be ++ * transitioning internaly when the external read-back occurs. This ++ * means that the bits aren't in the final state and they might appear ++ * to report an error as they are in a transient state: dq7 is ++ * asynchronous with dq6 and other status bits. ++ * ++ * This asynchronous behaviour can cause infrequent errors that will ++ * usually disappear the next time an erase or write happens (Try ++ * tracking those errors down!). To ensure that the bits are not in ++ * transition, the location must be read-back two more times and ++ * compared against what was written - BOTH reads MUST match what was ++ * written. Don't think this can be simplified to only the last read ++ * matching the datum written: status bits *can* match the datum ++ * written. ++ * ++ * If the final comparison fails, error state can *then* be decoded. ++ * ++ * - Thayne Harbaugh ++ */ ++ /* See comment above for timeout value. */ ++ timeo = jiffies + uWriteTimeout; ++ for (;;) { ++ if (chip->state != FL_WRITING) { ++ /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ cfi_spin_unlock(chip->mutex); ++ schedule(); ++ remove_wait_queue(&chip->wq, &wait); ++ timeo = jiffies + (HZ / 2); /* FIXME */ ++ cfi_spin_lock(chip->mutex); ++ continue; ++ } + + oldstatus = cfi_read(map, adr); + status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); + +- while( (status & dq6) != (oldstatus & dq6) && +- (status & dq5) != dq5 && +- !time_after(jiffies, timeo) ) { ++ /* ++ * This only checks if dq6 is still toggling and that our ++ * timer hasn't expired. We purposefully ignore the chip's ++ * internal timer that will assert dq5 and leave dq6 toggling. ++ * This is done for a variety of reasons: ++ * ++ * 1) Not all chips support dq5. ++ * ++ * 2) Dealing with asynchronous status bit and data updates ++ * and reading a device two more times creates _messy_ logic ++ * when trying to deal with interleaved devices - some may be ++ * changing while others are still busy. ++ * ++ * 3) Checking dq5 only helps to optimize an error case that ++ * should at worst be infrequent and at best non-existent. ++ * ++ * If our timeout occurs _then_ we will check dq5 to see if ++ * the device also had an internal timeout. ++ */ ++ if ( (((status ^ oldstatus) & dq6) == 0) ++ || ( ta = time_after(jiffies, timeo)) ) ++ break; + +- if (need_resched()) { ++ /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); +- yield(); ++ cfi_udelay(1); + cfi_spin_lock(chip->mutex); +- } else +- udelay(1); +- +- oldstatus = cfi_read( map, adr ); +- status = cfi_read( map, adr ); + } + +- if( (status & dq6) != (oldstatus & dq6) ) { +- /* The erasing didn't stop?? */ +- if( (status & dq5) == dq5 ) { +- /* When DQ5 raises, we must check once again +- if DQ6 is toggling. If not, the erase has been +- completed OK. If not, reset chip. */ ++ /* ++ * Something kicked us out of the read-back loop. We'll check success ++ * befor checking failure. Even though dq6 might be true data, it is ++ * unkown if all of the other bits have changed to true data due to ++ * the asynchronous nature of the internal state machine. We will ++ * read two more times and use this to either verify that the write ++ * completed successfully or that something really went wrong. BOTH ++ * reads must match what was written - this certifies that bits aren't ++ * still changing and that the status bits erroneously match the datum ++ * that was written. ++ */ ++ prev_oldstatus = oldstatus; ++ prev_status = status; + oldstatus = cfi_read(map, adr); + status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); + +- if ( (oldstatus & 0x00FF) == (status & 0x00FF) ) { +- printk(KERN_WARNING "Warning: DQ5 raised while program operation was in progress, however operation completed OK\n" ); ++ if ( oldstatus == datum && status == datum ) { ++ /* success - do nothing */ ++ goto op_done; ++ } ++ ++ if ( ta ) { ++ /* Only check dq5 on the chips that are still toggling. */ ++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; ++ if ( status & dq5mask ) { ++ /* dq5 asserted - decode interleave chips */ ++ printk( KERN_WARNING ++ "MTD %s(): FLASH internal timeout: 0x%.8x 0x%.8x 0x%8x\n", ++ __func__, ++ status & dq5mask, status, datum ); + } else { +- /* DQ5 is active so we can do a reset and stop the erase */ +- cfi_write(map, CMD(0xF0), chip->start); +- printk(KERN_WARNING "Internal flash device timeout occurred or write operation was performed while flash was programming.\n" ); ++ printk( KERN_WARNING ++ "MTD %s(): Software timed out during write.\n", ++ __func__ ); ++ } ++ goto op_failed; + } +- } else { +- printk(KERN_WARNING "Waiting for write to complete timed out in do_write_oneword."); + ++ /* ++ * If we get to here then it means that something ++ * is wrong and it's not a timeout. Something ++ * is seriously wacky! Dump some debug info. ++ */ ++ /* ++ * Found a clue about the chips that reach this state. ++ * Some flash chips (SST >cough<) ++ * are horribly broken. They do not ignore traffic that is ++ * destined to other devices. This happens because some solutions ++ * are on shared busses, the erase and program sequences have ++ * have multiple commands, and the sequence is interspersed with ++ * commands destined to other devices. A good flash chip will ++ * examine the command and destination address and will ignore ++ * commands that are for other devices. ++ */ ++ HANDLE_WACKY_STATE(); ++ ++ op_failed: ++ /* reset on all failures. */ ++ cfi_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ CHECK_RETRIES(); ++ ret = -EIO; ++ ++ op_done: + chip->state = FL_READY; +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); +- DISABLE_VPP(map); +- ret = -EIO; +- } +- } +- +- DISABLE_VPP(map); +- chip->state = FL_READY; +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); + + return ret; + } + +-static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_t *retlen, const u_char *buf) ++ ++static int cfi_amdstd_write_words(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int ret = 0; + int chipnum; + unsigned long ofs, chipstart; ++ DECLARE_WAITQUEUE(wait, current); + + *retlen = 0; + if (!len) +@@ -587,19 +1009,52 @@ + unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); + int i = ofs - bus_ofs; + int n = 0; +- u_char tmp_buf[4]; +- __u32 datum; ++ u_char tmp_buf[8]; ++ cfi_word datum; ++ ++ retry: ++ cfi_spin_lock(cfi->chips[chipnum].mutex); ++ ++ if (cfi->chips[chipnum].state != FL_READY) { ++#if 0 ++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); ++#endif ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&cfi->chips[chipnum].wq, &wait); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ schedule(); ++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait); ++#if 0 ++ if(signal_pending(current)) ++ return -EINTR; ++#endif ++ goto retry; ++ } ++ ++ map_copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); + +- map->copy_from(map, tmp_buf, bus_ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); +- while (len && i < CFIDEV_BUSWIDTH) +- tmp_buf[i++] = buf[n++], len--; ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ while (len && i < CFIDEV_BUSWIDTH) { ++ tmp_buf[i++] = buf[n++]; ++ len--; ++ } + ++ /* already know that buswidth > 1 */ + if (cfi_buswidth_is_2()) { + datum = *(__u16*)tmp_buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)tmp_buf; ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *(__u64*)tmp_buf; ++#endif + } else { +- return -EINVAL; /* should never happen, but be safe */ ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ return -EINVAL; + } + + ret = do_write_oneword(map, &cfi->chips[chipnum], +@@ -628,7 +1083,7 @@ + + /* We are now aligned, write as much as possible */ + while(len >= CFIDEV_BUSWIDTH) { +- __u32 datum; ++ cfi_word datum; + + if (cfi_buswidth_is_1()) { + datum = *(__u8*)buf; +@@ -636,7 +1091,13 @@ + datum = *(__u16*)buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)buf; ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *(__u64*)buf; ++#endif + } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); + return -EINVAL; + } + ret = do_write_oneword(map, &cfi->chips[chipnum], +@@ -685,10 +1146,34 @@ + /* Write the trailing bytes if any */ + if (len & (CFIDEV_BUSWIDTH-1)) { + int i = 0, n = 0; +- u_char tmp_buf[4]; +- __u32 datum; ++ u_char tmp_buf[8]; ++ cfi_word datum; ++ ++ retry1: ++ cfi_spin_lock(cfi->chips[chipnum].mutex); ++ ++ if (cfi->chips[chipnum].state != FL_READY) { ++#if 0 ++ printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", cfi->chips[chipnum].state); ++#endif ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&cfi->chips[chipnum].wq, &wait); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); ++ ++ schedule(); ++ remove_wait_queue(&cfi->chips[chipnum].wq, &wait); ++#if 0 ++ if(signal_pending(current)) ++ return -EINTR; ++#endif ++ goto retry1; ++ } ++ ++ map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); ++ ++ cfi_spin_unlock(cfi->chips[chipnum].mutex); + +- map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); + while (len--) + tmp_buf[i++] = buf[n++]; + +@@ -696,8 +1181,14 @@ + datum = *(__u16*)tmp_buf; + } else if (cfi_buswidth_is_4()) { + datum = *(__u32*)tmp_buf; ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *(__u64*)tmp_buf; ++#endif + } else { +- return -EINVAL; /* should never happen, but be safe */ ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ return -EINVAL; + } + + ret = do_write_oneword(map, &cfi->chips[chipnum], +@@ -711,289 +1202,446 @@ + return 0; + } + +-static inline int do_erase_chip(struct map_info *map, struct flchip *chip) ++ ++/* ++ * FIXME: interleaved mode not tested, and probably not supported! ++ */ ++static inline int do_write_buffer(struct map_info *map, struct flchip *chip, ++ unsigned long adr, const u_char *buf, int len) + { +- unsigned int oldstatus, status; +- unsigned int dq6, dq5; +- unsigned long timeo = jiffies + HZ; +- unsigned int adr; + struct cfi_private *cfi = map->fldrv_priv; +- DECLARE_WAITQUEUE(wait, current); +- +- retry: +- cfi_spin_lock(chip->mutex); ++ unsigned long timeo = jiffies + HZ; ++ cfi_word oldstatus, status, prev_oldstatus, prev_status; ++ cfi_word dq6 = CMD(1<<6); ++ /* see comments in do_write_oneword() regarding uWriteTimeo. */ ++ static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; ++ int ret = -EIO; ++ int ta = 0; ++ unsigned long cmd_adr; ++ int z, bytes, words; ++ cfi_word datum; ++ DECLARE_RETRY_CMD_CNT(); + +- if (chip->state != FL_READY){ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ adr += chip->start; ++ cmd_adr = adr; + ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + cfi_spin_unlock(chip->mutex); ++ return ret; ++ } + +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if(signal_pending(current)) +- return -EINTR; ++ if (cfi_buswidth_is_1()) { ++ datum = *(__u8*)buf; ++ } else if (cfi_buswidth_is_2()) { ++ datum = *(__u16*)buf; ++ } else if (cfi_buswidth_is_4()) { ++ datum = *(__u32*)buf; ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *(__u64*)buf; + #endif +- timeo = jiffies + HZ; +- +- goto retry; ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ return -EINVAL; + } + +- chip->state = FL_ERASING; ++ RETRY_CMD_LABEL; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n", ++ __func__, adr, datum ); + +- /* Handle devices with one erase region, that only implement +- * the chip erase command. +- */ + ENABLE_VPP(map); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- timeo = jiffies + (HZ*20); +- adr = cfi->addr_unlock1; ++ //cfi_send_gen_cmd(0xA0, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + +- /* Wait for the end of programing/erasure by using the toggle method. +- * As long as there is a programming procedure going on, bit 6 of the last +- * written byte is toggling it's state with each consectuve read. +- * The toggling stops as soon as the procedure is completed. +- * +- * If the process has gone on for too long on the chip bit 5 gets. +- * After bit5 is set you can kill the operation by sending a reset +- * command to the chip. +- */ +- dq6 = CMD(1<<6); +- dq5 = CMD(1<<5); ++ /* Write Buffer Load */ ++ cfi_write(map, CMD(0x25), cmd_adr); + +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); +- while( ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5) && +- !time_after(jiffies, timeo)) { +- int wait_reps; ++ chip->state = FL_WRITING_TO_BUFFER; ++ ++ /* Write length of data to come */ ++ bytes = len & (CFIDEV_BUSWIDTH-1); ++ words = len / CFIDEV_BUSWIDTH; ++ cfi_write(map, CMD(words - !bytes), cmd_adr ); ++ /* Write data */ ++ z = 0; ++ while(z < words * CFIDEV_BUSWIDTH) { ++ if (cfi_buswidth_is_1()) { ++ datum = *((__u8*)buf); ++ map_write8 (map, *((__u8*)buf)++, adr+z); ++ } else if (cfi_buswidth_is_2()) { ++ datum = *((__u16*)buf); ++ map_write16 (map, *((__u16*)buf)++, adr+z); ++ } else if (cfi_buswidth_is_4()) { ++ datum = *((__u32*)buf); ++ map_write32 (map, *((__u32*)buf)++, adr+z); ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *((__u64*)buf); ++ map_write64 (map, *((__u64*)buf)++, adr+z); ++#endif ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ ret = -EINVAL; ++ goto op_failed; ++ } ++ z += CFIDEV_BUSWIDTH; ++ } ++ if (bytes) { ++ int i = 0, n = 0; ++ u_char tmp_buf[8], *tmp_p = tmp_buf; ++ ++ while (bytes--) ++ tmp_buf[i++] = buf[n++]; ++ while (i < CFIDEV_BUSWIDTH) ++ tmp_buf[i++] = 0xff; ++ if (cfi_buswidth_is_2()) { ++ datum = *((__u16*)tmp_p); ++ map_write16 (map, *((__u16*)tmp_p)++, adr+z); ++ } else if (cfi_buswidth_is_4()) { ++ datum = *((__u32*)tmp_p); ++ map_write32 (map, *((__u32*)tmp_p)++, adr+z); ++#ifdef CFI_WORD_64 ++ } else if (cfi_buswidth_is_8()) { ++ datum = *((__u64*)tmp_p); ++ map_write64 (map, *((__u64*)tmp_p)++, adr+z); ++#endif ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ ret = -EINVAL; ++ goto op_failed; ++ } ++ } else if (words > 0) { ++ z -= CFIDEV_BUSWIDTH; ++ } ++ ++ adr += z; ++ ++ /* Write Buffer Program Confirm: GO GO GO */ ++ cfi_write(map, CMD(0x29), cmd_adr); ++ chip->state = FL_WRITING; + +- /* an initial short sleep */ + cfi_spin_unlock(chip->mutex); +- schedule_timeout(HZ/100); ++ cfi_udelay(chip->buffer_write_time); + cfi_spin_lock(chip->mutex); + +- if (chip->state != FL_ERASING) { +- /* Someone's suspended the erase. Sleep */ ++ timeo = jiffies + uWriteTimeout; ++ ++ for (;;) { ++ if (chip->state != FL_WRITING) { ++ /* Someone's suspended the write. Sleep */ ++ DECLARE_WAITQUEUE(wait, current); ++ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); +- + cfi_spin_unlock(chip->mutex); +- printk("erase suspended. Sleeping\n"); +- + schedule(); + remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if (signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + (HZ*2); /* FIXME */ ++ timeo = jiffies + (HZ / 2); /* FIXME */ + cfi_spin_lock(chip->mutex); + continue; + } + +- /* Busy wait for 1/10 of a milisecond */ +- for(wait_reps = 0; +- (wait_reps < 100) && +- ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5); +- wait_reps++) { ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ ++ /* See comments in do_write_oneword() about checking status */ ++ if ( (((status ^ oldstatus) & dq6) == 0) ++ || ( ta = time_after(jiffies, timeo)) ) { ++ break; ++ } + + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); +- + cfi_udelay(1); +- + cfi_spin_lock(chip->mutex); +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); + } ++ ++ /* See comments in do_write_oneword() about "two more checks" */ ++ prev_oldstatus = oldstatus; ++ prev_status = status; + oldstatus = cfi_read(map, adr); + status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ ++ if ( oldstatus == datum && status == datum ) { ++ /* success - do nothing */ ++ goto op_done; ++ } ++ ++ if ( ta ) { ++ /* Only check dq5 on the chips that are still toggling. */ ++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; ++ if ( status & dq5mask ) { ++ /* dq5 asserted - decode interleave chips */ ++ printk( KERN_WARNING ++ "MTD %s(): FLASH internal timeout: 0x%.8x 0x%.8x 0x%8x\n", ++ __func__, ++ status & dq5mask, status, datum ); ++ } else { ++ printk( KERN_WARNING ++ "MTD %s(): Software timed out during write.\n", ++ __func__ ); + } +- if ((status & dq6) != (oldstatus & dq6)) { +- /* The erasing didn't stop?? */ +- if ((status & dq5) == dq5) { +- /* dq5 is active so we can do a reset and stop the erase */ +- cfi_write(map, CMD(0xF0), chip->start); ++ goto op_failed; + } ++ ++ HANDLE_WACKY_STATE(); ++ ++ op_failed: ++ /* reset on all failures. */ ++ cfi_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ CHECK_RETRIES(); ++ ret = -EIO; ++ ++ op_done: + chip->state = FL_READY; +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); +- printk("waiting for erase to complete timed out."); +- DISABLE_VPP(map); +- return -EIO; ++ ++ return ret; ++} ++ ++ ++static int cfi_amdstd_write_buffers(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct map_info *map = mtd->priv; ++ struct cfi_private *cfi = map->fldrv_priv; ++ int wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; ++ int ret = 0; ++ int chipnum; ++ unsigned long ofs; ++ ++ *retlen = 0; ++ if (!len) ++ return 0; ++ ++ chipnum = to >> cfi->chipshift; ++ ofs = to - (chipnum << cfi->chipshift); ++ ++ /* If it's not bus-aligned, do the first word write */ ++ if (ofs & (CFIDEV_BUSWIDTH-1)) { ++ size_t local_len = (-ofs)&(CFIDEV_BUSWIDTH-1); ++ if (local_len > len) ++ local_len = len; ++ ret = cfi_amdstd_write_words(mtd, to, local_len, ++ retlen, buf); ++ if (ret) ++ return ret; ++ ofs += local_len; ++ buf += local_len; ++ len -= local_len; ++ ++ if (ofs >> cfi->chipshift) { ++ chipnum ++; ++ ofs = 0; ++ if (chipnum == cfi->numchips) ++ return 0; ++ } ++ } ++ ++ /* Write buffer is worth it only if more than one word to write... */ ++ while (len) { ++ /* We must not cross write block boundaries */ ++ int size = wbufsize - (ofs & (wbufsize-1)); ++ ++ if (size > len) ++ size = len; ++ ret = do_write_buffer(map, &cfi->chips[chipnum], ++ ofs, buf, size); ++ if (ret) ++ return ret; ++ ++ ofs += size; ++ buf += size; ++ (*retlen) += size; ++ len -= size; ++ ++ if (ofs >> cfi->chipshift) { ++ chipnum ++; ++ ofs = 0; ++ if (chipnum == cfi->numchips) ++ return 0; ++ } + } +- DISABLE_VPP(map); +- chip->state = FL_READY; +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); + + return 0; + } + +-static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) ++ ++/* ++ * Handle devices with one erase region, that only implement ++ * the chip erase command. ++ */ ++static inline int do_erase_chip(struct map_info *map, struct flchip *chip) + { +- unsigned int oldstatus, status; +- unsigned int dq6, dq5; +- unsigned long timeo = jiffies + HZ; + struct cfi_private *cfi = map->fldrv_priv; ++ cfi_word oldstatus, status, prev_oldstatus, prev_status; ++ cfi_word dq6 = CMD(1<<6); ++ unsigned long timeo = jiffies + HZ; ++ unsigned long int adr; + DECLARE_WAITQUEUE(wait, current); ++ int ret = 0; ++ int ta = 0; ++ cfi_word datum = 0; ++ DECLARE_RETRY_CMD_CNT(); + +- retry: +- cfi_spin_lock(chip->mutex); +- +- if (chip->state != FL_READY){ +- set_current_state(TASK_UNINTERRUPTIBLE); +- add_wait_queue(&chip->wq, &wait); ++ adr = cfi->addr_unlock1; + ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_WRITING); ++ if (ret) { + cfi_spin_unlock(chip->mutex); +- +- schedule(); +- remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if(signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + HZ; +- +- goto retry; ++ return ret; + } + +- chip->state = FL_ERASING; ++ RETRY_CMD_LABEL; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", ++ __func__, chip->start ); + +- adr += chip->start; + ENABLE_VPP(map); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); +- cfi_write(map, CMD(0x30), adr); +- +- timeo = jiffies + (HZ*20); +- +- /* Wait for the end of programing/erasure by using the toggle method. +- * As long as there is a programming procedure going on, bit 6 of the last +- * written byte is toggling it's state with each consectuve read. +- * The toggling stops as soon as the procedure is completed. +- * +- * If the process has gone on for too long on the chip bit 5 gets. +- * After bit5 is set you can kill the operation by sending a reset +- * command to the chip. +- */ +- dq6 = CMD(1<<6); +- dq5 = CMD(1<<5); ++ cfi_send_gen_cmd(0x10, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); + +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); +- while( ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5) && +- !time_after(jiffies, timeo)) { +- int wait_reps; ++ chip->state = FL_ERASING; ++ chip->erase_suspended = 0; ++ chip->in_progress_block_addr = adr; + +- /* an initial short sleep */ + cfi_spin_unlock(chip->mutex); +- schedule_timeout(HZ/100); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout((chip->erase_time*HZ)/(2*1000)); + cfi_spin_lock(chip->mutex); + ++ timeo = jiffies + (HZ*20); ++ ++ for (;;) { + if (chip->state != FL_ERASING) { + /* Someone's suspended the erase. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); +- + cfi_spin_unlock(chip->mutex); +- printk(KERN_DEBUG "erase suspended. Sleeping\n"); +- + schedule(); + remove_wait_queue(&chip->wq, &wait); +-#if 0 +- if (signal_pending(current)) +- return -EINTR; +-#endif +- timeo = jiffies + (HZ*2); /* FIXME */ + cfi_spin_lock(chip->mutex); + continue; + } ++ if (chip->erase_suspended) { ++ /* This erase was suspended and resumed. ++ Adjust the timeout */ ++ timeo = jiffies + (HZ*20); /* FIXME */ ++ chip->erase_suspended = 0; ++ } + +- /* Busy wait for 1/10 of a milisecond */ +- for(wait_reps = 0; +- (wait_reps < 100) && +- ((status & dq6) != (oldstatus & dq6)) && +- ((status & dq5) != dq5); +- wait_reps++) { ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ if ( (((status ^ oldstatus) & dq6) == 0) ++ || ( ta = time_after(jiffies, timeo)) ) ++ break; + + /* Latency issues. Drop the lock, wait a while and retry */ + cfi_spin_unlock(chip->mutex); +- +- cfi_udelay(1); +- ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(1); + cfi_spin_lock(chip->mutex); +- oldstatus = cfi_read(map, adr); +- status = cfi_read(map, adr); + } ++ ++ prev_oldstatus = oldstatus; ++ prev_status = status; + oldstatus = cfi_read(map, adr); + status = cfi_read(map, adr); +- } +- if( (status & dq6) != (oldstatus & dq6) ) +- { +- /* The erasing didn't stop?? */ +- if( ( status & dq5 ) == dq5 ) +- { +- /* When DQ5 raises, we must check once again if DQ6 is toggling. +- If not, the erase has been completed OK. If not, reset chip. */ +- oldstatus = cfi_read( map, adr ); +- status = cfi_read( map, adr ); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); + +- if( ( oldstatus & 0x00FF ) == ( status & 0x00FF ) ) +- { +- printk( "Warning: DQ5 raised while erase operation was in progress, but erase completed OK\n" ); +- } +- else +- { +- /* DQ5 is active so we can do a reset and stop the erase */ +- cfi_write(map, CMD(0xF0), chip->start); +- printk( KERN_WARNING "Internal flash device timeout occured or write operation was performed while flash was erasing\n" ); ++ if ( cfi_buswidth_is_1() ) { ++ datum = (__u8)~0; ++ } else if ( cfi_buswidth_is_2() ) { ++ datum = (__u16)~0; ++ } else if ( cfi_buswidth_is_4() ) { ++ datum = (__u32)~0; ++#ifdef CFI_WORD_64 ++ } else if ( cfi_buswidth_is_8() ) { ++ datum = (__u64)~0; ++#endif ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ goto op_failed; ++ } ++ ++ if ( oldstatus == datum && status == datum ) { ++ /* success - do nothing */ ++ goto op_done; ++ } ++ ++ if ( ta ) { ++ /* Only check dq5 on the chips that are still toggling. */ ++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; ++ if ( status & dq5mask ) { ++ /* dq5 asserted - decode interleave chips */ ++ printk( KERN_WARNING ++ "MTD %s(): FLASH internal timeout: 0x%.8x\n", ++ __func__, ++ status & dq5mask ); ++ } else { ++ printk( KERN_WARNING ++ "MTD %s(): Software timed out during write.\n", ++ __func__ ); + } ++ goto op_failed; + } +- else +- { +- printk( "Waiting for erase to complete timed out in do_erase_oneblock."); + +- chip->state = FL_READY; +- wake_up(&chip->wq); +- cfi_spin_unlock(chip->mutex); +- DISABLE_VPP(map); +- return -EIO; +- } +- } ++ HANDLE_WACKY_STATE(); + +- DISABLE_VPP(map); ++ op_failed: ++ /* reset on all failures. */ ++ cfi_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ CHECK_RETRIES(); ++ ret = -EIO; ++ ++ op_done: + chip->state = FL_READY; +- wake_up(&chip->wq); ++ put_chip(map, chip, adr); + cfi_spin_unlock(chip->mutex); +- return 0; ++ ++ return ret; + } + +-static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) ++ ++typedef int (*frob_t)(struct map_info *map, struct flchip *chip, ++ unsigned long adr, void *thunk); ++ ++ ++static int cfi_amdstd_varsize_frob(struct mtd_info *mtd, frob_t frob, ++ loff_t ofs, size_t len, void *thunk) + { + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- unsigned long adr, len; ++ unsigned long adr; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + +- if (instr->addr > mtd->size) ++ if (ofs > mtd->size) + return -EINVAL; + +- if ((instr->len + instr->addr) > mtd->size) ++ if ((len + ofs) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are +@@ -1008,7 +1656,7 @@ + start of the requested erase, and then go back one. + */ + +- while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) ++ while (i < mtd->numeraseregions && ofs >= regions[i].offset) + i++; + i--; + +@@ -1018,7 +1666,7 @@ + effect here. + */ + +- if (instr->addr & (regions[i].erasesize-1)) ++ if (ofs & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ +@@ -1028,7 +1676,7 @@ + * with the erase region at that address. + */ + +- while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset) ++ while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which +@@ -1036,17 +1684,16 @@ + */ + i--; + +- if ((instr->addr + instr->len) & (regions[i].erasesize-1)) ++ if ((ofs + len) & (regions[i].erasesize-1)) + return -EINVAL; + +- chipnum = instr->addr >> cfi->chipshift; +- adr = instr->addr - (chipnum << cfi->chipshift); +- len = instr->len; ++ chipnum = ofs >> cfi->chipshift; ++ adr = ofs - (chipnum << cfi->chipshift); + + i=first; + +- while(len) { +- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); ++ while (len) { ++ ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); + + if (ret) + return ret; +@@ -1066,50 +1713,171 @@ + } + } + +- instr->state = MTD_ERASE_DONE; +- if (instr->callback) +- instr->callback(instr); +- + return 0; + } + +-static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *instr) ++ ++static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) + { +- struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; +- unsigned long adr, len; +- int chipnum, ret = 0; ++ cfi_word oldstatus, status, prev_oldstatus, prev_status; ++ cfi_word dq6 = CMD(1<<6); ++ unsigned long timeo = jiffies + HZ; ++ DECLARE_WAITQUEUE(wait, current); ++ int ret = 0; ++ int ta = 0; ++ cfi_word datum = 0; ++ DECLARE_RETRY_CMD_CNT(); + +- if (instr->addr & (mtd->erasesize - 1)) +- return -EINVAL; ++ adr += chip->start; + +- if (instr->len & (mtd->erasesize -1)) +- return -EINVAL; ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_ERASING); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } + +- if ((instr->len + instr->addr) > mtd->size) +- return -EINVAL; ++ RETRY_CMD_LABEL; ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", ++ __func__, adr ); + +- chipnum = instr->addr >> cfi->chipshift; +- adr = instr->addr - (chipnum << cfi->chipshift); +- len = instr->len; ++ ENABLE_VPP(map); ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x80, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chip->start, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_write(map, CMD(0x30), adr); + +- while(len) { +- ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); ++ chip->state = FL_ERASING; ++ chip->erase_suspended = 0; ++ chip->in_progress_block_addr = adr; + +- if (ret) +- return ret; ++ cfi_spin_unlock(chip->mutex); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout((chip->erase_time*HZ)/(2*1000)); ++ cfi_spin_lock(chip->mutex); + +- adr += mtd->erasesize; +- len -= mtd->erasesize; ++ timeo = jiffies + (HZ*20); + +- if (adr >> cfi->chipshift) { +- adr = 0; +- chipnum++; ++ /* Wait for the end of programing/erasure by using the toggle method. ++ * As long as there is a programming procedure going on, bit 6 is ++ * toggling its state with each consecutive read. The toggling stops ++ * as soon as the procedure is completed. ++ * ++ * If the process has gone on for too long on the chip, bit 5 gets ++ * set. After bit5 is set you can kill the operation by sending a ++ * reset command to the chip. ++ */ ++ /* See comments in do_write_oneword(). */ + +- if (chipnum >= cfi->numchips) ++ for (;;) { ++ if (chip->state != FL_ERASING) { ++ /* Someone's suspended the erase. Sleep */ ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ add_wait_queue(&chip->wq, &wait); ++ cfi_spin_unlock(chip->mutex); ++ schedule(); ++ remove_wait_queue(&chip->wq, &wait); ++ cfi_spin_lock(chip->mutex); ++ continue; ++ } ++ if (chip->erase_suspended) { ++ /* This erase was suspended and resumed. ++ Adjust the timeout */ ++ timeo = jiffies + (HZ*20); /* FIXME */ ++ chip->erase_suspended = 0; ++ } ++ ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ if ( (((status ^ oldstatus) & dq6) == 0) ++ || ( ta = time_after(jiffies, timeo)) ) + break; ++ ++ /* Latency issues. Drop the lock, wait a while and retry */ ++ cfi_spin_unlock(chip->mutex); ++ set_current_state(TASK_UNINTERRUPTIBLE); ++ schedule_timeout(1); ++ cfi_spin_lock(chip->mutex); + } ++ ++ prev_oldstatus = oldstatus; ++ prev_status = status; ++ oldstatus = cfi_read(map, adr); ++ status = cfi_read(map, adr); ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", ++ __func__, oldstatus, status ); ++ ++ if ( cfi_buswidth_is_1() ) { ++ datum = (__u8)~0; ++ } else if ( cfi_buswidth_is_2() ) { ++ datum = (__u16)~0; ++ } else if ( cfi_buswidth_is_4() ) { ++ datum = (__u32)~0; ++#ifdef CFI_WORD_64 ++ } else if ( cfi_buswidth_is_8() ) { ++ datum = (__u64)~0; ++#endif ++ } else { ++ printk(KERN_WARNING "MTD %s(): Unsupported buswidth %d\n", ++ __func__, CFIDEV_BUSWIDTH); ++ goto op_failed; ++ } ++ ++ if ( oldstatus == datum && status == datum ) { ++ /* success - do nothing */ ++ goto op_done; ++ } ++ ++ if ( ta ) { ++ /* Only check dq5 on the chips that are still toggling. */ ++ cfi_word dq5mask = ( ( status ^ oldstatus ) & dq6 ) >> 1; ++ if ( status & dq5mask ) { ++ /* dq5 asserted - decode interleave chips */ ++ printk( KERN_WARNING ++ "MTD %s(): FLASH internal timeout: 0x%.8x\n", ++ __func__, ++ status & dq5mask ); ++ } else { ++ printk( KERN_WARNING ++ "MTD %s(): Software timed out during write.\n", ++ __func__ ); + } ++ goto op_failed; ++ } ++ ++ HANDLE_WACKY_STATE(); ++ ++ op_failed: ++ /* reset on all failures. */ ++ cfi_write( map, CMD(0xF0), chip->start ); ++ /* FIXME - should have reset delay before continuing */ ++ CHECK_RETRIES(); ++ ret = -EIO; ++ ++ op_done: ++ chip->state = FL_READY; ++ put_chip(map, chip, adr); ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++} ++ ++ ++int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ unsigned long ofs, len; ++ int ret; ++ ++ ofs = instr->addr; ++ len = instr->len; ++ ++ ret = cfi_amdstd_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0); ++ if (ret) ++ return ret; + + instr->state = MTD_ERASE_DONE; + if (instr->callback) +@@ -1118,6 +1886,7 @@ + return 0; + } + ++ + static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) + { + struct map_info *map = mtd->priv; +@@ -1141,6 +1910,7 @@ + return 0; + } + ++ + static void cfi_amdstd_sync (struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -1254,6 +2024,7 @@ + return ret; + } + ++ + static void cfi_amdstd_resume(struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -1279,6 +2050,137 @@ + } + } + ++ ++#ifdef DEBUG_LOCK_BITS ++ ++static int do_printlockstatus_oneblock(struct map_info *map, ++ struct flchip *chip, ++ unsigned long adr, ++ void *thunk) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ int ofs_factor = cfi->interleave * cfi->device_type; ++ ++ cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); ++ printk(KERN_DEBUG "block status register for 0x%08lx is %x\n", ++ adr, cfi_read_query(map, adr+(2*ofs_factor))); ++ cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); ++ ++ return 0; ++} ++ ++ ++#define debug_dump_locks(mtd, frob, ofs, len, thunk) \ ++ cfi_amdstd_varsize_frob((mtd), (frob), (ofs), (len), (thunk)) ++ ++#else ++ ++#define debug_dump_locks(...) ++ ++#endif /* DEBUG_LOCK_BITS */ ++ ++ ++struct xxlock_thunk { ++ cfi_word val; ++ flstate_t state; ++}; ++ ++ ++#define DO_XXLOCK_ONEBLOCK_LOCK ((struct xxlock_thunk){0x01, FL_LOCKING}) ++#define DO_XXLOCK_ONEBLOCK_UNLOCK ((struct xxlock_thunk){0x00, FL_UNLOCKING}) ++ ++ ++/* ++ * FIXME - this is *very* specific to a particular chip. It likely won't ++ * work for all chips that require unlock. It also hasn't been tested ++ * with interleaved chips. ++ */ ++static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct xxlock_thunk *xxlt = (struct xxlock_thunk *)thunk; ++ int ret; ++ ++ /* ++ * This is easy because these are writes to registers and not writes ++ * to flash memory - that means that we don't have to check status ++ * and timeout. ++ */ ++ ++ adr += chip->start; ++ /* ++ * lock block registers: ++ * - on 64k boundariesand ++ * - bit 1 set high ++ * - block lock registers are 4MiB lower - overflow subtract (danger) ++ */ ++ adr = ((adr & ~0xffff) | 0x2) + ~0x3fffff; ++ ++ cfi_spin_lock(chip->mutex); ++ ret = get_chip(map, chip, adr, FL_LOCKING); ++ if (ret) { ++ cfi_spin_unlock(chip->mutex); ++ return ret; ++ } ++ ++ chip->state = xxlt->state; ++ cfi_write(map, CMD(xxlt->val), adr); ++ ++ /* Done and happy. */ ++ chip->state = FL_READY; ++ put_chip(map, chip, adr); ++ cfi_spin_unlock(chip->mutex); ++ return 0; ++} ++ ++ ++static int cfi_amdstd_lock_varsize(struct mtd_info *mtd, ++ loff_t ofs, ++ size_t len) ++{ ++ int ret; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", ++ __func__, ofs, len); ++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); ++ ++ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, ++ (void *)&DO_XXLOCK_ONEBLOCK_LOCK); ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "%s: lock status after, ret=%d\n", ++ __func__, ret); ++ ++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); ++ ++ return ret; ++} ++ ++ ++static int cfi_amdstd_unlock_varsize(struct mtd_info *mtd, ++ loff_t ofs, ++ size_t len) ++{ ++ int ret; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", ++ __func__, ofs, len); ++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); ++ ++ ret = cfi_amdstd_varsize_frob(mtd, do_xxlock_oneblock, ofs, len, ++ (void *)&DO_XXLOCK_ONEBLOCK_UNLOCK); ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "%s: lock status after, ret=%d\n", ++ __func__, ret); ++ debug_dump_locks(mtd, do_printlockstatus_oneblock, ofs, len, 0); ++ ++ return ret; ++} ++ ++ + static void cfi_amdstd_destroy(struct mtd_info *mtd) + { + struct map_info *map = mtd->priv; +@@ -1291,17 +2193,20 @@ + + static char im_name[]="cfi_cmdset_0002"; + ++ + int __init cfi_amdstd_init(void) + { + inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0002); + return 0; + } + ++ + static void __exit cfi_amdstd_exit(void) + { + inter_module_unregister(im_name); + } + ++ + module_init(cfi_amdstd_init); + module_exit(cfi_amdstd_exit); + +@@ -1309,3 +2214,7 @@ + MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al."); + MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); + ++#ifdef CONFIG_MTD_CFI_AMDSTD_RETRY ++MODULE_PARM(retry_cmd_max, "i"); ++MODULE_PARM_DESC(retry_cmd_max, "Number of times to retry an erase or program command if it fails - should only be needed by buggy hardware: default 0"); ++#endif +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0020.c linux/drivers/mtd/chips/cfi_cmdset_0020.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_cmdset_0020.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/cfi_cmdset_0020.c 2004-11-19 10:25:11.747222896 +0100 +@@ -21,16 +21,19 @@ + #include <linux/types.h> + #include <linux/kernel.h> + #include <linux/sched.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <asm/byteorder.h> + + #include <linux/errno.h> ++#include <linux/init.h> + #include <linux/slab.h> + #include <linux/delay.h> + #include <linux/interrupt.h> ++#include <linux/mtd/compatmac.h> + #include <linux/mtd/map.h> + #include <linux/mtd/cfi.h> +-#include <linux/mtd/compatmac.h> ++#include <linux/mtd/mtd.h> + + + static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); +@@ -51,10 +54,10 @@ + static struct mtd_info *cfi_staa_setup (struct map_info *); + + static struct mtd_chip_driver cfi_staa_chipdrv = { +- probe: NULL, /* Not usable directly */ +- destroy: cfi_staa_destroy, +- name: "cfi_cmdset_0020", +- module: THIS_MODULE ++ .probe = NULL, /* Not usable directly */ ++ .destroy = cfi_staa_destroy, ++ .name = "cfi_cmdset_0020", ++ .module = THIS_MODULE + }; + + /* #define DEBUG_LOCK_BITS */ +@@ -113,7 +116,6 @@ + { + struct cfi_private *cfi = map->fldrv_priv; + int i; +- __u32 base = cfi->chips[0].start; + + if (cfi->cfi_mode) { + /* +@@ -123,35 +125,10 @@ + */ + __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; + struct cfi_pri_intelext *extp; +- int ofs_factor = cfi->interleave * cfi->device_type; +- +- printk(" ST Microelectronics Extended Query Table at 0x%4.4X\n", adr); +- if (!adr) +- return NULL; +- +- /* Switch it into Query Mode */ +- cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); +- +- extp = kmalloc(sizeof(*extp), GFP_KERNEL); +- if (!extp) { +- printk(KERN_ERR "Failed to allocate memory\n"); +- return NULL; +- } +- +- /* Read in the Extended Query Table */ +- for (i=0; i<sizeof(*extp); i++) { +- ((unsigned char *)extp)[i] = +- cfi_read_query(map, (base+((adr+i)*ofs_factor))); +- } + +- if (extp->MajorVersion != '1' || +- (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { +- printk(KERN_WARNING " Unknown staa Extended Query " +- "version %c.%c.\n", extp->MajorVersion, +- extp->MinorVersion); +- kfree(extp); ++ extp = (struct cfi_pri_intelext*)cfi_read_pri(map, adr, sizeof(*extp), "ST Microelectronics"); ++ if (!extp) + return NULL; +- } + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); +@@ -172,11 +149,6 @@ + cfi->chips[i].erase_time = 1024; + } + +- map->fldrv = &cfi_staa_chipdrv; +- MOD_INC_USE_COUNT; +- +- /* Make sure it's in read mode */ +- cfi_send_gen_cmd(0xff, 0x55, base, map, cfi, cfi->device_type, NULL); + return cfi_staa_setup(map); + } + +@@ -208,6 +180,7 @@ + if (!mtd->eraseregions) { + printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); + kfree(cfi->cmdset_priv); ++ kfree(mtd); + return NULL; + } + +@@ -232,6 +205,7 @@ + printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); + kfree(mtd->eraseregions); + kfree(cfi->cmdset_priv); ++ kfree(mtd); + return NULL; + } + +@@ -256,7 +230,7 @@ + mtd->flags |= MTD_ECC; /* FIXME: Not all STMicro flashes have this */ + mtd->eccsize = 8; /* FIXME: Should be 0 for STMicro flashes w/out ECC */ + map->fldrv = &cfi_staa_chipdrv; +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + mtd->name = map->name; + return mtd; + } +@@ -288,7 +262,7 @@ + */ + switch (chip->state) { + case FL_ERASING: +- if (!((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2) ++ if (!(((struct cfi_pri_intelext *)cfi->cmdset_priv)->FeatureSupport & 2)) + goto sleep; /* We don't support erase suspend */ + + cfi_write (map, CMD(0xb0), cmd_addr); +@@ -374,7 +348,7 @@ + goto retry; + } + +- map->copy_from(map, buf, adr, len); ++ map_copy_from(map, buf, adr, len); + + if (suspended) { + chip->state = chip->oldstate; +@@ -540,11 +514,11 @@ + /* Write data */ + for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { + if (cfi_buswidth_is_1()) { +- map->write8 (map, *((__u8*)buf)++, adr+z); ++ map_write8 (map, *((__u8*)buf)++, adr+z); + } else if (cfi_buswidth_is_2()) { +- map->write16 (map, *((__u16*)buf)++, adr+z); ++ map_write16 (map, *((__u16*)buf)++, adr+z); + } else if (cfi_buswidth_is_4()) { +- map->write32 (map, *((__u32*)buf)++, adr+z); ++ map_write32 (map, *((__u32*)buf)++, adr+z); + } else { + DISABLE_VPP(map); + return -EINVAL; +@@ -1436,13 +1410,13 @@ + + static char im_name[]="cfi_cmdset_0020"; + +-mod_init_t cfi_staa_init(void) ++int __init cfi_staa_init(void) + { + inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020); + return 0; + } + +-mod_exit_t cfi_staa_exit(void) ++static void __exit cfi_staa_exit(void) + { + inter_module_unregister(im_name); + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_probe.c linux/drivers/mtd/chips/cfi_probe.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_probe.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/cfi_probe.c 2004-11-19 10:25:11.766220008 +0100 +@@ -1,13 +1,14 @@ + /* + Common Flash Interface probe code. + (C) 2000 Red Hat. GPL'd. +- $Id: cfi_probe.c,v 1.69 2002/05/11 22:13:03 dwmw2 Exp $ ++ $Id: cfi_probe.c,v 1.73 2003/11/08 00:51:21 dsaxena Exp $ + */ + + #include <linux/config.h> + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <asm/byteorder.h> + #include <linux/errno.h> +@@ -25,7 +26,7 @@ + #endif + + static int cfi_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi); ++ unsigned long *chip_map, struct cfi_private *cfi); + static int cfi_chip_setup(struct map_info *map, struct cfi_private *cfi); + + struct mtd_info *cfi_probe(struct map_info *map); +@@ -48,7 +49,7 @@ + } + + static int cfi_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi) ++ unsigned long *chip_map, struct cfi_private *cfi) + { + int i; + +@@ -77,18 +78,24 @@ + } + + /* Check each previous chip to see if it's an alias */ +- for (i=0; i<cfi->numchips; i++) { ++ for (i=0; i < (base >> cfi->chipshift); i++) { ++ unsigned long start; ++ if(!test_bit(i, chip_map)) { ++ /* Skip location; no valid chip at this address */ ++ continue; ++ } ++ start = i << cfi->chipshift; + /* This chip should be in read mode if it's one + we've already touched. */ +- if (qry_present(map,chips[i].start,cfi)) { ++ if (qry_present(map, start, cfi)) { + /* Eep. This chip also had the QRY marker. + * Is it an alias for the new one? */ +- cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xF0, 0, start, map, cfi, cfi->device_type, NULL); + + /* If the QRY marker goes away, it's an alias */ +- if (!qry_present(map, chips[i].start, cfi)) { ++ if (!qry_present(map, start, cfi)) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + /* Yes, it's actually got QRY for data. Most +@@ -99,7 +106,7 @@ + + if (qry_present(map, base, cfi)) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + } +@@ -107,13 +114,7 @@ + + /* OK, if we got to here, then none of the previous chips appear to + be aliases for the current one. */ +- if (cfi->numchips == MAX_CFI_CHIPS) { +- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); +- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ +- return -1; +- } +- chips[cfi->numchips].start = base; +- chips[cfi->numchips].state = FL_READY; ++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ + cfi->numchips++; + + /* Put it back into Read Mode */ +@@ -179,9 +180,28 @@ + (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1); + #endif + } ++ ++ /* Note we put the device back into Read Mode BEFORE going into Auto ++ * Select Mode, as some devices support nesting of modes, others ++ * don't. This way should always work. ++ * On cmdset 0001 the writes of 0xaa and 0x55 are not needed, and ++ * so should be treated as nops or illegal (and so put the device ++ * back into Read Mode, which is a nop in this case). ++ */ ++ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0xaa, 0x555, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x55, 0x2aa, base, map, cfi, cfi->device_type, NULL); ++ cfi_send_gen_cmd(0x90, 0x555, base, map, cfi, cfi->device_type, NULL); ++ cfi->mfr = cfi_read_query(map, base); ++ cfi->id = cfi_read_query(map, base + ofs_factor); ++ + /* Put it back into Read Mode */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + ++ printk(KERN_INFO "%s: Found %d x%d devices at 0x%x in %d-bit mode\n", ++ map->name, cfi->interleave, cfi->device_type*8, base, ++ map->buswidth*8); ++ + return 1; + } + +@@ -240,11 +260,11 @@ + printk("No Alternate Algorithm Table\n"); + + +- printk("Vcc Minimum: %x.%x V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); +- printk("Vcc Maximum: %x.%x V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); ++ printk("Vcc Minimum: %2d.%d V\n", cfip->VccMin >> 4, cfip->VccMin & 0xf); ++ printk("Vcc Maximum: %2d.%d V\n", cfip->VccMax >> 4, cfip->VccMax & 0xf); + if (cfip->VppMin) { +- printk("Vpp Minimum: %x.%x V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); +- printk("Vpp Maximum: %x.%x V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); ++ printk("Vpp Minimum: %2d.%d V\n", cfip->VppMin >> 4, cfip->VppMin & 0xf); ++ printk("Vpp Maximum: %2d.%d V\n", cfip->VppMax >> 4, cfip->VppMax & 0xf); + } + else + printk("No Vpp line\n"); +@@ -303,8 +323,8 @@ + #endif /* DEBUG_CFI */ + + static struct chip_probe cfi_chip_probe = { +- name: "CFI", +- probe_chip: cfi_probe_chip ++ .name = "CFI", ++ .probe_chip = cfi_probe_chip + }; + + struct mtd_info *cfi_probe(struct map_info *map) +@@ -317,9 +337,9 @@ + } + + static struct mtd_chip_driver cfi_chipdrv = { +- probe: cfi_probe, +- name: "cfi_probe", +- module: THIS_MODULE ++ .probe = cfi_probe, ++ .name = "cfi_probe", ++ .module = THIS_MODULE + }; + + int __init cfi_probe_init(void) +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/cfi_util.c linux/drivers/mtd/chips/cfi_util.c +--- linux-mips-2.4.27/drivers/mtd/chips/cfi_util.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/chips/cfi_util.c 2004-11-19 10:25:11.767219856 +0100 +@@ -0,0 +1,91 @@ ++/* ++ * Common Flash Interface support: ++ * Generic utility functions not dependant on command set ++ * ++ * Copyright (C) 2002 Red Hat ++ * Copyright (C) 2003 STMicroelectronics Limited ++ * ++ * This code is covered by the GPL. ++ * ++ * $Id: cfi_util.c,v 1.3 2003/11/14 19:50:03 thayne Exp $ ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <asm/io.h> ++#include <asm/byteorder.h> ++ ++#include <linux/errno.h> ++#include <linux/slab.h> ++#include <linux/delay.h> ++#include <linux/interrupt.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/cfi.h> ++#include <linux/mtd/compatmac.h> ++ ++struct cfi_extquery * ++cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, const char* name) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ __u32 base = 0; // cfi->chips[0].start; ++ int ofs_factor = cfi->interleave * cfi->device_type; ++ int i; ++ struct cfi_extquery *extp = NULL; ++ ++ printk(" %s Extended Query Table at 0x%4.4X\n", name, adr); ++ if (!adr) ++ goto out; ++ ++ /* Switch it into Query Mode */ ++ cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); ++ ++ extp = kmalloc(size, GFP_KERNEL); ++ if (!extp) { ++ printk(KERN_ERR "Failed to allocate memory\n"); ++ goto out; ++ } ++ ++ /* Read in the Extended Query Table */ ++ for (i=0; i<size; i++) { ++ ((unsigned char *)extp)[i] = ++ cfi_read_query(map, base+((adr+i)*ofs_factor)); ++ } ++ ++ if (extp->MajorVersion != '1' || ++ (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { ++ printk(KERN_WARNING " Unknown %s Extended Query " ++ "version %c.%c.\n", name, extp->MajorVersion, ++ extp->MinorVersion); ++ kfree(extp); ++ extp = NULL; ++ goto out; ++ } ++ ++out: ++ /* Make sure it's in read mode */ ++ cfi_send_gen_cmd(0xf0, 0, base, map, cfi, cfi->device_type, NULL); ++ ++ return extp; ++} ++ ++EXPORT_SYMBOL(cfi_read_pri); ++ ++void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups) ++{ ++ struct cfi_private *cfi = map->fldrv_priv; ++ struct cfi_fixup *f; ++ ++ for (f=fixups; f->fixup; f++) { ++ if (((f->mfr == CFI_MFR_ANY) || (f->mfr == cfi->mfr)) && ++ ((f->id == CFI_ID_ANY) || (f->id == cfi->id))) { ++ f->fixup(map, f->param); ++ } ++ } ++} ++ ++EXPORT_SYMBOL(cfi_fixup); ++ ++MODULE_LICENSE("GPL"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/chipreg.c linux/drivers/mtd/chips/chipreg.c +--- linux-mips-2.4.27/drivers/mtd/chips/chipreg.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/chipreg.c 2004-11-19 10:25:11.769219552 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: chipreg.c,v 1.13 2002/02/21 08:26:58 dwmw2 Exp $ ++ * $Id: chipreg.c,v 1.16 2003/05/29 09:36:15 dwmw2 Exp $ + * + * Registration for chip drivers + * +@@ -7,10 +7,13 @@ + + #include <linux/kernel.h> + #include <linux/config.h> ++#include <linux/module.h> + #include <linux/kmod.h> + #include <linux/spinlock.h> +-#include <linux/mtd/compatmac.h> ++#include <linux/slab.h> + #include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/compatmac.h> + + spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED; + static LIST_HEAD(chip_drvs_list); +@@ -44,10 +47,8 @@ + break; + } + } +- if (ret && !try_inc_mod_count(ret->module)) { +- /* Eep. Failed. */ ++ if (ret && !try_module_get(ret->module)) + ret = NULL; +- } + + spin_unlock(&chip_drvs_lock); + +@@ -64,32 +65,46 @@ + + drv = get_mtd_chip_driver(name); + +- if (!drv && !request_module(name)) ++ if (!drv && !request_module("%s", name)) + drv = get_mtd_chip_driver(name); + + if (!drv) + return NULL; + + ret = drv->probe(map); +-#ifdef CONFIG_MODULES ++ + /* We decrease the use count here. It may have been a + probe-only module, which is no longer required from this + point, having given us a handle on (and increased the use + count of) the actual driver code. + */ +- if(drv->module) +- __MOD_DEC_USE_COUNT(drv->module); +-#endif ++ module_put(drv->module); + + if (ret) + return ret; + + return NULL; + } ++/* ++ * Destroy an MTD device which was created for a map device. ++ * Make sure the MTD device is already unregistered before calling this ++ */ ++void map_destroy(struct mtd_info *mtd) ++{ ++ struct map_info *map = mtd->priv; ++ ++ if (map->fldrv->destroy) ++ map->fldrv->destroy(mtd); ++ ++ module_put(map->fldrv->module); ++ ++ kfree(mtd); ++} + + EXPORT_SYMBOL(register_mtd_chip_driver); + EXPORT_SYMBOL(unregister_mtd_chip_driver); + EXPORT_SYMBOL(do_map_probe); ++EXPORT_SYMBOL(map_destroy); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/gen_probe.c linux/drivers/mtd/chips/gen_probe.c +--- linux-mips-2.4.27/drivers/mtd/chips/gen_probe.c 2003-08-13 19:19:18.000000000 +0200 ++++ linux/drivers/mtd/chips/gen_probe.c 2004-11-19 10:25:11.770219400 +0100 +@@ -1,14 +1,17 @@ + /* + * Routines common to all CFI-type probes. +- * (C) 2001, 2001 Red Hat, Inc. ++ * (C) 2001-2003 Red Hat, Inc. + * GPL'd +- * $Id: gen_probe.c,v 1.9 2002/09/05 05:15:32 acurtis Exp $ ++ * $Id: gen_probe.c,v 1.14 2003/11/08 00:51:21 dsaxena Exp $ + */ + + #include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/module.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> + #include <linux/mtd/cfi.h> ++#include <linux/mtd/mtd.h> + #include <linux/mtd/gen_probe.h> + + static struct mtd_info *check_cmd_set(struct map_info *, int); +@@ -50,11 +53,11 @@ + + struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe *cp) + { +- unsigned long base=0; + struct cfi_private cfi; + struct cfi_private *retcfi; +- struct flchip chip[MAX_CFI_CHIPS]; +- int i; ++ unsigned long *chip_map; ++ int i, j; ++ int max_chips; + + memset(&cfi, 0, sizeof(cfi)); + +@@ -77,8 +80,6 @@ + return NULL; + } + #endif +- chip[0].start = 0; +- chip[0].state = FL_READY; + cfi.chipshift = cfi.cfiq->DevSize; + + switch(cfi.interleave) { +@@ -103,20 +104,28 @@ + cfi.numchips = 1; + + /* ++ * Allocate memory for bitmap of valid chips. ++ * Align bitmap storage size to full byte. ++ */ ++ max_chips = map->size >> cfi.chipshift; ++ chip_map = kmalloc((max_chips / 8) + ((max_chips % 8) ? 1 : 0), GFP_KERNEL); ++ if (!chip_map) { ++ printk(KERN_WARNING "%s: kmalloc failed for CFI chip map\n", map->name); ++ kfree(cfi.cfiq); ++ return NULL; ++ } ++ ++ set_bit(0, chip_map); /* Mark first chip valid */ ++ ++ /* + * Now probe for other chips, checking sensibly for aliases while + * we're at it. The new_chip probe above should have let the first + * chip in read mode. +- * +- * NOTE: Here, we're checking if there is room for another chip +- * the same size within the mapping. Therefore, +- * base + chipsize <= map->size is the correct thing to do, +- * because, base + chipsize would be the _first_ byte of the +- * next chip, not the one we're currently pondering. + */ + +- for (base = (1<<cfi.chipshift); base + (1<<cfi.chipshift) <= map->size; +- base += (1<<cfi.chipshift)) +- cp->probe_chip(map, base, &chip[0], &cfi); ++ for (i = 1; i < max_chips; i++) { ++ cp->probe_chip(map, i << cfi.chipshift, chip_map, &cfi); ++ } + + /* + * Now allocate the space for the structures we need to return to +@@ -128,19 +137,26 @@ + if (!retcfi) { + printk(KERN_WARNING "%s: kmalloc failed for CFI private structure\n", map->name); + kfree(cfi.cfiq); ++ kfree(chip_map); + return NULL; + } + + memcpy(retcfi, &cfi, sizeof(cfi)); +- memcpy(&retcfi->chips[0], chip, sizeof(struct flchip) * cfi.numchips); ++ memset(&retcfi->chips[0], 0, sizeof(struct flchip) * cfi.numchips); + +- /* Fix up the stuff that breaks when you move it */ +- for (i=0; i< retcfi->numchips; i++) { +- init_waitqueue_head(&retcfi->chips[i].wq); +- spin_lock_init(&retcfi->chips[i]._spinlock); +- retcfi->chips[i].mutex = &retcfi->chips[i]._spinlock; ++ for (i = 0, j = 0; (j < cfi.numchips) && (i < max_chips); i++) { ++ if(test_bit(i, chip_map)) { ++ struct flchip *pchip = &retcfi->chips[j++]; ++ ++ pchip->start = (i << cfi.chipshift); ++ pchip->state = FL_READY; ++ init_waitqueue_head(&pchip->wq); ++ spin_lock_init(&pchip->_spinlock); ++ pchip->mutex = &pchip->_spinlock; ++ } + } + ++ kfree(chip_map); + return retcfi; + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/jedec.c linux/drivers/mtd/chips/jedec.c +--- linux-mips-2.4.27/drivers/mtd/chips/jedec.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/jedec.c 2004-11-19 10:25:11.772219096 +0100 +@@ -11,10 +11,16 @@ + * not going to guess how to send commands to them, plus I expect they will + * all speak CFI.. + * +- * $Id: jedec.c,v 1.14 2002/06/27 02:19:12 dwmw2 Exp $ ++ * $Id: jedec.c,v 1.19 2003/05/29 09:25:23 dwmw2 Exp $ + */ + ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/kernel.h> + #include <linux/mtd/jedec.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/compatmac.h> + + static struct mtd_info *jedec_probe(struct map_info *); + static int jedec_probe8(struct map_info *map,unsigned long base, +@@ -33,14 +39,51 @@ + + /* Listing of parts and sizes. We need this table to learn the sector + size of the chip and the total length */ +-static const struct JEDECTable JEDEC_table[] = +- {{0x013D,"AMD Am29F017D",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x01AD,"AMD Am29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x01D5,"AMD Am29F080",1*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x01A4,"AMD Am29F040",512*1024,64*1024,MTD_CAP_NORFLASH}, +- {0x20E3,"AMD Am29W040B",512*1024,64*1024,MTD_CAP_NORFLASH}, +- {0xC2AD,"Macronix MX29F016",2*1024*1024,64*1024,MTD_CAP_NORFLASH}, +- {}}; ++static const struct JEDECTable JEDEC_table[] = { ++ { ++ .jedec = 0x013D, ++ .name = "AMD Am29F017D", ++ .size = 2*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x01AD, ++ .name = "AMD Am29F016", ++ .size = 2*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x01D5, ++ .name = "AMD Am29F080", ++ .size = 1*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x01A4, ++ .name = "AMD Am29F040", ++ .size = 512*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0x20E3, ++ .name = "AMD Am29W040B", ++ .size = 512*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { ++ .jedec = 0xC2AD, ++ .name = "Macronix MX29F016", ++ .size = 2*1024*1024, ++ .sectorsize = 64*1024, ++ .capabilities = MTD_CAP_NORFLASH ++ }, ++ { .jedec = 0x0 } ++}; + + static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id); + static void jedec_sync(struct mtd_info *mtd) {}; +@@ -54,9 +97,9 @@ + + + static struct mtd_chip_driver jedec_chipdrv = { +- probe: jedec_probe, +- name: "jedec", +- module: THIS_MODULE ++ .probe = jedec_probe, ++ .name = "jedec", ++ .module = THIS_MODULE + }; + + /* Probe entry point */ +@@ -131,8 +174,7 @@ + /* Generate a part name that includes the number of different chips and + other configuration information */ + count = 1; +- strncpy(Part,map->name,sizeof(Part)-10); +- Part[sizeof(Part)-11] = 0; ++ strlcpy(Part,map->name,sizeof(Part)-10); + strcat(Part," "); + Uniq = 0; + for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) +@@ -209,8 +251,7 @@ + // printk("Part: '%s'\n",Part); + + memset(MTD,0,sizeof(*MTD)); +- // strncpy(MTD->name,Part,sizeof(MTD->name)); +- // MTD->name[sizeof(MTD->name)-1] = 0; ++ // strlcpy(MTD->name,Part,sizeof(MTD->name)); + MTD->name = map->name; + MTD->type = MTD_NORFLASH; + MTD->flags = MTD_CAP_NORFLASH; +@@ -229,7 +270,7 @@ + MTD->priv = map; + map->fldrv_priv = priv; + map->fldrv = &jedec_chipdrv; +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return MTD; + } + +@@ -351,8 +392,8 @@ + static int jedec_probe8(struct map_info *map,unsigned long base, + struct jedec_private *priv) + { +- #define flread(x) map->read8(map,base+x) +- #define flwrite(v,x) map->write8(map,v,base+x) ++ #define flread(x) map_read8(map,base+x) ++ #define flwrite(v,x) map_write8(map,v,base+x) + + const unsigned long AutoSel1 = 0xAA; + const unsigned long AutoSel2 = 0x55; +@@ -411,8 +452,8 @@ + static int jedec_probe32(struct map_info *map,unsigned long base, + struct jedec_private *priv) + { +- #define flread(x) map->read32(map,base+((x)<<2)) +- #define flwrite(v,x) map->write32(map,v,base+((x)<<2)) ++ #define flread(x) map_read32(map,base+((x)<<2)) ++ #define flwrite(v,x) map_write32(map,v,base+((x)<<2)) + + const unsigned long AutoSel1 = 0xAAAAAAAA; + const unsigned long AutoSel2 = 0x55555555; +@@ -490,7 +531,7 @@ + { + struct map_info *map = (struct map_info *)mtd->priv; + +- map->copy_from(map, buf, from, len); ++ map_copy_from(map, buf, from, len); + *retlen = len; + return 0; + } +@@ -514,7 +555,7 @@ + get = priv->bank_fill[0] - offset; + + bank /= priv->bank_fill[0]; +- map->copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); ++ map_copy_from(map,buf + *retlen,bank*my_bank_size + offset,get); + + len -= get; + *retlen += get; +@@ -545,8 +586,8 @@ + static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) + { + // Does IO to the currently selected chip +- #define flread(x) map->read8(map,chip->base+((x)<<chip->addrshift)) +- #define flwrite(v,x) map->write8(map,v,chip->base+((x)<<chip->addrshift)) ++ #define flread(x) map_read8(map,chip->base+((x)<<chip->addrshift)) ++ #define flwrite(v,x) map_write8(map,v,chip->base+((x)<<chip->addrshift)) + + unsigned long Time = 0; + unsigned long NoTime = 0; +@@ -608,7 +649,7 @@ + + /* Poll the flash for erasure completion, specs say this can take as long + as 480 seconds to do all the sectors (for a 2 meg flash). +- Erasure time is dependant on chip age, temp and wear.. */ ++ Erasure time is dependent on chip age, temp and wear.. */ + + /* This being a generic routine assumes a 32 bit bus. It does read32s + and bundles interleved chips into the same grouping. This will work +@@ -651,19 +692,19 @@ + or this is not really flash ;> */ + switch (map->buswidth) { + case 1: +- Last[0] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[1] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[2] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[0] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[1] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[2] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 2: +- Last[0] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[1] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[2] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[0] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[1] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[2] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 3: +- Last[0] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[1] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); +- Last[2] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[0] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[1] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[2] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + } + Count = 3; +@@ -699,13 +740,13 @@ + + switch (map->buswidth) { + case 1: +- Last[Count % 4] = map->read8(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[Count % 4] = map_read8(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 2: +- Last[Count % 4] = map->read16(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[Count % 4] = map_read16(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + case 4: +- Last[Count % 4] = map->read32(map,(chip->base >> chip->addrshift) + chip->start + off); ++ Last[Count % 4] = map_read32(map,(chip->base >> chip->addrshift) + chip->start + off); + break; + } + Count++; +@@ -755,10 +796,10 @@ + size_t *retlen, const u_char *buf) + { + /* Does IO to the currently selected chip. It takes the bank addressing +- base (which is divisable by the chip size) adds the necesary lower bits +- of addrshift (interleve index) and then adds the control register index. */ +- #define flread(x) map->read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) +- #define flwrite(v,x) map->write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) ++ base (which is divisible by the chip size) adds the necessary lower bits ++ of addrshift (interleave index) and then adds the control register index. */ ++ #define flread(x) map_read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) ++ #define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) + + struct map_info *map = (struct map_info *)mtd->priv; + struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; +@@ -794,7 +835,7 @@ + // Loop over this page + for (; off != (chip->size << chip->addrshift) && len != 0; start++, len--, off++,buf++) + { +- unsigned char oldbyte = map->read8(map,base+off); ++ unsigned char oldbyte = map_read8(map,base+off); + unsigned char Last[4]; + unsigned long Count = 0; + +@@ -809,10 +850,10 @@ + flwrite(0xAA,0x555); + flwrite(0x55,0x2AA); + flwrite(0xA0,0x555); +- map->write8(map,*buf,base + off); +- Last[0] = map->read8(map,base + off); +- Last[1] = map->read8(map,base + off); +- Last[2] = map->read8(map,base + off); ++ map_write8(map,*buf,base + off); ++ Last[0] = map_read8(map,base + off); ++ Last[1] = map_read8(map,base + off); ++ Last[2] = map_read8(map,base + off); + + /* Wait for the flash to finish the operation. We store the last 4 + status bytes that have been retrieved so we can determine why +@@ -820,7 +861,7 @@ + failure */ + for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && + Count < 10000; Count++) +- Last[Count % 4] = map->read8(map,base + off); ++ Last[Count % 4] = map_read8(map,base + off); + if (Last[(Count - 1) % 4] != *buf) + { + jedec_flash_failed(Last[(Count - 3) % 4]); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/jedec_probe.c linux/drivers/mtd/chips/jedec_probe.c +--- linux-mips-2.4.27/drivers/mtd/chips/jedec_probe.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/jedec_probe.c 2004-11-19 10:25:11.774218792 +0100 +@@ -1,9 +1,11 @@ + /* + Common Flash Interface probe code. + (C) 2000 Red Hat. GPL'd. +- $Id: jedec_probe.c,v 1.19 2002/11/12 13:12:10 dwmw2 Exp $ ++ $Id: jedec_probe.c,v 1.44 2003/11/17 15:57:35 thayne Exp $ + See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) + for the standard this probe goes back to. ++ ++ Occasionally maintained by Thayne Harbaugh tharbaugh at lnxi dot com + */ + + #include <linux/config.h> +@@ -15,7 +17,9 @@ + #include <linux/errno.h> + #include <linux/slab.h> + #include <linux/interrupt.h> ++#include <linux/init.h> + ++#include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> + #include <linux/mtd/cfi.h> + #include <linux/mtd/gen_probe.h> +@@ -26,20 +30,24 @@ + #define MANUFACTURER_FUJITSU 0x0004 + #define MANUFACTURER_INTEL 0x0089 + #define MANUFACTURER_MACRONIX 0x00C2 +-#define MANUFACTURER_ST 0x0020 ++#define MANUFACTURER_PMC 0x009D + #define MANUFACTURER_SST 0x00BF ++#define MANUFACTURER_ST 0x0020 + #define MANUFACTURER_TOSHIBA 0x0098 ++#define MANUFACTURER_WINBOND 0x00da + + + /* AMD */ + #define AM29F800BB 0x2258 + #define AM29F800BT 0x22D6 ++#define AM29LV400BB 0x22BA ++#define AM29LV400BT 0x22B9 + #define AM29LV800BB 0x225B + #define AM29LV800BT 0x22DA + #define AM29LV160DT 0x22C4 + #define AM29LV160DB 0x2249 + #define AM29F017D 0x003D +-#define AM29F016 0x00AD ++#define AM29F016D 0x00AD + #define AM29F080 0x00D5 + #define AM29F040 0x00A4 + #define AM29LV040B 0x004F +@@ -54,6 +62,7 @@ + #define AT49BV32XT 0x00C9 + + /* Fujitsu */ ++#define MBM29F040C 0x00A4 + #define MBM29LV650UE 0x22D7 + #define MBM29LV320TE 0x22F6 + #define MBM29LV320BE 0x22F9 +@@ -61,6 +70,9 @@ + #define MBM29LV160BE 0x2249 + #define MBM29LV800BA 0x225B + #define MBM29LV800TA 0x22DA ++#define MBM29LV400TC 0x22B9 ++#define MBM29LV400BC 0x22BA ++ + + /* Intel */ + #define I28F004B3T 0x00d4 +@@ -93,8 +105,14 @@ + #define MX29F004T 0x0045 + #define MX29F004B 0x0046 + ++/* PMC */ ++#define PM49FL002 0x006D ++#define PM49FL004 0x006E ++#define PM49FL008 0x006A ++ + /* ST - www.st.com */ +-#define M29W800T 0x00D7 ++#define M29W800DT 0x00D7 ++#define M29W800DB 0x005B + #define M29W160DT 0x22C4 + #define M29W160DB 0x2249 + #define M29W040B 0x00E3 +@@ -110,6 +128,7 @@ + #define SST39LF040 0x00D7 + #define SST39SF010A 0x00B5 + #define SST39SF020A 0x00B6 ++#define SST49LF004B 0x0060 + #define SST49LF030A 0x001C + #define SST49LF040A 0x0051 + #define SST49LF080A 0x005B +@@ -122,15 +141,87 @@ + #define TC58FVT641 0x0093 + #define TC58FVB641 0x0095 + ++/* Winbond */ ++#define W49V002A 0x00b0 ++ ++ ++/* ++ * Unlock address sets for AMD command sets. ++ * Intel command sets use the MTD_UADDR_UNNECESSARY. ++ * Each identifier, except MTD_UADDR_UNNECESSARY, and ++ * MTD_UADDR_NO_SUPPORT must be defined below in unlock_addrs[]. ++ * MTD_UADDR_NOT_SUPPORTED must be 0 so that structure ++ * initialization need not require initializing all of the ++ * unlock addresses for all bit widths. ++ */ ++enum uaddr { ++ MTD_UADDR_NOT_SUPPORTED = 0, /* data width not supported */ ++ MTD_UADDR_0x0555_0x02AA, ++ MTD_UADDR_0x0555_0x0AAA, ++ MTD_UADDR_0x5555_0x2AAA, ++ MTD_UADDR_0x0AAA_0x0555, ++ MTD_UADDR_DONT_CARE, /* Requires an arbitrary address */ ++ MTD_UADDR_UNNECESSARY, /* Does not require any address */ ++}; ++ ++ ++struct unlock_addr { ++ int addr1; ++ int addr2; ++}; ++ ++ ++/* ++ * I don't like the fact that the first entry in unlock_addrs[] ++ * exists, but is for MTD_UADDR_NOT_SUPPORTED - and, therefore, ++ * should not be used. The problem is that structures with ++ * initializers have extra fields initialized to 0. It is _very_ ++ * desireable to have the unlock address entries for unsupported ++ * data widths automatically initialized - that means that ++ * MTD_UADDR_NOT_SUPPORTED must be 0 and the first entry here ++ * must go unused. ++ */ ++static const struct unlock_addr unlock_addrs[] = { ++ [MTD_UADDR_NOT_SUPPORTED] = { ++ .addr1 = 0xffff, ++ .addr2 = 0xffff ++ }, ++ ++ [MTD_UADDR_0x0555_0x02AA] = { ++ .addr1 = 0x0555, ++ .addr2 = 0x02aa ++ }, ++ ++ [MTD_UADDR_0x0555_0x0AAA] = { ++ .addr1 = 0x0555, ++ .addr2 = 0x0aaa ++ }, ++ ++ [MTD_UADDR_0x5555_0x2AAA] = { ++ .addr1 = 0x5555, ++ .addr2 = 0x2aaa ++ }, ++ ++ [MTD_UADDR_0x0AAA_0x0555] = { ++ .addr1 = 0x0AAA, ++ .addr2 = 0x0555 ++ }, ++ ++ [MTD_UADDR_DONT_CARE] = { ++ .addr1 = 0x0000, /* Doesn't matter which address */ ++ .addr2 = 0x0000 /* is used - must be last entry */ ++ } ++}; ++ + + struct amd_flash_info { + const __u16 mfr_id; + const __u16 dev_id; + const char *name; + const int DevSize; +- const int InterfaceDesc; + const int NumEraseRegions; + const int CmdSet; ++ const __u8 uaddr[4]; /* unlock addrs for 8, 16, 32, 64 */ + const ulong regions[4]; + }; + +@@ -145,760 +236,1214 @@ + #define SIZE_4MiB 22 + #define SIZE_8MiB 23 + ++ ++/* ++ * Please keep this list ordered by manufacturer! ++ * Fortunately, the list isn't searched often and so a ++ * slow, linear search isn't so bad. ++ */ + static const struct amd_flash_info jedec_table[] = { + { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F032B, +- name: "AMD AM29F032B", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,64) +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DT, +- name: "AMD AM29LV160DT", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F032B, ++ .name = "AMD AM29F032B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,64) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DT, ++ .name = "AMD AM29LV160DT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV160DB, +- name: "AMD AM29LV160DB", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV160DB, ++ .name = "AMD AM29LV160DB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT160, +- name: "Toshiba TC58FVT160", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV400BB, ++ .name = "AMD AM29LV400BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,7) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV400BT, ++ .name = "AMD AM29LV400BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,7), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB160, +- name: "Toshiba TC58FVB160", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BB, ++ .name = "AMD AM29LV800BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,31) ++ ERASEINFO(0x10000,15), + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB321, +- name: "Toshiba TC58FVB321", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,63) ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BB, ++ .name = "AMD AM29F800BB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,15), + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT321, +- name: "Toshiba TC58FVT321", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,63), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV800BT, ++ .name = "AMD AM29LV800BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVB641, +- name: "Toshiba TC58FVB641", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,127) ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F800BT, ++ .name = "AMD AM29F800BT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_TOSHIBA, +- dev_id: TC58FVT641, +- name: "Toshiba TC58FVT641", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,127), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F017D, ++ .name = "AMD AM29F017D", ++ .uaddr = { ++ [0] = MTD_UADDR_DONT_CARE /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F016D, ++ .name = "AMD AM29F016D", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F080, ++ .name = "AMD AM29F080", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29F040, ++ .name = "AMD AM29F040", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_AMD, ++ .dev_id = AM29LV040B, ++ .name = "AMD AM29LV040B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV512, ++ .name = "Atmel AT49BV512", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_64KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,1) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT29LV512, ++ .name = "Atmel AT29LV512", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_64KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x80,256), ++ ERASEINFO(0x80,256) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV16X, ++ .name = "Atmel AT49BV16X", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV650UE, +- name: "Fujitsu MBM29LV650UE", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,128) +- } +- }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV320TE, +- name: "Fujitsu MBM29LV320TE", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,63), ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV16XT, ++ .name = "Atmel AT49BV16XT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV320BE, +- name: "Fujitsu MBM29LV320BE", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV32X, ++ .name = "Atmel AT49BV32X", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), + ERASEINFO(0x10000,63) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160TE, +- name: "Fujitsu MBM29LV160TE", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1) ++ .mfr_id = MANUFACTURER_ATMEL, ++ .dev_id = AT49BV32XT, ++ .name = "Atmel AT49BV32XT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x0AAA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x0AAA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,63), ++ ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV160BE, +- name: "Fujitsu MBM29LV160BE", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,31) ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29F040C, ++ .name = "Fujitsu MBM29F040C", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV650UE, ++ .name = "Fujitsu MBM29LV650UE", ++ .uaddr = { ++ [0] = MTD_UADDR_DONT_CARE /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,128) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV320TE, ++ .name = "Fujitsu MBM29LV320TE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,63), ++ ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV800BA, +- name: "Fujitsu MBM29LV800BA", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,15) ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV320BE, ++ .name = "Fujitsu MBM29LV320BE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,63) + } + }, { +- mfr_id: MANUFACTURER_FUJITSU, +- dev_id: MBM29LV800TA, +- name: "Fujitsu MBM29LV800TA", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160TE, ++ .name = "Fujitsu MBM29LV160TE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV160BE, ++ .name = "Fujitsu MBM29LV160BE", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,15), ++ ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BB, +- name: "AMD AM29F800BB", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV800BA, ++ .name = "Fujitsu MBM29LV800BA", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,15), ++ ERASEINFO(0x10000,15) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BT, +- name: "AMD AM29LV800BT", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV800TA, ++ .name = "Fujitsu MBM29LV800TA", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F800BT, +- name: "AMD AM29F800BT", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), +- ERASEINFO(0x08000,1), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV400BC, ++ .name = "Fujitsu MBM29LV400BC", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1) ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,7) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV800BB, +- name: "AMD AM29LV800BB", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_FUJITSU, ++ .dev_id = MBM29LV400TC, ++ .name = "Fujitsu MBM29LV400TC", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,7), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F004B3B, +- name: "Intel 28F004B3B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F004B3B, ++ .name = "Intel 28F004B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 7), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F004B3T, +- name: "Intel 28F004B3T", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F004B3T, ++ .name = "Intel 28F004B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 7), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F400B3B, +- name: "Intel 28F400B3B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F400B3B, ++ .name = "Intel 28F400B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 7), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F400B3T, +- name: "Intel 28F400B3T", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F400B3T, ++ .name = "Intel 28F400B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 7), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008B3B, +- name: "Intel 28F008B3B", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008B3B, ++ .name = "Intel 28F008B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 15), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008B3T, +- name: "Intel 28F008B3T", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008B3T, ++ .name = "Intel 28F008B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008S5, +- name: "Intel 28F008S5", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,16), +- } +- }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016S5, +- name: "Intel 28F016S5", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), +- } +- }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F008SA, +- name: "Intel 28F008SA", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 1, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008S5, ++ .name = "Intel 28F008S5", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016S5, ++ .name = "Intel 28F016S5", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F008SA, ++ .name = "Intel 28F008SA", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 1, ++ .regions = { + ERASEINFO(0x10000, 16), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F800B3B, +- name: "Intel 28F800B3B", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F800B3B, ++ .name = "Intel 28F800B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 15), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F800B3T, +- name: "Intel 28F800B3T", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F800B3T, ++ .name = "Intel 28F800B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 15), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016B3B, +- name: "Intel 28F016B3B", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016B3B, ++ .name = "Intel 28F016B3B", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 31), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016S3, +- name: "Intel I28F016S3", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 1, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016S3, ++ .name = "Intel I28F016S3", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 1, ++ .regions = { + ERASEINFO(0x10000, 32), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F016B3T, +- name: "Intel 28F016B3T", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F016B3T, ++ .name = "Intel 28F016B3T", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 31), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F160B3B, +- name: "Intel 28F160B3B", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F160B3B, ++ .name = "Intel 28F160B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 31), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F160B3T, +- name: "Intel 28F160B3T", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F160B3T, ++ .name = "Intel 28F160B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 31), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F320B3B, +- name: "Intel 28F320B3B", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F320B3B, ++ .name = "Intel 28F320B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 63), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F320B3T, +- name: "Intel 28F320B3T", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F320B3T, ++ .name = "Intel 28F320B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 63), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F640B3B, +- name: "Intel 28F640B3B", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F640B3B, ++ .name = "Intel 28F640B3B", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x02000, 8), + ERASEINFO(0x10000, 127), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I28F640B3T, +- name: "Intel 28F640B3T", +- DevSize: SIZE_8MiB, +- CmdSet: P_ID_INTEL_STD, +- NumEraseRegions: 2, +- regions: { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I28F640B3T, ++ .name = "Intel 28F640B3T", ++ .uaddr = { ++ [1] = MTD_UADDR_UNNECESSARY, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_INTEL_STD, ++ .NumEraseRegions= 2, ++ .regions = { + ERASEINFO(0x10000, 127), + ERASEINFO(0x02000, 8), + } + }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I82802AB, +- name: "Intel 82802AB", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), +- } +- }, { +- mfr_id: MANUFACTURER_INTEL, +- dev_id: I82802AC, +- name: "Intel 82802AC", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_INTEL_EXT, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,16), +- } +- }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W800T, +- name: "ST M29W800T", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,15), ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I82802AB, ++ .name = "Intel 82802AB", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_INTEL, ++ .dev_id = I82802AC, ++ .name = "Intel 82802AC", ++ .uaddr = { ++ [0] = MTD_UADDR_UNNECESSARY, /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_INTEL_EXT, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,16), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29LV160T, ++ .name = "MXIC MX29LV160T", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DT, +- name: "ST M29W160DT", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), +- ERASEINFO(0x08000,1), +- ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1) +- } +- }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W160DB, +- name: "ST M29W160DB", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29LV160B, ++ .name = "MXIC MX29LV160B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV512, +- name: "Atmel AT49BV512", +- DevSize: SIZE_64KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,1) +- } +- }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT29LV512, +- name: "Atmel AT29LV512", +- DevSize: SIZE_64KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: { +- ERASEINFO(0x80,256), +- ERASEINFO(0x80,256) +- } +- }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV16X, +- name: "Atmel AT49BV16X", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,31) ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F016, ++ .name = "Macronix MX29F016", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F004T, ++ .name = "Macronix MX29F004T", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,7), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1), + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV16XT, +- name: "Atmel AT49BV16XT", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,31), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_MACRONIX, ++ .dev_id = MX29F004B, ++ .name = "Macronix MX29F004B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,7), + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV32X, +- name: "Atmel AT49BV32X", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x02000,8), +- ERASEINFO(0x10000,63) ++ .mfr_id = MANUFACTURER_PMC, ++ .dev_id = PM49FL002, ++ .name = "PMC Pm49FL002", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO( 0x01000, 64 ) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_PMC, ++ .dev_id = PM49FL004, ++ .name = "PMC Pm49FL004", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO( 0x01000, 128 ) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_PMC, ++ .dev_id = PM49FL008, ++ .name = "PMC Pm49FL008", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO( 0x01000, 256 ) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF512, ++ .name = "SST 39LF512", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_64KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,16), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF010, ++ .name = "SST 39LF010", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_128KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF020, ++ .name = "SST 39LF020", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,64), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39LF040, ++ .name = "SST 39LF040", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,128), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39SF010A, ++ .name = "SST 39SF010A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_128KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,32), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST39SF020A, ++ .name = "SST 39SF020A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,64), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF004B, ++ .name = "SST 49LF004B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,128), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF030A, ++ .name = "SST 49LF030A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,96), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF040A, ++ .name = "SST 49LF040A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,128), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_SST, ++ .dev_id = SST49LF080A, ++ .name = "SST 49LF080A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x01000,256), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W800DT, ++ .name = "ST M29W800DT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ ++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,15), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_ATMEL, +- dev_id: AT49BV32XT, +- name: "Atmel AT49BV32XT", +- DevSize: SIZE_4MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 2, +- regions: {ERASEINFO(0x10000,63), +- ERASEINFO(0x02000,8) ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W800DB, ++ .name = "ST M29W800DB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA, /* x8 */ ++ [1] = MTD_UADDR_0x5555_0x2AAA /* x16 */ ++ }, ++ .DevSize = SIZE_1MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), ++ ERASEINFO(0x02000,2), ++ ERASEINFO(0x08000,1), ++ ERASEINFO(0x10000,15) + } + }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F017D, +- name: "AMD AM29F017D", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F016, +- name: "AMD AM29F016", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F080, +- name: "AMD AM29F080", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,16), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29F040, +- name: "AMD AM29F040", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), +- } +- }, { +- mfr_id: MANUFACTURER_AMD, +- dev_id: AM29LV040B, +- name: "AMD AM29LV040B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), +- } +- }, { +- mfr_id: MANUFACTURER_ST, +- dev_id: M29W040B, +- name: "ST M29W040B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,8), +- } +- }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29LV160T, +- name: "MXIC MX29LV160T", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,31), ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W160DT, ++ .name = "ST M29W160DT", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29LV160B, +- name: "MXIC MX29LV160B", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_ST, /* FIXME - CFI device? */ ++ .dev_id = M29W160DB, ++ .name = "ST M29W160DB", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), + ERASEINFO(0x10000,31) + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29F016, +- name: "Macronix MX29F016", +- DevSize: SIZE_2MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x10000,32), +- } +- }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29F004T, +- name: "Macronix MX29F004T", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x10000,7), ++ .mfr_id = MANUFACTURER_ST, ++ .dev_id = M29W040B, ++ .name = "ST M29W040B", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0555_0x02AA /* x8 */ ++ }, ++ .DevSize = SIZE_512KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 1, ++ .regions = { ++ ERASEINFO(0x10000,8), ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT160, ++ .name = "Toshiba TC58FVT160", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000,31), + ERASEINFO(0x08000,1), + ERASEINFO(0x02000,2), +- ERASEINFO(0x04000,1), ++ ERASEINFO(0x04000,1) + } + }, { +- mfr_id: MANUFACTURER_MACRONIX, +- dev_id: MX29F004B, +- name: "Macronix MX29F004B", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 4, +- regions: {ERASEINFO(0x04000,1), ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB160, ++ .name = "Toshiba TC58FVB160", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_2MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x04000,1), + ERASEINFO(0x02000,2), + ERASEINFO(0x08000,1), +- ERASEINFO(0x10000,7), ++ ERASEINFO(0x10000,31) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB321, ++ .name = "Toshiba TC58FVB321", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,63) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT321, ++ .name = "Toshiba TC58FVT321", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA /* x16 */ ++ }, ++ .DevSize = SIZE_4MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,63), ++ ERASEINFO(0x02000,8) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVB641, ++ .name = "Toshiba TC58FVB641", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x02000,8), ++ ERASEINFO(0x10000,127) ++ } ++ }, { ++ .mfr_id = MANUFACTURER_TOSHIBA, ++ .dev_id = TC58FVT641, ++ .name = "Toshiba TC58FVT641", ++ .uaddr = { ++ [0] = MTD_UADDR_0x0AAA_0x0555, /* x8 */ ++ [1] = MTD_UADDR_0x0555_0x02AA, /* x16 */ ++ }, ++ .DevSize = SIZE_8MiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 2, ++ .regions = { ++ ERASEINFO(0x10000,127), ++ ERASEINFO(0x02000,8) + } + }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF512, +- name: "SST 39LF512", +- DevSize: SIZE_64KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,16), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF010, +- name: "SST 39LF010", +- DevSize: SIZE_128KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,32), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF020, +- name: "SST 39LF020", +- DevSize: SIZE_256KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,64), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39LF040, +- name: "SST 39LF040", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,128), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39SF010A, +- name: "SST 39SF010A", +- DevSize: SIZE_128KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,32), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST39SF020A, +- name: "SST 39SF020A", +- DevSize: SIZE_256KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,64), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST49LF030A, +- name: "SST 49LF030A", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,96), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST49LF040A, +- name: "SST 49LF040A", +- DevSize: SIZE_512KiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,128), +- } +- }, { +- mfr_id: MANUFACTURER_SST, +- dev_id: SST49LF080A, +- name: "SST 49LF080A", +- DevSize: SIZE_1MiB, +- CmdSet: P_ID_AMD_STD, +- NumEraseRegions: 1, +- regions: {ERASEINFO(0x01000,256), ++ .mfr_id = MANUFACTURER_WINBOND, ++ .dev_id = W49V002A, ++ .name = "Winbond W49V002A", ++ .uaddr = { ++ [0] = MTD_UADDR_0x5555_0x2AAA /* x8 */ ++ }, ++ .DevSize = SIZE_256KiB, ++ .CmdSet = P_ID_AMD_STD, ++ .NumEraseRegions= 4, ++ .regions = { ++ ERASEINFO(0x10000, 3), ++ ERASEINFO(0x08000, 1), ++ ERASEINFO(0x02000, 2), ++ ERASEINFO(0x04000, 1), + } + } + }; +@@ -907,7 +1452,7 @@ + static int cfi_jedec_setup(struct cfi_private *p_cfi, int index); + + static int jedec_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi); ++ unsigned long *chip_map, struct cfi_private *cfi); + + struct mtd_info *jedec_probe(struct map_info *map); + +@@ -944,11 +1489,43 @@ + * this should be safe. + */ + cfi_send_gen_cmd(0xFF, 0, base, map, cfi, cfi->device_type, NULL); ++ /* FIXME - should have reset delay before continuing */ ++} + ++ ++static inline __u8 finfo_uaddr(const struct amd_flash_info *finfo, int device_type) ++{ ++ int uaddr_idx; ++ __u8 uaddr = MTD_UADDR_NOT_SUPPORTED; ++ ++ switch ( device_type ) { ++ case CFI_DEVICETYPE_X8: uaddr_idx = 0; break; ++ case CFI_DEVICETYPE_X16: uaddr_idx = 1; break; ++ case CFI_DEVICETYPE_X32: uaddr_idx = 2; break; ++ default: ++ printk(KERN_NOTICE "MTD: %s(): unknown device_type %d\n", ++ __func__, device_type); ++ goto uaddr_done; ++ } ++ ++ uaddr = finfo->uaddr[uaddr_idx]; ++ ++ if (uaddr != MTD_UADDR_NOT_SUPPORTED ) { ++ /* ASSERT("The unlock addresses for non-8-bit mode ++ are bollocks. We don't really need an array."); */ ++ uaddr = finfo->uaddr[0]; ++ } ++ ++ uaddr_done: ++ return uaddr; + } ++ ++ + static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) + { + int i,num_erase_regions; ++ unsigned long mask; ++ __u8 uaddr; + + printk("Found: %s\n",jedec_table[index].name); + +@@ -971,41 +1548,170 @@ + p_cfi->cfiq->EraseRegionInfo[i] = jedec_table[index].regions[i]; + } + p_cfi->cmdset_priv = 0; ++ ++ /* This may be redundant for some cases, but it doesn't hurt */ ++ p_cfi->mfr = jedec_table[index].mfr_id; ++ p_cfi->id = jedec_table[index].dev_id; ++ ++ uaddr = finfo_uaddr(&jedec_table[index], p_cfi->device_type); ++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { ++ kfree( p_cfi->cfiq ); ++ return 0; ++ } ++ ++ /* Mask out address bits which are smaller than the device type */ ++ mask = ~(p_cfi->device_type-1); ++ p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1 & mask; ++ p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2 & mask; ++ + return 1; /* ok */ + } + +-static int jedec_probe_chip(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi) ++ ++/* ++ * There is a BIG problem properly ID'ing the JEDEC devic and guaranteeing ++ * the mapped address, unlock addresses, and proper chip ID. This function ++ * attempts to minimize errors. It is doubtfull that this probe will ever ++ * be perfect - consequently there should be some module parameters that ++ * could be manually specified to force the chip info. ++ */ ++static inline int jedec_match( __u32 base, ++ struct map_info *map, ++ struct cfi_private *cfi, ++ const struct amd_flash_info *finfo ) + { +- int i; +- int unlockpass = 0; ++ int rc = 0; /* failure until all tests pass */ ++ u32 mfr, id; ++ __u8 uaddr; ++ unsigned long mask; + +- if (!cfi->numchips) { ++ /* ++ * The IDs must match. For X16 and X32 devices operating in ++ * a lower width ( X8 or X16 ), the device ID's are usually just ++ * the lower byte(s) of the larger device ID for wider mode. If ++ * a part is found that doesn't fit this assumption (device id for ++ * smaller width mode is completely unrealated to full-width mode) ++ * then the jedec_table[] will have to be augmented with the IDs ++ * for different widths. ++ */ + switch (cfi->device_type) { + case CFI_DEVICETYPE_X8: +- cfi->addr_unlock1 = 0x555; +- cfi->addr_unlock2 = 0x2aa; ++ mfr = (__u8)finfo->mfr_id; ++ id = (__u8)finfo->dev_id; + break; + case CFI_DEVICETYPE_X16: +- cfi->addr_unlock1 = 0xaaa; +- if (map->buswidth == cfi->interleave) { +- /* X16 chip(s) in X8 mode */ +- cfi->addr_unlock2 = 0x555; +- } else { +- cfi->addr_unlock2 = 0x554; +- } ++ mfr = (__u16)finfo->mfr_id; ++ id = (__u16)finfo->dev_id; + break; + case CFI_DEVICETYPE_X32: +- cfi->addr_unlock1 = 0x1555; +- cfi->addr_unlock2 = 0xaaa; ++ mfr = (__u16)finfo->mfr_id; ++ id = (__u32)finfo->dev_id; + break; + default: +- printk(KERN_NOTICE "Eep. Unknown jedec_probe device type %d\n", cfi->device_type); +- return 0; ++ printk(KERN_WARNING ++ "MTD %s(): Unsupported device type %d\n", ++ __func__, cfi->device_type); ++ goto match_done; ++ } ++ if ( cfi->mfr != mfr || cfi->id != id ) { ++ goto match_done; ++ } ++ ++ /* the part size must fit in the memory window */ ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): Check fit 0x%.8x + 0x%.8x = 0x%.8x\n", ++ __func__, base, 1 << finfo->DevSize, base + (1 << finfo->DevSize) ); ++ if ( base + cfi->interleave * ( 1 << finfo->DevSize ) > map->size ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): 0x%.4x 0x%.4x %dKiB doesn't fit\n", ++ __func__, finfo->mfr_id, finfo->dev_id, ++ 1 << finfo->DevSize ); ++ goto match_done; ++ } ++ ++ uaddr = finfo_uaddr(finfo, cfi->device_type); ++ if ( uaddr == MTD_UADDR_NOT_SUPPORTED ) { ++ goto match_done; ++ } ++ ++ mask = ~(cfi->device_type-1); ++ ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): check unlock addrs 0x%.4x 0x%.4x\n", ++ __func__, cfi->addr_unlock1, cfi->addr_unlock2 ); ++ if ( MTD_UADDR_UNNECESSARY != uaddr && MTD_UADDR_DONT_CARE != uaddr ++ && ( (unlock_addrs[uaddr].addr1 & mask) != cfi->addr_unlock1 || ++ (unlock_addrs[uaddr].addr2 & mask) != cfi->addr_unlock2 ) ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): 0x%.4x 0x%.4x did not match\n", ++ __func__, ++ unlock_addrs[uaddr].addr1 & mask, ++ unlock_addrs[uaddr].addr2 & mask); ++ goto match_done; + } ++ ++ /* ++ * Make sure the ID's dissappear when the device is taken out of ++ * ID mode. The only time this should fail when it should succeed ++ * is when the ID's are written as data to the same ++ * addresses. For this rare and unfortunate case the chip ++ * cannot be probed correctly. ++ * FIXME - write a driver that takes all of the chip info as ++ * module parameters, doesn't probe but forces a load. ++ */ ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): check ID's disappear when not in ID mode\n", ++ __func__ ); ++ jedec_reset( base, map, cfi ); ++ mfr = jedec_read_mfr( map, base, cfi ); ++ id = jedec_read_id( map, base, cfi ); ++ if ( mfr == cfi->mfr && id == cfi->id ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): ID 0x%.2x:0x%.2x did not change after reset:\n" ++ "You might need to manually specify JEDEC parameters.\n", ++ __func__, cfi->mfr, cfi->id ); ++ goto match_done; ++ } ++ ++ /* all tests passed - mark as success */ ++ rc = 1; ++ ++ /* ++ * Put the device back in ID mode - only need to do this if we ++ * were truly frobbing a real device. ++ */ ++ DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): return to ID mode\n", __func__ ); ++ if(cfi->addr_unlock1) { ++ cfi_send_gen_cmd(0xaa, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + } ++ cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ /* FIXME - should have a delay before continuing */ ++ ++ match_done: ++ return rc; ++} ++ ++ ++static int jedec_probe_chip(struct map_info *map, __u32 base, ++ unsigned long *chip_map, struct cfi_private *cfi) ++{ ++ int i; ++ enum uaddr uaddr_idx = MTD_UADDR_NOT_SUPPORTED; + + retry: ++ if (!cfi->numchips) { ++ unsigned long mask = ~(cfi->device_type-1); ++ ++ uaddr_idx++; ++ ++ if (MTD_UADDR_UNNECESSARY == uaddr_idx) ++ return 0; ++ ++ /* Mask out address bits which are smaller than the device type */ ++ cfi->addr_unlock1 = unlock_addrs[uaddr_idx].addr1 & mask; ++ cfi->addr_unlock2 = unlock_addrs[uaddr_idx].addr2 & mask; ++ } ++ + /* Make certain we aren't probing past the end of map */ + if (base >= map->size) { + printk(KERN_NOTICE +@@ -1038,6 +1744,7 @@ + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, base, map, cfi, CFI_DEVICETYPE_X8, NULL); + } + cfi_send_gen_cmd(0x90, cfi->addr_unlock1, base, map, cfi, CFI_DEVICETYPE_X8, NULL); ++ /* FIXME - should have a delay before continuing */ + + if (!cfi->numchips) { + /* This is the first time we're called. Set up the CFI +@@ -1045,26 +1752,21 @@ + + cfi->mfr = jedec_read_mfr(map, base, cfi); + cfi->id = jedec_read_id(map, base, cfi); +- printk(KERN_INFO "Search for id:(%02x %02x) interleave(%d) type(%d)\n", ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "Search for id:(%02x %02x) interleave(%d) type(%d)\n", + cfi->mfr, cfi->id, cfi->interleave, cfi->device_type); + for (i=0; i<sizeof(jedec_table)/sizeof(jedec_table[0]); i++) { +- if (cfi->mfr == jedec_table[i].mfr_id && +- cfi->id == jedec_table[i].dev_id) { ++ if ( jedec_match( base, map, cfi, &jedec_table[i] ) ) { ++ DEBUG( MTD_DEBUG_LEVEL3, ++ "MTD %s(): matched device 0x%x,0x%x unlock_addrs: 0x%.4x 0x%.4x\n", ++ __func__, cfi->mfr, cfi->id, ++ cfi->addr_unlock1, cfi->addr_unlock2 ); + if (!cfi_jedec_setup(cfi, i)) + return 0; + goto ok_out; + } + } +- switch(unlockpass++) { +- case 0: +- cfi->addr_unlock1 |= cfi->addr_unlock1 << 4; +- cfi->addr_unlock2 |= cfi->addr_unlock2 << 4; +- goto retry; +- case 1: +- cfi->addr_unlock1 = cfi->addr_unlock2 = 0; + goto retry; +- } +- return 0; + } else { + __u16 mfr; + __u16 id; +@@ -1081,21 +1783,24 @@ + } + } + +- /* Check each previous chip to see if it's an alias */ +- for (i=0; i<cfi->numchips; i++) { +- /* This chip should be in read mode if it's one +- we've already touched. */ +- if (jedec_read_mfr(map, chips[i].start, cfi) == cfi->mfr && +- jedec_read_id(map, chips[i].start, cfi) == cfi->id) { ++ /* Check each previous chip locations to see if it's an alias */ ++ for (i=0; i < (base >> cfi->chipshift); i++) { ++ unsigned long start; ++ if(!test_bit(i, chip_map)) { ++ continue; /* Skip location; no valid chip at this address */ ++ } ++ start = i << cfi->chipshift; ++ if (jedec_read_mfr(map, start, cfi) == cfi->mfr && ++ jedec_read_id(map, start, cfi) == cfi->id) { + /* Eep. This chip also looks like it's in autoselect mode. + Is it an alias for the new one? */ +- jedec_reset(chips[i].start, map, cfi); ++ jedec_reset(start, map, cfi); + + /* If the device IDs go away, it's an alias */ + if (jedec_read_mfr(map, base, cfi) != cfi->mfr || + jedec_read_id(map, base, cfi) != cfi->id) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + +@@ -1107,7 +1812,7 @@ + if (jedec_read_mfr(map, base, cfi) == cfi->mfr && + jedec_read_id(map, base, cfi) == cfi->id) { + printk(KERN_DEBUG "%s: Found an alias at 0x%x for the chip at 0x%lx\n", +- map->name, base, chips[i].start); ++ map->name, base, start); + return 0; + } + } +@@ -1115,13 +1820,7 @@ + + /* OK, if we got to here, then none of the previous chips appear to + be aliases for the current one. */ +- if (cfi->numchips == MAX_CFI_CHIPS) { +- printk(KERN_WARNING"%s: Too many flash chips detected. Increase MAX_CFI_CHIPS from %d.\n", map->name, MAX_CFI_CHIPS); +- /* Doesn't matter about resetting it to Read Mode - we're not going to talk to it anyway */ +- return -1; +- } +- chips[cfi->numchips].start = base; +- chips[cfi->numchips].state = FL_READY; ++ set_bit((base >> cfi->chipshift), chip_map); /* Update chip map */ + cfi->numchips++; + + ok_out: +@@ -1136,8 +1835,8 @@ + } + + static struct chip_probe jedec_chip_probe = { +- name: "JEDEC", +- probe_chip: jedec_probe_chip ++ .name = "JEDEC", ++ .probe_chip = jedec_probe_chip + }; + + struct mtd_info *jedec_probe(struct map_info *map) +@@ -1150,9 +1849,9 @@ + } + + static struct mtd_chip_driver jedec_chipdrv = { +- probe: jedec_probe, +- name: "jedec_probe", +- module: THIS_MODULE ++ .probe = jedec_probe, ++ .name = "jedec_probe", ++ .module = THIS_MODULE + }; + + int __init jedec_probe_init(void) +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/map_absent.c linux/drivers/mtd/chips/map_absent.c +--- linux-mips-2.4.27/drivers/mtd/chips/map_absent.c 2001-11-05 21:15:51.000000000 +0100 ++++ linux/drivers/mtd/chips/map_absent.c 2004-11-19 10:25:11.776218488 +0100 +@@ -1,7 +1,7 @@ + /* + * Common code to handle absent "placeholder" devices + * Copyright 2001 Resilience Corporation <ebrower@resilience.com> +- * $Id: map_absent.c,v 1.2 2001/10/02 15:05:12 dwmw2 Exp $ ++ * $Id: map_absent.c,v 1.4 2003/05/28 12:51:49 dwmw2 Exp $ + * + * This map driver is used to allocate "placeholder" MTD + * devices on systems that have socketed/removable media. +@@ -23,9 +23,10 @@ + #include <linux/kernel.h> + #include <linux/errno.h> + #include <linux/slab.h> +- ++#include <linux/init.h> ++#include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +- ++#include <linux/mtd/compatmac.h> + + static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +@@ -36,10 +37,10 @@ + + + static struct mtd_chip_driver map_absent_chipdrv = { +- probe: map_absent_probe, +- destroy: map_absent_destroy, +- name: "map_absent", +- module: THIS_MODULE ++ .probe = map_absent_probe, ++ .destroy = map_absent_destroy, ++ .name = "map_absent", ++ .module = THIS_MODULE + }; + + static struct mtd_info *map_absent_probe(struct map_info *map) +@@ -65,7 +66,7 @@ + mtd->flags = 0; + mtd->erasesize = PAGE_SIZE; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/map_ram.c linux/drivers/mtd/chips/map_ram.c +--- linux-mips-2.4.27/drivers/mtd/chips/map_ram.c 2001-11-05 21:15:51.000000000 +0100 ++++ linux/drivers/mtd/chips/map_ram.c 2004-11-19 10:25:11.777218336 +0100 +@@ -1,7 +1,7 @@ + /* + * Common code to handle map devices which are simple RAM + * (C) 2000 Red Hat. GPL'd. +- * $Id: map_ram.c,v 1.14 2001/10/02 15:05:12 dwmw2 Exp $ ++ * $Id: map_ram.c,v 1.17 2003/05/28 12:51:49 dwmw2 Exp $ + */ + + #include <linux/module.h> +@@ -11,8 +11,10 @@ + #include <asm/byteorder.h> + #include <linux/errno.h> + #include <linux/slab.h> +- ++#include <linux/init.h> ++#include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> ++#include <linux/mtd/compatmac.h> + + + static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); +@@ -23,9 +25,9 @@ + + + static struct mtd_chip_driver mapram_chipdrv = { +- probe: map_ram_probe, +- name: "map_ram", +- module: THIS_MODULE ++ .probe = map_ram_probe, ++ .name = "map_ram", ++ .module = THIS_MODULE + }; + + static struct mtd_info *map_ram_probe(struct map_info *map) +@@ -34,21 +36,21 @@ + + /* Check the first byte is RAM */ + #if 0 +- map->write8(map, 0x55, 0); +- if (map->read8(map, 0) != 0x55) ++ map_write8(map, 0x55, 0); ++ if (map_read8(map, 0) != 0x55) + return NULL; + +- map->write8(map, 0xAA, 0); +- if (map->read8(map, 0) != 0xAA) ++ map_write8(map, 0xAA, 0); ++ if (map_read8(map, 0) != 0xAA) + return NULL; + + /* Check the last byte is RAM */ +- map->write8(map, 0x55, map->size-1); +- if (map->read8(map, map->size-1) != 0x55) ++ map_write8(map, 0x55, map->size-1); ++ if (map_read8(map, map->size-1) != 0x55) + return NULL; + +- map->write8(map, 0xAA, map->size-1); +- if (map->read8(map, map->size-1) != 0xAA) ++ map_write8(map, 0xAA, map->size-1); ++ if (map_read8(map, map->size-1) != 0xAA) + return NULL; + #endif + /* OK. It seems to be RAM. */ +@@ -74,7 +76,7 @@ + while(mtd->size & (mtd->erasesize - 1)) + mtd->erasesize >>= 1; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + +@@ -83,7 +85,7 @@ + { + struct map_info *map = (struct map_info *)mtd->priv; + +- map->copy_from(map, buf, from, len); ++ map_copy_from(map, buf, from, len); + *retlen = len; + return 0; + } +@@ -92,7 +94,7 @@ + { + struct map_info *map = (struct map_info *)mtd->priv; + +- map->copy_to(map, to, buf, len); ++ map_copy_to(map, to, buf, len); + *retlen = len; + return 0; + } +@@ -105,7 +107,7 @@ + unsigned long i; + + for (i=0; i<instr->len; i++) +- map->write8(map, 0xFF, instr->addr + i); ++ map_write8(map, 0xFF, instr->addr + i); + + if (instr->callback) + instr->callback(instr); +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/map_rom.c linux/drivers/mtd/chips/map_rom.c +--- linux-mips-2.4.27/drivers/mtd/chips/map_rom.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/chips/map_rom.c 2004-11-19 10:25:11.778218184 +0100 +@@ -1,7 +1,7 @@ + /* + * Common code to handle map devices which are simple ROM + * (C) 2000 Red Hat. GPL'd. +- * $Id: map_rom.c,v 1.17 2001/10/02 15:05:12 dwmw2 Exp $ ++ * $Id: map_rom.c,v 1.20 2003/05/28 12:51:49 dwmw2 Exp $ + */ + + #include <linux/version.h> +@@ -12,8 +12,10 @@ + #include <asm/byteorder.h> + #include <linux/errno.h> + #include <linux/slab.h> +- ++#include <linux/init.h> ++#include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> ++#include <linux/mtd/compatmac.h> + + static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); + static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +@@ -21,9 +23,9 @@ + struct mtd_info *map_rom_probe(struct map_info *map); + + static struct mtd_chip_driver maprom_chipdrv = { +- probe: map_rom_probe, +- name: "map_rom", +- module: THIS_MODULE ++ .probe = map_rom_probe, ++ .name = "map_rom", ++ .module = THIS_MODULE + }; + + struct mtd_info *map_rom_probe(struct map_info *map) +@@ -49,7 +51,7 @@ + while(mtd->size & (mtd->erasesize - 1)) + mtd->erasesize >>= 1; + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + return mtd; + } + +@@ -58,7 +60,7 @@ + { + struct map_info *map = (struct map_info *)mtd->priv; + +- map->copy_from(map, buf, from, len); ++ map_copy_from(map, buf, from, len); + *retlen = len; + return 0; + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/chips/sharp.c linux/drivers/mtd/chips/sharp.c +--- linux-mips-2.4.27/drivers/mtd/chips/sharp.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/chips/sharp.c 2004-11-19 10:25:11.792216056 +0100 +@@ -4,7 +4,7 @@ + * Copyright 2000,2001 David A. Schleef <ds@schleef.org> + * 2000,2001 Lineo, Inc. + * +- * $Id: sharp.c,v 1.8 2002/05/17 08:59:19 dwmw2 Exp $ ++ * $Id: sharp.c,v 1.12 2003/05/28 15:39:52 dwmw2 Exp $ + * + * Devices supported: + * LH28F016SCT Symmetrical block flash memory, 2Mx8 +@@ -28,6 +28,7 @@ + #include <linux/errno.h> + #include <linux/interrupt.h> + #include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> + #include <linux/mtd/cfi.h> + #include <linux/delay.h> + +@@ -98,10 +99,10 @@ + static void sharp_destroy(struct mtd_info *mtd); + + static struct mtd_chip_driver sharp_chipdrv = { +- probe: sharp_probe, +- destroy: sharp_destroy, +- name: "sharp", +- module: THIS_MODULE ++ .probe = sharp_probe, ++ .destroy = sharp_destroy, ++ .name = "sharp", ++ .module = THIS_MODULE + }; + + +@@ -116,8 +117,10 @@ + return NULL; + + sharp = kmalloc(sizeof(*sharp), GFP_KERNEL); +- if(!sharp) ++ if(!sharp) { ++ kfree(mtd); + return NULL; ++ } + + memset(mtd, 0, sizeof(*mtd)); + +@@ -163,12 +166,12 @@ + u32 read0, read4; + int width = 4; + +- tmp = map->read32(map, base+0); ++ tmp = map_read32(map, base+0); + +- map->write32(map, CMD_READ_ID, base+0); ++ map_write32(map, CMD_READ_ID, base+0); + +- read0=map->read32(map, base+0); +- read4=map->read32(map, base+4); ++ read0=map_read32(map, base+0); ++ read4=map_read32(map, base+4); + if(read0 == 0x89898989){ + printk("Looks like sharp flash\n"); + switch(read4){ +@@ -196,10 +199,10 @@ + printk("Sort-of looks like sharp flash, 0x%08x 0x%08x\n", + read0,read4); + } +- }else if((map->read32(map, base+0) == CMD_READ_ID)){ ++ }else if((map_read32(map, base+0) == CMD_READ_ID)){ + /* RAM, probably */ + printk("Looks like RAM\n"); +- map->write32(map, tmp, base+0); ++ map_write32(map, tmp, base+0); + }else{ + printk("Doesn't look like sharp flash, 0x%08x 0x%08x\n", + read0,read4); +@@ -221,10 +224,10 @@ + + switch(chip->state){ + case FL_READY: +- map->write32(map,CMD_READ_STATUS,adr); ++ map_write32(map,CMD_READ_STATUS,adr); + chip->state = FL_STATUS; + case FL_STATUS: +- status = map->read32(map,adr); ++ status = map_read32(map,adr); + //printk("status=%08x\n",status); + + udelay(100); +@@ -252,7 +255,7 @@ + goto retry; + } + +- map->write32(map,CMD_RESET, adr); ++ map_write32(map,CMD_RESET, adr); + + chip->state = FL_READY; + +@@ -293,7 +296,7 @@ + if(ret<0) + break; + +- map->copy_from(map,buf,ofs,thislen); ++ map_copy_from(map,buf,ofs,thislen); + + sharp_release(&sharp->chips[chipnum]); + +@@ -354,17 +357,17 @@ + ret = sharp_wait(map,chip); + + for(try=0;try<10;try++){ +- map->write32(map,CMD_BYTE_WRITE,adr); ++ map_write32(map,CMD_BYTE_WRITE,adr); + /* cpu_to_le32 -> hack to fix the writel be->le conversion */ +- map->write32(map,cpu_to_le32(datum),adr); ++ map_write32(map,cpu_to_le32(datum),adr); + + chip->state = FL_WRITING; + + timeo = jiffies + (HZ/2); + +- map->write32(map,CMD_READ_STATUS,adr); ++ map_write32(map,CMD_READ_STATUS,adr); + for(i=0;i<100;i++){ +- status = map->read32(map,adr); ++ status = map_read32(map,adr); + if((status & SR_READY)==SR_READY) + break; + } +@@ -377,9 +380,9 @@ + + printk("sharp: error writing byte at addr=%08lx status=%08x\n",adr,status); + +- map->write32(map,CMD_CLEAR_STATUS,adr); ++ map_write32(map,CMD_CLEAR_STATUS,adr); + } +- map->write32(map,CMD_RESET,adr); ++ map_write32(map,CMD_RESET,adr); + chip->state = FL_READY; + + wake_up(&chip->wq); +@@ -436,14 +439,14 @@ + int status; + DECLARE_WAITQUEUE(wait, current); + +- map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + + timeo = jiffies + HZ; + + while(time_before(jiffies, timeo)){ +- map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + if((status & SR_READY)==SR_READY){ + ret = 0; + goto out; +@@ -485,26 +488,26 @@ + sharp_unlock_oneblock(map,chip,adr); + #endif + +- map->write32(map,CMD_BLOCK_ERASE_1,adr); +- map->write32(map,CMD_BLOCK_ERASE_2,adr); ++ map_write32(map,CMD_BLOCK_ERASE_1,adr); ++ map_write32(map,CMD_BLOCK_ERASE_2,adr); + + chip->state = FL_ERASING; + + ret = sharp_do_wait_for_ready(map,chip,adr); + if(ret<0)return ret; + +- map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + + if(!(status&SR_ERRORS)){ +- map->write32(map,CMD_RESET,adr); ++ map_write32(map,CMD_RESET,adr); + chip->state = FL_READY; + //spin_unlock_bh(chip->mutex); + return 0; + } + + printk("sharp: error erasing block at addr=%08lx status=%08x\n",adr,status); +- map->write32(map,CMD_CLEAR_STATUS,adr); ++ map_write32(map,CMD_CLEAR_STATUS,adr); + + //spin_unlock_bh(chip->mutex); + +@@ -518,17 +521,17 @@ + int i; + int status; + +- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr); +- map->write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr); ++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_1,adr); ++ map_write32(map,CMD_CLEAR_BLOCK_LOCKS_2,adr); + + udelay(100); + +- status = map->read32(map,adr); ++ status = map_read32(map,adr); + printk("status=%08x\n",status); + + for(i=0;i<1000;i++){ +- //map->write32(map,CMD_READ_STATUS,adr); +- status = map->read32(map,adr); ++ //map_write32(map,CMD_READ_STATUS,adr); ++ status = map_read32(map,adr); + if((status & SR_READY)==SR_READY) + break; + udelay(100); +@@ -538,13 +541,13 @@ + } + + if(!(status&SR_ERRORS)){ +- map->write32(map,CMD_RESET,adr); ++ map_write32(map,CMD_RESET,adr); + chip->state = FL_READY; + return; + } + + printk("sharp: error unlocking block at addr=%08lx status=%08x\n",adr,status); +- map->write32(map,CMD_CLEAR_STATUS,adr); ++ map_write32(map,CMD_CLEAR_STATUS,adr); + } + #endif + +diff -Nurb linux-mips-2.4.27/drivers/mtd/cmdlinepart.c linux/drivers/mtd/cmdlinepart.c +--- linux-mips-2.4.27/drivers/mtd/cmdlinepart.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/cmdlinepart.c 2004-11-19 10:25:11.628240984 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: cmdlinepart.c,v 1.6 2002/11/16 01:37:39 dneuer Exp $ ++ * $Id: cmdlinepart.c,v 1.11 2003/10/23 08:32:45 dwmw2 Exp $ + * + * Read flash partition table from command line + * +@@ -28,7 +28,7 @@ + + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> +-#include <asm/setup.h> ++#include <linux/mtd/compatmac.h> + #include <linux/bootmem.h> + + /* error message prefix */ +@@ -178,8 +178,7 @@ + parts[this_part].mask_flags = mask_flags; + if (name) + { +- strncpy(extra_mem, name, name_len); +- extra_mem[name_len] = 0; ++ strlcpy(extra_mem, name, name_len + 1); + } + else + { +@@ -258,8 +257,7 @@ + this_mtd->parts = parts; + this_mtd->num_parts = num_parts; + this_mtd->mtd_id = (char*)(this_mtd + 1); +- strncpy(this_mtd->mtd_id, mtd_id, mtd_id_len); +- this_mtd->mtd_id[mtd_id_len] = 0; ++ strlcpy(this_mtd->mtd_id, mtd_id, mtd_id_len + 1); + + /* link into chain */ + this_mtd->next = partitions; +@@ -291,13 +289,14 @@ + * information. It returns partitions for the requested mtd device, or + * the first one in the chain if a NULL mtd_id is passed in. + */ +-int parse_cmdline_partitions(struct mtd_info *master, ++static int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, +- const char *mtd_id) ++ unsigned long origin) + { + unsigned long offset; + int i; + struct cmdline_mtd_partition *part; ++ char *mtd_id = master->name; + + if(!cmdline) + return -EINVAL; +@@ -349,7 +348,25 @@ + + __setup("mtdparts=", mtdpart_setup); + +-EXPORT_SYMBOL(parse_cmdline_partitions); ++static struct mtd_part_parser cmdline_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_cmdline_partitions, ++ .name = "cmdlinepart", ++}; ++ ++static int __init cmdline_parser_init(void) ++{ ++ return register_mtd_parser(&cmdline_parser); ++} ++ ++static void __exit cmdline_parser_exit(void) ++{ ++ deregister_mtd_parser(&cmdline_parser); ++} ++ ++module_init(cmdline_parser_init); ++module_exit(cmdline_parser_exit); ++ + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/Config.in linux/drivers/mtd/devices/Config.in +--- linux-mips-2.4.27/drivers/mtd/devices/Config.in 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/Config.in 2004-11-19 10:25:11.811213168 +0100 +@@ -1,6 +1,6 @@ +-# drivers/mtd/maps/Config.in ++# drivers/mtd/devices/Config.in + +-# $Id: Config.in,v 1.8 2003/01/24 23:25:14 dwmw2 Exp $ ++# $Id: Config.in,v 1.11 2003/05/28 10:54:23 dwmw2 Exp $ + + mainmenu_option next_comment + +@@ -28,13 +28,13 @@ + dep_tristate ' MTD emulation using block device' CONFIG_MTD_BLKMTD $CONFIG_MTD + + comment 'Disk-On-Chip Device Drivers' +- dep_tristate ' M-Systems Disk-On-Chip 1000' CONFIG_MTD_DOC1000 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip 2000 and Millennium' CONFIG_MTD_DOC2000 $CONFIG_MTD + dep_tristate ' M-Systems Disk-On-Chip Millennium-only alternative driver (see help)' CONFIG_MTD_DOC2001 $CONFIG_MTD +- if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then ++ dep_tristate ' M-Systems Disk-On-Chip Millennium Plus driver (see help)' CONFIG_MTD_DOC2001PLUS $CONFIG_MTD ++ if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" ]; then + define_bool CONFIG_MTD_DOCPROBE y + else +- if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then ++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" ]; then + define_bool CONFIG_MTD_DOCPROBE m + else + define_bool CONFIG_MTD_DOCPROBE n +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/Makefile linux/drivers/mtd/devices/Makefile +--- linux-mips-2.4.27/drivers/mtd/devices/Makefile 2002-03-30 09:15:50.000000000 +0100 ++++ linux/drivers/mtd/devices/Makefile 2004-11-19 10:25:11.813212864 +0100 +@@ -1,9 +1,12 @@ + # + # linux/drivers/devices/Makefile + # +-# $Id: Makefile,v 1.4 2001/06/26 21:10:05 spse Exp $ ++# $Id: Makefile.common,v 1.4 2003/08/21 17:52:29 joern Exp $ + ++ifeq ($(PATCHLEVEL),4) + O_TARGET := devlink.o ++export-objs := docecc.o ++endif + + # *** BIG UGLY NOTE *** + # +@@ -12,15 +15,16 @@ + # here where previously there was none. We now have to ensure that + # doc200[01].o are linked before docprobe.o + +-obj-$(CONFIG_MTD_DOC1000) += doc1000.o + obj-$(CONFIG_MTD_DOC2000) += doc2000.o + obj-$(CONFIG_MTD_DOC2001) += doc2001.o ++obj-$(CONFIG_MTD_DOC2001PLUS) += doc2001plus.o + obj-$(CONFIG_MTD_DOCPROBE) += docprobe.o docecc.o + obj-$(CONFIG_MTD_SLRAM) += slram.o ++obj-$(CONFIG_MTD_PHRAM) += phram.o + obj-$(CONFIG_MTD_PMC551) += pmc551.o + obj-$(CONFIG_MTD_MS02NV) += ms02-nv.o + obj-$(CONFIG_MTD_MTDRAM) += mtdram.o + obj-$(CONFIG_MTD_LART) += lart.o + obj-$(CONFIG_MTD_BLKMTD) += blkmtd.o + +-include $(TOPDIR)/Rules.make ++-include $(TOPDIR)/Rules.make +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/blkmtd-25.c linux/drivers/mtd/devices/blkmtd-25.c +--- linux-mips-2.4.27/drivers/mtd/devices/blkmtd-25.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/devices/blkmtd-25.c 2004-11-19 10:25:11.814212712 +0100 +@@ -0,0 +1,827 @@ ++/* ++ * $Id: blkmtd-25.c,v 1.5 2003/07/16 06:48:27 spse Exp $ ++ * ++ * blkmtd.c - use a block device as a fake MTD ++ * ++ * Author: Simon Evans <spse@secret.org.uk> ++ * ++ * Copyright (C) 2001,2002 Simon Evans ++ * ++ * Licence: GPL ++ * ++ * How it works: ++ * The driver uses raw/io to read/write the device and the page ++ * cache to cache access. Writes update the page cache with the ++ * new data and mark it dirty and add the page into a BIO which ++ * is then written out. ++ * ++ * It can be loaded Read-Only to prevent erases and writes to the ++ * medium. ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/fs.h> ++#include <linux/blkdev.h> ++#include <linux/bio.h> ++#include <linux/pagemap.h> ++#include <linux/list.h> ++#include <linux/init.h> ++#include <linux/mtd/mtd.h> ++ ++ ++#define err(format, arg...) printk(KERN_ERR "blkmtd: " format "\n" , ## arg) ++#define info(format, arg...) printk(KERN_INFO "blkmtd: " format "\n" , ## arg) ++#define warn(format, arg...) printk(KERN_WARNING "blkmtd: " format "\n" , ## arg) ++#define crit(format, arg...) printk(KERN_CRIT "blkmtd: " format "\n" , ## arg) ++ ++ ++/* Default erase size in K, always make it a multiple of PAGE_SIZE */ ++#define CONFIG_MTD_BLKDEV_ERASESIZE (128 << 10) /* 128KiB */ ++#define VERSION "$Revision: 1.5 $" ++ ++/* Info for the block device */ ++struct blkmtd_dev { ++ struct list_head list; ++ struct block_device *blkdev; ++ struct mtd_info mtd_info; ++ struct semaphore wrbuf_mutex; ++}; ++ ++ ++/* Static info about the MTD, used in cleanup_module */ ++static LIST_HEAD(blkmtd_device_list); ++ ++ ++static void blkmtd_sync(struct mtd_info *mtd); ++ ++#define MAX_DEVICES 4 ++ ++/* Module parameters passed by insmod/modprobe */ ++char *device[MAX_DEVICES]; /* the block device to use */ ++int erasesz[MAX_DEVICES]; /* optional default erase size */ ++int ro[MAX_DEVICES]; /* optional read only flag */ ++int sync; ++ ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>"); ++MODULE_DESCRIPTION("Emulate an MTD using a block device"); ++MODULE_PARM(device, "1-4s"); ++MODULE_PARM_DESC(device, "block device to use"); ++MODULE_PARM(erasesz, "1-4i"); ++MODULE_PARM_DESC(erasesz, "optional erase size to use in KiB. eg 4=4KiB."); ++MODULE_PARM(ro, "1-4i"); ++MODULE_PARM_DESC(ro, "1=Read only, writes and erases cause errors"); ++MODULE_PARM(sync, "i"); ++MODULE_PARM_DESC(sync, "1=Synchronous writes"); ++ ++ ++/* completion handler for BIO reads */ ++static int bi_read_complete(struct bio *bio, unsigned int bytes_done, int error) ++{ ++ if (bio->bi_size) ++ return 1; ++ ++ complete((struct completion*)bio->bi_private); ++ return 0; ++} ++ ++ ++/* completion handler for BIO writes */ ++static int bi_write_complete(struct bio *bio, unsigned int bytes_done, int error) ++{ ++ const int uptodate = test_bit(BIO_UPTODATE, &bio->bi_flags); ++ struct bio_vec *bvec = bio->bi_io_vec + bio->bi_vcnt - 1; ++ ++ if (bio->bi_size) ++ return 1; ++ ++ if(!uptodate) ++ err("bi_write_complete: not uptodate\n"); ++ ++ do { ++ struct page *page = bvec->bv_page; ++ DEBUG(3, "Cleaning up page %ld\n", page->index); ++ if (--bvec >= bio->bi_io_vec) ++ prefetchw(&bvec->bv_page->flags); ++ ++ if (uptodate) { ++ SetPageUptodate(page); ++ } else { ++ ClearPageUptodate(page); ++ SetPageError(page); ++ } ++ ClearPageDirty(page); ++ unlock_page(page); ++ page_cache_release(page); ++ } while (bvec >= bio->bi_io_vec); ++ ++ complete((struct completion*)bio->bi_private); ++ return 0; ++} ++ ++ ++/* read one page from the block device */ ++static int blkmtd_readpage(struct blkmtd_dev *dev, struct page *page) ++{ ++ struct bio *bio; ++ struct completion event; ++ int err = -ENOMEM; ++ ++ if(PageUptodate(page)) { ++ DEBUG(2, "blkmtd: readpage page %ld is already upto date\n", page->index); ++ unlock_page(page); ++ return 0; ++ } ++ ++ ClearPageUptodate(page); ++ ClearPageError(page); ++ ++ bio = bio_alloc(GFP_KERNEL, 1); ++ if(bio) { ++ init_completion(&event); ++ bio->bi_bdev = dev->blkdev; ++ bio->bi_sector = page->index << (PAGE_SHIFT-9); ++ bio->bi_private = &event; ++ bio->bi_end_io = bi_read_complete; ++ if(bio_add_page(bio, page, PAGE_SIZE, 0) == PAGE_SIZE) { ++ submit_bio(READ, bio); ++ blk_run_queues(); ++ wait_for_completion(&event); ++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; ++ bio_put(bio); ++ } ++ } ++ ++ if(err) ++ SetPageError(page); ++ else ++ SetPageUptodate(page); ++ flush_dcache_page(page); ++ unlock_page(page); ++ return err; ++} ++ ++ ++/* write out the current BIO and wait for it to finish */ ++static int blkmtd_write_out(struct bio *bio) ++{ ++ struct completion event; ++ int err; ++ ++ if(!bio->bi_vcnt) { ++ bio_put(bio); ++ return 0; ++ } ++ ++ init_completion(&event); ++ bio->bi_private = &event; ++ bio->bi_end_io = bi_write_complete; ++ submit_bio(WRITE, bio); ++ blk_run_queues(); ++ wait_for_completion(&event); ++ DEBUG(3, "submit_bio completed, bi_vcnt = %d\n", bio->bi_vcnt); ++ err = test_bit(BIO_UPTODATE, &bio->bi_flags) ? 0 : -EIO; ++ bio_put(bio); ++ return err; ++} ++ ++ ++/** ++ * blkmtd_add_page - add a page to the current BIO ++ * @bio: bio to add to (NULL to alloc initial bio) ++ * @blkdev: block device ++ * @page: page to add ++ * @pagecnt: pages left to add ++ * ++ * Adds a page to the current bio, allocating it if necessary. If it cannot be ++ * added, the current bio is written out and a new one is allocated. Returns ++ * the new bio to add or NULL on error ++ */ ++static struct bio *blkmtd_add_page(struct bio *bio, struct block_device *blkdev, ++ struct page *page, int pagecnt) ++{ ++ ++ retry: ++ if(!bio) { ++ bio = bio_alloc(GFP_KERNEL, pagecnt); ++ if(!bio) ++ return NULL; ++ bio->bi_sector = page->index << (PAGE_SHIFT-9); ++ bio->bi_bdev = blkdev; ++ } ++ ++ if(bio_add_page(bio, page, PAGE_SIZE, 0) != PAGE_SIZE) { ++ blkmtd_write_out(bio); ++ bio = NULL; ++ goto retry; ++ } ++ return bio; ++} ++ ++ ++/** ++ * write_pages - write block of data to device via the page cache ++ * @dev: device to write to ++ * @buf: data source or NULL if erase (output is set to 0xff) ++ * @to: offset into output device ++ * @len: amount to data to write ++ * @retlen: amount of data written ++ * ++ * Grab pages from the page cache and fill them with the source data. ++ * Non page aligned start and end result in a readin of the page and ++ * part of the page being modified. Pages are added to the bio and then written ++ * out. ++ */ ++static int write_pages(struct blkmtd_dev *dev, const u_char *buf, loff_t to, ++ size_t len, size_t *retlen) ++{ ++ int pagenr, offset; ++ size_t start_len = 0, end_len; ++ int pagecnt = 0; ++ int err = 0; ++ struct bio *bio = NULL; ++ size_t thislen = 0; ++ ++ pagenr = to >> PAGE_SHIFT; ++ offset = to & ~PAGE_MASK; ++ ++ DEBUG(2, "blkmtd: write_pages: buf = %p to = %ld len = %d pagenr = %d offset = %d\n", ++ buf, (long)to, len, pagenr, offset); ++ ++ /* see if we have to do a partial write at the start */ ++ if(offset) { ++ start_len = ((offset + len) > PAGE_SIZE) ? PAGE_SIZE - offset : len; ++ len -= start_len; ++ } ++ ++ /* calculate the length of the other two regions */ ++ end_len = len & ~PAGE_MASK; ++ len -= end_len; ++ ++ if(start_len) ++ pagecnt++; ++ ++ if(len) ++ pagecnt += len >> PAGE_SHIFT; ++ ++ if(end_len) ++ pagecnt++; ++ ++ down(&dev->wrbuf_mutex); ++ ++ DEBUG(3, "blkmtd: write: start_len = %d len = %d end_len = %d pagecnt = %d\n", ++ start_len, len, end_len, pagecnt); ++ ++ if(start_len) { ++ /* do partial start region */ ++ struct page *page; ++ ++ DEBUG(3, "blkmtd: write: doing partial start, page = %d len = %d offset = %d\n", ++ pagenr, start_len, offset); ++ ++ BUG_ON(!buf); ++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); ++ lock_page(page); ++ if(PageDirty(page)) { ++ err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n", ++ to, start_len, len, end_len, pagenr); ++ BUG(); ++ } ++ memcpy(page_address(page)+offset, buf, start_len); ++ SetPageDirty(page); ++ SetPageUptodate(page); ++ buf += start_len; ++ thislen = start_len; ++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); ++ if(!bio) { ++ err = -ENOMEM; ++ err("bio_add_page failed\n"); ++ goto write_err; ++ } ++ pagecnt--; ++ pagenr++; ++ } ++ ++ /* Now do the main loop to a page aligned, n page sized output */ ++ if(len) { ++ int pagesc = len >> PAGE_SHIFT; ++ DEBUG(3, "blkmtd: write: whole pages start = %d, count = %d\n", ++ pagenr, pagesc); ++ while(pagesc) { ++ struct page *page; ++ ++ /* see if page is in the page cache */ ++ DEBUG(3, "blkmtd: write: grabbing page %d from page cache\n", pagenr); ++ page = grab_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr); ++ if(PageDirty(page)) { ++ BUG(); ++ } ++ if(!page) { ++ warn("write: cannot grab cache page %d", pagenr); ++ err = -ENOMEM; ++ goto write_err; ++ } ++ if(!buf) { ++ memset(page_address(page), 0xff, PAGE_SIZE); ++ } else { ++ memcpy(page_address(page), buf, PAGE_SIZE); ++ buf += PAGE_SIZE; ++ } ++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); ++ if(!bio) { ++ err = -ENOMEM; ++ err("bio_add_page failed\n"); ++ goto write_err; ++ } ++ pagenr++; ++ pagecnt--; ++ SetPageDirty(page); ++ SetPageUptodate(page); ++ pagesc--; ++ thislen += PAGE_SIZE; ++ } ++ } ++ ++ if(end_len) { ++ /* do the third region */ ++ struct page *page; ++ DEBUG(3, "blkmtd: write: doing partial end, page = %d len = %d\n", ++ pagenr, end_len); ++ BUG_ON(!buf); ++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); ++ lock_page(page); ++ if(PageDirty(page)) { ++ err("to = %lld start_len = %d len = %d end_len = %d pagenr = %d\n", ++ to, start_len, len, end_len, pagenr); ++ BUG(); ++ } ++ memcpy(page_address(page), buf, end_len); ++ SetPageDirty(page); ++ SetPageUptodate(page); ++ DEBUG(3, "blkmtd: write: writing out partial end\n"); ++ thislen += end_len; ++ bio = blkmtd_add_page(bio, dev->blkdev, page, pagecnt); ++ if(!bio) { ++ err = -ENOMEM; ++ err("bio_add_page failed\n"); ++ goto write_err; ++ } ++ pagenr++; ++ } ++ ++ DEBUG(3, "blkmtd: write: got %d vectors to write\n", bio->bi_vcnt); ++ write_err: ++ if(bio) ++ blkmtd_write_out(bio); ++ ++ DEBUG(2, "blkmtd: write: end, retlen = %d, err = %d\n", *retlen, err); ++ up(&dev->wrbuf_mutex); ++ ++ if(retlen) ++ *retlen = thislen; ++ return err; ++} ++ ++ ++/* erase a specified part of the device */ ++static int blkmtd_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ struct mtd_erase_region_info *einfo = mtd->eraseregions; ++ int numregions = mtd->numeraseregions; ++ size_t from; ++ u_long len; ++ int err = -EIO; ++ int retlen; ++ ++ instr->state = MTD_ERASING; ++ from = instr->addr; ++ len = instr->len; ++ ++ /* check erase region has valid start and length */ ++ DEBUG(2, "blkmtd: erase: dev = `%s' from = 0x%x len = 0x%lx\n", ++ mtd->name+9, from, len); ++ while(numregions) { ++ DEBUG(3, "blkmtd: checking erase region = 0x%08X size = 0x%X num = 0x%x\n", ++ einfo->offset, einfo->erasesize, einfo->numblocks); ++ if(from >= einfo->offset ++ && from < einfo->offset + (einfo->erasesize * einfo->numblocks)) { ++ if(len == einfo->erasesize ++ && ( (from - einfo->offset) % einfo->erasesize == 0)) ++ break; ++ } ++ numregions--; ++ einfo++; ++ } ++ ++ if(!numregions) { ++ /* Not a valid erase block */ ++ err("erase: invalid erase request 0x%lX @ 0x%08X", len, from); ++ instr->state = MTD_ERASE_FAILED; ++ err = -EIO; ++ } ++ ++ if(instr->state != MTD_ERASE_FAILED) { ++ /* do the erase */ ++ DEBUG(3, "Doing erase from = %d len = %ld\n", from, len); ++ err = write_pages(dev, NULL, from, len, &retlen); ++ if(err || retlen != len) { ++ err("erase failed err = %d", err); ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ } ++ } ++ ++ DEBUG(3, "blkmtd: erase: checking callback\n"); ++ if (instr->callback) { ++ (*(instr->callback))(instr); ++ } ++ DEBUG(2, "blkmtd: erase: finished (err = %d)\n", err); ++ return err; ++} ++ ++ ++/* read a range of the data via the page cache */ ++static int blkmtd_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ int err = 0; ++ int offset; ++ int pagenr, pages; ++ size_t thislen = 0; ++ ++ DEBUG(2, "blkmtd: read: dev = `%s' from = %ld len = %d buf = %p\n", ++ mtd->name+9, (long int)from, len, buf); ++ ++ if(from > mtd->size) ++ return -EINVAL; ++ if(from + len > mtd->size) ++ len = mtd->size - from; ++ ++ pagenr = from >> PAGE_SHIFT; ++ offset = from - (pagenr << PAGE_SHIFT); ++ ++ pages = (offset+len+PAGE_SIZE-1) >> PAGE_SHIFT; ++ DEBUG(3, "blkmtd: read: pagenr = %d offset = %d, pages = %d\n", ++ pagenr, offset, pages); ++ ++ while(pages) { ++ struct page *page; ++ int cpylen; ++ ++ DEBUG(3, "blkmtd: read: looking for page: %d\n", pagenr); ++ page = read_cache_page(dev->blkdev->bd_inode->i_mapping, pagenr, (filler_t *)blkmtd_readpage, dev); ++ if(IS_ERR(page)) { ++ err = -EIO; ++ goto readerr; ++ } ++ ++ cpylen = (PAGE_SIZE > len) ? len : PAGE_SIZE; ++ if(offset+cpylen > PAGE_SIZE) ++ cpylen = PAGE_SIZE-offset; ++ ++ memcpy(buf + thislen, page_address(page) + offset, cpylen); ++ offset = 0; ++ len -= cpylen; ++ thislen += cpylen; ++ pagenr++; ++ pages--; ++ if(!PageDirty(page)) ++ page_cache_release(page); ++ } ++ ++ readerr: ++ if(retlen) ++ *retlen = thislen; ++ DEBUG(2, "blkmtd: end read: retlen = %d, err = %d\n", thislen, err); ++ return err; ++} ++ ++ ++/* write data to the underlying device */ ++static int blkmtd_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ struct blkmtd_dev *dev = mtd->priv; ++ int err; ++ ++ if(!len) ++ return 0; ++ ++ DEBUG(2, "blkmtd: write: dev = `%s' to = %ld len = %d buf = %p\n", ++ mtd->name+9, (long int)to, len, buf); ++ ++ if(to >= mtd->size) { ++ return -ENOSPC; ++ } ++ ++ if(to + len > mtd->size) { ++ len = mtd->size - to; ++ } ++ ++ err = write_pages(dev, buf, to, len, retlen); ++ if(err > 0) ++ err = 0; ++ DEBUG(2, "blkmtd: write: end, err = %d\n", err); ++ return err; ++} ++ ++ ++/* sync the device - wait until the write queue is empty */ ++static void blkmtd_sync(struct mtd_info *mtd) ++{ ++ /* Currently all writes are synchronous */ ++} ++ ++ ++static void free_device(struct blkmtd_dev *dev) ++{ ++ DEBUG(2, "blkmtd: free_device() dev = %p\n", dev); ++ if(dev) { ++ if(dev->mtd_info.eraseregions) ++ kfree(dev->mtd_info.eraseregions); ++ if(dev->mtd_info.name) ++ kfree(dev->mtd_info.name); ++ ++ if(dev->blkdev) { ++ invalidate_inode_pages(dev->blkdev->bd_inode->i_mapping); ++ close_bdev_excl(dev->blkdev, BDEV_RAW); ++ } ++ kfree(dev); ++ } ++} ++ ++ ++/* For a given size and initial erase size, calculate the number ++ * and size of each erase region. Goes round the loop twice, ++ * once to find out how many regions, then allocates space, ++ * then round the loop again to fill it in. ++ */ ++static struct mtd_erase_region_info *calc_erase_regions( ++ size_t erase_size, size_t total_size, int *regions) ++{ ++ struct mtd_erase_region_info *info = NULL; ++ ++ DEBUG(2, "calc_erase_regions, es = %d size = %d regions = %d\n", ++ erase_size, total_size, *regions); ++ /* Make any user specified erasesize be a power of 2 ++ and at least PAGE_SIZE */ ++ if(erase_size) { ++ int es = erase_size; ++ erase_size = 1; ++ while(es != 1) { ++ es >>= 1; ++ erase_size <<= 1; ++ } ++ if(erase_size < PAGE_SIZE) ++ erase_size = PAGE_SIZE; ++ } else { ++ erase_size = CONFIG_MTD_BLKDEV_ERASESIZE; ++ } ++ ++ *regions = 0; ++ ++ do { ++ int tot_size = total_size; ++ int er_size = erase_size; ++ int count = 0, offset = 0, regcnt = 0; ++ ++ while(tot_size) { ++ count = tot_size / er_size; ++ if(count) { ++ tot_size = tot_size % er_size; ++ if(info) { ++ DEBUG(2, "adding to erase info off=%d er=%d cnt=%d\n", ++ offset, er_size, count); ++ (info+regcnt)->offset = offset; ++ (info+regcnt)->erasesize = er_size; ++ (info+regcnt)->numblocks = count; ++ (*regions)++; ++ } ++ regcnt++; ++ offset += (count * er_size); ++ } ++ while(er_size > tot_size) ++ er_size >>= 1; ++ } ++ if(info == NULL) { ++ info = kmalloc(regcnt * sizeof(struct mtd_erase_region_info), GFP_KERNEL); ++ if(!info) ++ break; ++ } ++ } while(!(*regions)); ++ DEBUG(2, "calc_erase_regions done, es = %d size = %d regions = %d\n", ++ erase_size, total_size, *regions); ++ return info; ++} ++ ++ ++extern dev_t __init name_to_dev_t(const char *line); ++ ++static struct blkmtd_dev *add_device(char *devname, int readonly, int erase_size) ++{ ++ struct block_device *bdev; ++ int mode; ++ struct blkmtd_dev *dev; ++ ++ if(!devname) ++ return NULL; ++ ++ /* Get a handle on the device */ ++ ++ ++#ifdef MODULE ++ mode = (readonly) ? O_RDONLY : O_RDWR; ++ bdev = open_bdev_excl(devname, mode, BDEV_RAW, NULL); ++#else ++ mode = (readonly) ? FMODE_READ : FMODE_WRITE; ++ bdev = open_by_devnum(name_to_dev_t(devname), mode, BDEV_RAW); ++#endif ++ if(IS_ERR(bdev)) { ++ err("error: cannot open device %s", devname); ++ DEBUG(2, "blkmtd: opening bdev returned %ld\n", PTR_ERR(bdev)); ++ return NULL; ++ } ++ ++ DEBUG(1, "blkmtd: found a block device major = %d, minor = %d\n", ++ MAJOR(bdev->bd_dev), MINOR(bdev->bd_dev)); ++ ++ if(MAJOR(bdev->bd_dev) == MTD_BLOCK_MAJOR) { ++ err("attempting to use an MTD device as a block device"); ++ blkdev_put(bdev, BDEV_RAW); ++ return NULL; ++ } ++ ++ dev = kmalloc(sizeof(struct blkmtd_dev), GFP_KERNEL); ++ if(dev == NULL) { ++ blkdev_put(bdev, BDEV_RAW); ++ return NULL; ++ } ++ ++ memset(dev, 0, sizeof(struct blkmtd_dev)); ++ if(!readonly) { ++ init_MUTEX(&dev->wrbuf_mutex); ++ } ++ ++ dev->blkdev = bdev; ++ dev->mtd_info.size = dev->blkdev->bd_inode->i_size & PAGE_MASK; ++ ++ /* Setup the MTD structure */ ++ /* make the name contain the block device in */ ++ dev->mtd_info.name = kmalloc(sizeof("blkmtd: ") + strlen(devname), GFP_KERNEL); ++ if(dev->mtd_info.name == NULL) ++ goto devinit_err; ++ ++ sprintf(dev->mtd_info.name, "blkmtd: %s", devname); ++ dev->mtd_info.eraseregions = calc_erase_regions(erase_size, dev->mtd_info.size, ++ &dev->mtd_info.numeraseregions); ++ if(dev->mtd_info.eraseregions == NULL) ++ goto devinit_err; ++ ++ dev->mtd_info.erasesize = dev->mtd_info.eraseregions->erasesize; ++ DEBUG(1, "blkmtd: init: found %d erase regions\n", ++ dev->mtd_info.numeraseregions); ++ ++ if(readonly) { ++ dev->mtd_info.type = MTD_ROM; ++ dev->mtd_info.flags = MTD_CAP_ROM; ++ } else { ++ dev->mtd_info.type = MTD_RAM; ++ dev->mtd_info.flags = MTD_CAP_RAM; ++ dev->mtd_info.erase = blkmtd_erase; ++ dev->mtd_info.write = blkmtd_write; ++ dev->mtd_info.writev = default_mtd_writev; ++ dev->mtd_info.sync = blkmtd_sync; ++ } ++ dev->mtd_info.read = blkmtd_read; ++ dev->mtd_info.readv = default_mtd_readv; ++ dev->mtd_info.priv = dev; ++ dev->mtd_info.owner = THIS_MODULE; ++ ++ list_add(&dev->list, &blkmtd_device_list); ++ if (add_mtd_device(&dev->mtd_info)) { ++ /* Device didnt get added, so free the entry */ ++ list_del(&dev->list); ++ goto devinit_err; ++ } else { ++ info("mtd%d: [%s] erase_size = %dKiB %s", ++ dev->mtd_info.index, dev->mtd_info.name + strlen("blkmtd: "), ++ dev->mtd_info.erasesize >> 10, ++ readonly ? "(read-only)" : ""); ++ } ++ ++ return dev; ++ ++ devinit_err: ++ free_device(dev); ++ return NULL; ++} ++ ++ ++/* Cleanup and exit - sync the device and kill of the kernel thread */ ++static void __devexit cleanup_blkmtd(void) ++{ ++ struct list_head *temp1, *temp2; ++ ++ /* Remove the MTD devices */ ++ list_for_each_safe(temp1, temp2, &blkmtd_device_list) { ++ struct blkmtd_dev *dev = list_entry(temp1, struct blkmtd_dev, ++ list); ++ blkmtd_sync(&dev->mtd_info); ++ del_mtd_device(&dev->mtd_info); ++ info("mtd%d: [%s] removed", dev->mtd_info.index, ++ dev->mtd_info.name + strlen("blkmtd: ")); ++ list_del(&dev->list); ++ free_device(dev); ++ } ++} ++ ++#ifndef MODULE ++ ++/* Handle kernel boot params */ ++ ++ ++static int __init param_blkmtd_device(char *str) ++{ ++ int i; ++ ++ for(i = 0; i < MAX_DEVICES; i++) { ++ device[i] = str; ++ DEBUG(2, "blkmtd: device setup: %d = %s\n", i, device[i]); ++ strsep(&str, ","); ++ } ++ return 1; ++} ++ ++ ++static int __init param_blkmtd_erasesz(char *str) ++{ ++ int i; ++ for(i = 0; i < MAX_DEVICES; i++) { ++ char *val = strsep(&str, ","); ++ if(val) ++ erasesz[i] = simple_strtoul(val, NULL, 0); ++ DEBUG(2, "blkmtd: erasesz setup: %d = %d\n", i, erasesz[i]); ++ } ++ ++ return 1; ++} ++ ++ ++static int __init param_blkmtd_ro(char *str) ++{ ++ int i; ++ for(i = 0; i < MAX_DEVICES; i++) { ++ char *val = strsep(&str, ","); ++ if(val) ++ ro[i] = simple_strtoul(val, NULL, 0); ++ DEBUG(2, "blkmtd: ro setup: %d = %d\n", i, ro[i]); ++ } ++ ++ return 1; ++} ++ ++ ++static int __init param_blkmtd_sync(char *str) ++{ ++ if(str[0] == '1') ++ sync = 1; ++ return 1; ++} ++ ++__setup("blkmtd_device=", param_blkmtd_device); ++__setup("blkmtd_erasesz=", param_blkmtd_erasesz); ++__setup("blkmtd_ro=", param_blkmtd_ro); ++__setup("blkmtd_sync=", param_blkmtd_sync); ++ ++#endif ++ ++ ++/* Startup */ ++static int __init init_blkmtd(void) ++{ ++ int i; ++ ++ info("version " VERSION); ++ /* Check args - device[0] is the bare minimum*/ ++ if(!device[0]) { ++ err("error: missing `device' name\n"); ++ return -EINVAL; ++ } ++ ++ for(i = 0; i < MAX_DEVICES; i++) ++ add_device(device[i], ro[i], erasesz[i] << 10); ++ ++ if(list_empty(&blkmtd_device_list)) ++ return -EINVAL; ++ ++ return 0; ++} ++ ++module_init(init_blkmtd); ++module_exit(cleanup_blkmtd); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/blkmtd.c linux/drivers/mtd/devices/blkmtd.c +--- linux-mips-2.4.27/drivers/mtd/devices/blkmtd.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/blkmtd.c 2004-11-19 10:25:11.816212408 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: blkmtd.c,v 1.17 2003/01/24 13:00:24 dwmw2 Exp $ ++ * $Id: blkmtd.c,v 1.20 2003/06/27 15:10:35 dwmw2 Exp $ + * + * blkmtd.c - use a block device as a fake MTD + * +@@ -143,7 +143,7 @@ + for(cnt = 0; cnt < pages; cnt++) { + page = grab_cache_page(dev->binding->bd_inode->i_mapping, pagenrs[cnt]); + pagelst[cnt] = page; +- if(!PageUptodate(page)) { ++ if(!Page_Uptodate(page)) { + iobuf->blocks[iobuf->nr_pages] = pagenrs[cnt]; + iobuf->maplist[iobuf->nr_pages++] = page; + } +@@ -912,7 +912,7 @@ + dev->mtd_info.point = 0; + dev->mtd_info.unpoint = 0; + dev->mtd_info.priv = dev; +- dev->mtd_info.module = THIS_MODULE; ++ dev->mtd_info.owner = THIS_MODULE; + + list_add(&dev->list, &blkmtd_device_list); + if (add_mtd_device(&dev->mtd_info)) { +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/doc2000.c linux/drivers/mtd/devices/doc2000.c +--- linux-mips-2.4.27/drivers/mtd/devices/doc2000.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/doc2000.c 2004-11-19 10:25:11.818212104 +0100 +@@ -4,7 +4,7 @@ + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> + * +- * $Id: doc2000.c,v 1.50 2002/12/10 15:05:42 gleixner Exp $ ++ * $Id: doc2000.c,v 1.58 2003/11/05 16:42:25 dwmw2 Exp $ + */ + + #include <linux/kernel.h> +@@ -25,6 +25,7 @@ + #include <linux/mtd/doc2000.h> + + #define DOC_SUPPORT_2000 ++#define DOC_SUPPORT_2000TSOP + #define DOC_SUPPORT_MILLENNIUM + + #ifdef DOC_SUPPORT_2000 +@@ -33,7 +34,7 @@ + #define DoC_is_2000(doc) (0) + #endif + +-#ifdef DOC_SUPPORT_MILLENNIUM ++#if defined(DOC_SUPPORT_2000TSOP) || defined(DOC_SUPPORT_MILLENNIUM) + #define DoC_is_Millennium(doc) (doc->ChipID == DOC_ChipID_DocMil) + #else + #define DoC_is_Millennium(doc) (0) +@@ -56,6 +57,9 @@ + size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel); + static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel); ++static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen, ++ u_char *eccbuf, struct nand_oobinfo *oobsel); + static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t *retlen, u_char *buf); + static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, +@@ -92,6 +96,10 @@ + + /* Out-of-line routine to wait for chip response */ + while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ /* issue 2 read from NOP register after reading from CDSNControl register ++ see Software Requirement 11.4 item 2. */ ++ DoC_Delay(doc, 2); ++ + if (time_after(jiffies, timeo)) { + DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); + return -EIO; +@@ -145,6 +153,8 @@ + + /* Send the command */ + WriteDOC_(command, docptr, doc->ioreg); ++ if (DoC_is_Millennium(doc)) ++ WriteDOC(command, docptr, WritePipeTerm); + + /* Lower the CLE line */ + WriteDOC(xtraflags | CDSN_CTRL_CE, docptr, CDSNControl); +@@ -206,6 +216,9 @@ + } + } + ++ if (DoC_is_Millennium(doc)) ++ WriteDOC(ofs & 0xff, docptr, WritePipeTerm); ++ + DoC_Delay(doc, 2); /* Needed for some slow flash chips. mf. */ + + /* FIXME: The SlowIO's for millennium could be replaced by +@@ -344,15 +357,25 @@ + + /* Read the manufacturer and device id codes from the device */ + +- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ ++ if (DoC_is_Millennium(doc)) { ++ DoC_Delay(doc, 2); ++ dummy = ReadDOC(doc->virtadr, ReadPipeInit); ++ mfr = ReadDOC(doc->virtadr, LastDataRead); ++ ++ DoC_Delay(doc, 2); ++ dummy = ReadDOC(doc->virtadr, ReadPipeInit); ++ id = ReadDOC(doc->virtadr, LastDataRead); ++ } else { ++ /* CDSN Slow IO register see Software Req 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + mfr = ReadDOC_(doc->virtadr, doc->ioreg); + +- /* CDSN Slow IO register see Software Requirement 11.4 item 5. */ ++ /* CDSN Slow IO register see Software Req 11.4 item 5. */ + dummy = ReadDOC(doc->virtadr, CDSNSlowIO); + DoC_Delay(doc, 2); + id = ReadDOC_(doc->virtadr, doc->ioreg); ++ } + + /* No response - return failure */ + if (mfr == 0xff || mfr == 0) +@@ -410,20 +433,16 @@ + + /* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ + +-static void DoC_ScanChips(struct DiskOnChip *this) ++static void DoC_ScanChips(struct DiskOnChip *this, int maxchips) + { + int floor, chip; + int numchips[MAX_FLOORS]; +- int maxchips = MAX_CHIPS; + int ret = 1; + + this->numchips = 0; + this->mfr = 0; + this->id = 0; + +- if (DoC_is_Millennium(this)) +- maxchips = MAX_CHIPS_MIL; +- + /* For each floor, find the number of valid chips it contains */ + for (floor = 0; floor < MAX_FLOORS; floor++) { + ret = 1; +@@ -515,6 +534,7 @@ + { + struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; + struct DiskOnChip *old = NULL; ++ int maxchips; + + /* We must avoid being called twice for the same device. */ + +@@ -538,14 +558,28 @@ + + + switch (this->ChipID) { ++ case DOC_ChipID_Doc2kTSOP: ++ mtd->name = "DiskOnChip 2000 TSOP"; ++ this->ioreg = DoC_Mil_CDSN_IO; ++ /* Pretend it's a Millennium */ ++ this->ChipID = DOC_ChipID_DocMil; ++ maxchips = MAX_CHIPS; ++ break; + case DOC_ChipID_Doc2k: + mtd->name = "DiskOnChip 2000"; + this->ioreg = DoC_2k_CDSN_IO; ++ maxchips = MAX_CHIPS; + break; + case DOC_ChipID_DocMil: + mtd->name = "DiskOnChip Millennium"; + this->ioreg = DoC_Mil_CDSN_IO; ++ maxchips = MAX_CHIPS_MIL; + break; ++ default: ++ printk("Unknown ChipID 0x%02x\n", this->ChipID); ++ kfree(mtd); ++ iounmap((void *) this->virtadr); ++ return; + } + + printk(KERN_NOTICE "%s found at address 0x%lX\n", mtd->name, +@@ -553,11 +587,12 @@ + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->ecctype = MTD_ECC_RS_DiskOnChip; + mtd->size = 0; + mtd->erasesize = 0; + mtd->oobblock = 512; + mtd->oobsize = 16; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->erase = doc_erase; + mtd->point = NULL; + mtd->unpoint = NULL; +@@ -565,6 +600,7 @@ + mtd->write = doc_write; + mtd->read_ecc = doc_read_ecc; + mtd->write_ecc = doc_write_ecc; ++ mtd->writev_ecc = doc_writev_ecc; + mtd->read_oob = doc_read_oob; + mtd->write_oob = doc_write_oob; + mtd->sync = NULL; +@@ -577,7 +613,7 @@ + init_MUTEX(&this->lock); + + /* Ident all the chips present. */ +- DoC_ScanChips(this); ++ DoC_ScanChips(this, maxchips); + + if (!this->totlen) { + kfree(mtd); +@@ -608,6 +644,7 @@ + unsigned char syndrome[6]; + volatile char dummy; + int i, len256 = 0, ret=0; ++ size_t left = len; + + docptr = this->virtadr; + +@@ -617,6 +654,10 @@ + + down(&this->lock); + ++ *retlen = 0; ++ while (left) { ++ len = left; ++ + /* Don't allow a single read to cross a 512-byte block boundary */ + if (from + len > ((from | 0x1ff) + 1)) + len = ((from | 0x1ff) + 1) - from; +@@ -673,7 +714,7 @@ + DoC_ReadBuf(this, &buf[len256], len - len256); + + /* Let the caller know we completed it */ +- *retlen = len; ++ *retlen += len; + + if (eccbuf) { + /* Read the ECC data through the DiskOnChip ECC logic */ +@@ -730,11 +771,16 @@ + + /* according to 11.4.1, we need to wait for the busy line + * drop if we read to the end of the page. */ +- if(0 == ((from + *retlen) & 0x1ff)) ++ if(0 == ((from + len) & 0x1ff)) + { + DoC_WaitReady(this); + } + ++ from += len; ++ left -= len; ++ buf += len; ++ } ++ + up(&this->lock); + + return ret; +@@ -757,6 +803,8 @@ + volatile char dummy; + int len256 = 0; + struct Nand *mychip; ++ size_t left = len; ++ int status; + + docptr = this->virtadr; + +@@ -766,15 +814,21 @@ + + down(&this->lock); + ++ *retlen = 0; ++ while (left) { ++ len = left; ++ + /* Don't allow a single write to cross a 512-byte block boundary */ + if (to + len > ((to | 0x1ff) + 1)) + len = ((to | 0x1ff) + 1) - to; + + /* The ECC will not be calculated correctly if less than 512 is written */ ++/* DBB- + if (len != 0x200 && eccbuf) + printk(KERN_WARNING + "ECC needs a full sector write (adr: %lx size %lx)\n", + (long) to, (long) len); ++ -DBB */ + + /* printk("DoC_Write (adr: %lx size %lx)\n", (long) to, (long) len); */ + +@@ -853,6 +907,9 @@ + WriteDOC_(0, docptr, this->ioreg); + } + ++ WriteDOC(CDSN_CTRL_ECC_IO | CDSN_CTRL_FLASH_IO | CDSN_CTRL_CE, docptr, ++ CDSNControl); ++ + /* Read the ECC data through the DiskOnChip ECC logic */ + for (di = 0; di < 6; di++) { + eccbuf[di] = ReadDOC(docptr, ECCSyndrome0 + di); +@@ -874,10 +931,16 @@ + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + /* There's an implicit DoC_WaitReady() in DoC_Command */ + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error programming flash\n"); + /* Error in programming */ + *retlen = 0; +@@ -886,7 +949,7 @@ + } + + /* Let the caller know we completed it */ +- *retlen = len; ++ *retlen += len; + + if (eccbuf) { + unsigned char x[8]; +@@ -901,13 +964,81 @@ + x[7]=0x55; + + ret = doc_write_oob_nolock(mtd, to, 8, &dummy, x); ++ if (ret) { + up(&this->lock); + return ret; + } ++ } ++ ++ to += len; ++ left -= len; ++ buf += len; ++ } ++ + up(&this->lock); + return 0; + } + ++static int doc_writev_ecc(struct mtd_info *mtd, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen, ++ u_char *eccbuf, struct nand_oobinfo *oobsel) ++{ ++ static char static_buf[512]; ++ static DECLARE_MUTEX(writev_buf_sem); ++ ++ size_t totretlen = 0; ++ size_t thisvecofs = 0; ++ int ret= 0; ++ ++ down(&writev_buf_sem); ++ ++ while(count) { ++ size_t thislen, thisretlen; ++ unsigned char *buf; ++ ++ buf = vecs->iov_base + thisvecofs; ++ thislen = vecs->iov_len - thisvecofs; ++ ++ ++ if (thislen >= 512) { ++ thislen = thislen & ~(512-1); ++ thisvecofs += thislen; ++ } else { ++ /* Not enough to fill a page. Copy into buf */ ++ memcpy(static_buf, buf, thislen); ++ buf = &static_buf[thislen]; ++ ++ while(count && thislen < 512) { ++ vecs++; ++ count--; ++ thisvecofs = min((512-thislen), vecs->iov_len); ++ memcpy(buf, vecs->iov_base, thisvecofs); ++ thislen += thisvecofs; ++ buf += thisvecofs; ++ } ++ buf = static_buf; ++ } ++ if (count && thisvecofs == vecs->iov_len) { ++ thisvecofs = 0; ++ vecs++; ++ count--; ++ } ++ ret = doc_write_ecc(mtd, to, thislen, &thisretlen, buf, eccbuf, oobsel); ++ ++ totretlen += thisretlen; ++ ++ if (ret || thisretlen != thislen) ++ break; ++ ++ to += thislen; ++ } ++ ++ up(&writev_buf_sem); ++ *retlen = totretlen; ++ return ret; ++} ++ ++ + static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t * retlen, u_char * buf) + { +@@ -977,6 +1108,7 @@ + unsigned long docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + volatile int dummy; ++ int status; + + // printk("doc_write_oob(%lx, %d): %2.2X %2.2X %2.2X %2.2X ... %2.2X %2.2X .. %2.2X %2.2X\n",(long)ofs, len, + // buf[0], buf[1], buf[2], buf[3], buf[8], buf[9], buf[14],buf[15]); +@@ -1025,10 +1157,16 @@ + DoC_Command(this, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error programming oob data\n"); + /* There was an error */ + *retlen = 0; +@@ -1044,10 +1182,16 @@ + DoC_Command(this, NAND_CMD_STATUS, 0); + /* DoC_WaitReady() is implicit in DoC_Command */ + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error programming oob data\n"); + /* There was an error */ + *retlen = 0; +@@ -1080,6 +1224,7 @@ + volatile int dummy; + unsigned long docptr; + struct Nand *mychip; ++ int status; + + down(&this->lock); + +@@ -1111,10 +1256,16 @@ + + DoC_Command(this, NAND_CMD_STATUS, CDSN_CTRL_WP); + ++ if (DoC_is_Millennium(this)) { ++ ReadDOC(docptr, ReadPipeInit); ++ status = ReadDOC(docptr, LastDataRead); ++ } else { + dummy = ReadDOC(docptr, CDSNSlowIO); + DoC_Delay(this, 2); ++ status = ReadDOC_(docptr, this->ioreg); ++ } + +- if (ReadDOC_(docptr, this->ioreg) & 1) { ++ if (status & 1) { + printk(KERN_ERR "Error erasing at 0x%x\n", ofs); + /* There was an error */ + instr->state = MTD_ERASE_FAILED; +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/doc2001.c linux/drivers/mtd/devices/doc2001.c +--- linux-mips-2.4.27/drivers/mtd/devices/doc2001.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/doc2001.c 2004-11-19 10:25:11.835209520 +0100 +@@ -4,7 +4,7 @@ + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> + * +- * $Id: doc2001.c,v 1.38 2002/12/10 15:05:42 gleixner Exp $ ++ * $Id: doc2001.c,v 1.41 2003/06/11 09:45:19 dwmw2 Exp $ + */ + + #include <linux/kernel.h> +@@ -359,14 +359,15 @@ + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->ecctype = MTD_ECC_RS_DiskOnChip; + mtd->size = 0; + +- /* FIXME: erase size is not always 8kB */ ++ /* FIXME: erase size is not always 8KiB */ + mtd->erasesize = 0x2000; + + mtd->oobblock = 512; + mtd->oobsize = 16; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->erase = doc_erase; + mtd->point = NULL; + mtd->unpoint = NULL; +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/doc2001plus.c linux/drivers/mtd/devices/doc2001plus.c +--- linux-mips-2.4.27/drivers/mtd/devices/doc2001plus.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/devices/doc2001plus.c 2004-11-19 10:25:11.837209216 +0100 +@@ -0,0 +1,1154 @@ ++/* ++ * Linux driver for Disk-On-Chip Millennium Plus ++ * ++ * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com> ++ * (c) 2002-2003 SnapGear Inc ++ * (c) 1999 Machine Vision Holdings, Inc. ++ * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> ++ * ++ * $Id: doc2001plus.c,v 1.7 2003/07/11 07:36:22 dwmw2 Exp $ ++ * ++ * Released under GPL ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <asm/errno.h> ++#include <asm/io.h> ++#include <asm/uaccess.h> ++#include <linux/miscdevice.h> ++#include <linux/pci.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/types.h> ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/doc2000.h> ++ ++/* #define ECC_DEBUG */ ++ ++/* I have no idea why some DoC chips can not use memcop_form|to_io(). ++ * This may be due to the different revisions of the ASIC controller built-in or ++ * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment ++ * this:*/ ++#undef USE_MEMCPY ++ ++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf); ++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf); ++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel); ++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel); ++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, u_char *buf); ++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, const u_char *buf); ++static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); ++ ++static struct mtd_info *docmilpluslist = NULL; ++ ++ ++/* Perform the required delay cycles by writing to the NOP register */ ++static void DoC_Delay(unsigned long docptr, int cycles) ++{ ++ int i; ++ ++ for (i = 0; (i < cycles); i++) ++ WriteDOC(0, docptr, Mplus_NOP); ++} ++ ++#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) ++ ++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ ++static int _DoC_WaitReady(unsigned long docptr) ++{ ++ unsigned int c = 0xffff; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, ++ "_DoC_WaitReady called for out-of-line wait\n"); ++ ++ /* Out-of-line routine to wait for chip response */ ++ while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c) ++ ; ++ ++ if (c == 0) ++ DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); ++ ++ return (c == 0); ++} ++ ++static inline int DoC_WaitReady(unsigned long docptr) ++{ ++ /* This is inline, to optimise the common case, where it's ready instantly */ ++ int ret = 0; ++ ++ /* read form NOP register should be issued prior to the read from CDSNControl ++ see Software Requirement 11.4 item 2. */ ++ DoC_Delay(docptr, 4); ++ ++ if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) ++ /* Call the out-of-line routine to wait */ ++ ret = _DoC_WaitReady(docptr); ++ ++ return ret; ++} ++ ++/* For some reason the Millennium Plus seems to occassionally put itself ++ * into reset mode. For me this happens randomly, with no pattern that I ++ * can detect. M-systems suggest always check this on any block level ++ * operation and setting to normal mode if in reset mode. ++ */ ++static inline void DoC_CheckASIC(unsigned long docptr) ++{ ++ /* Make sure the DoC is in normal mode */ ++ if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { ++ WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl); ++ WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm); ++ } ++} ++ ++/* DoC_Command: Send a flash command to the flash chip through the Flash ++ * command register. Need 2 Write Pipeline Terminates to complete send. ++ */ ++static inline void DoC_Command(unsigned long docptr, unsigned char command, ++ unsigned char xtraflags) ++{ ++ WriteDOC(command, docptr, Mplus_FlashCmd); ++ WriteDOC(command, docptr, Mplus_WritePipeTerm); ++ WriteDOC(command, docptr, Mplus_WritePipeTerm); ++} ++ ++/* DoC_Address: Set the current address for the flash chip through the Flash ++ * Address register. Need 2 Write Pipeline Terminates to complete send. ++ */ ++static inline void DoC_Address(struct DiskOnChip *doc, int numbytes, ++ unsigned long ofs, unsigned char xtraflags1, ++ unsigned char xtraflags2) ++{ ++ unsigned long docptr = doc->virtadr; ++ ++ /* Allow for possible Mill Plus internal flash interleaving */ ++ ofs >>= doc->interleave; ++ ++ switch (numbytes) { ++ case 1: ++ /* Send single byte, bits 0-7. */ ++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); ++ break; ++ case 2: ++ /* Send bits 9-16 followed by 17-23 */ ++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); ++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); ++ break; ++ case 3: ++ /* Send 0-7, 9-16, then 17-23 */ ++ WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); ++ WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); ++ WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); ++ break; ++ default: ++ return; ++ } ++ ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++} ++ ++/* DoC_SelectChip: Select a given flash chip within the current floor */ ++static int DoC_SelectChip(unsigned long docptr, int chip) ++{ ++ /* No choice for flash chip on Millennium Plus */ ++ return 0; ++} ++ ++/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ ++static int DoC_SelectFloor(unsigned long docptr, int floor) ++{ ++ WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); ++ return 0; ++} ++ ++/* ++ * Translate the given offset into the appropriate command and offset. ++ * This does the mapping using the 16bit interleave layout defined by ++ * M-Systems, and looks like this for a sector pair: ++ * +-----------+-------+-------+-------+--------------+---------+-----------+ ++ * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055| ++ * +-----------+-------+-------+-------+--------------+---------+-----------+ ++ * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | ++ * +-----------+-------+-------+-------+--------------+---------+-----------+ ++ */ ++/* FIXME: This lives in INFTL not here. Other users of flash devices ++ may not want it */ ++static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ ++ if (this->interleave) { ++ unsigned int ofs = *from & 0x3ff; ++ unsigned int cmd; ++ ++ if (ofs < 512) { ++ cmd = NAND_CMD_READ0; ++ ofs &= 0x1ff; ++ } else if (ofs < 1014) { ++ cmd = NAND_CMD_READ1; ++ ofs = (ofs & 0x1ff) + 10; ++ } else { ++ cmd = NAND_CMD_READOOB; ++ ofs = ofs - 1014; ++ } ++ ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++ } else { ++ /* No interleave */ ++ if ((*from) & 0x100) ++ return NAND_CMD_READ1; ++ return NAND_CMD_READ0; ++ } ++} ++ ++static unsigned int DoC_GetECCOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ unsigned int ofs, cmd; ++ ++ if (*from & 0x200) { ++ cmd = NAND_CMD_READOOB; ++ ofs = 10 + (*from & 0xf); ++ } else { ++ cmd = NAND_CMD_READ1; ++ ofs = (*from & 0xf); ++ } ++ ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++} ++ ++static unsigned int DoC_GetFlagsOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ unsigned int ofs, cmd; ++ ++ cmd = NAND_CMD_READ1; ++ ofs = (*from & 0x200) ? 8 : 6; ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++} ++ ++static unsigned int DoC_GetHdrOffset(struct mtd_info *mtd, loff_t *from) ++{ ++ unsigned int ofs, cmd; ++ ++ cmd = NAND_CMD_READOOB; ++ ofs = (*from & 0x200) ? 24 : 16; ++ *from = (*from & ~0x3ff) | ofs; ++ return cmd; ++} ++ ++static inline void MemReadDOC(unsigned long docptr, unsigned char *buf, int len) ++{ ++#ifndef USE_MEMCPY ++ int i; ++ for (i = 0; i < len; i++) ++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO + i); ++#else ++ memcpy_fromio(buf, docptr + DoC_Mil_CDSN_IO, len); ++#endif ++} ++ ++static inline void MemWriteDOC(unsigned long docptr, unsigned char *buf, int len) ++{ ++#ifndef USE_MEMCPY ++ int i; ++ for (i = 0; i < len; i++) ++ WriteDOC(buf[i], docptr, Mil_CDSN_IO + i); ++#else ++ memcpy_toio(docptr + DoC_Mil_CDSN_IO, buf, len); ++#endif ++} ++ ++/* DoC_IdentChip: Identify a given NAND chip given {floor,chip} */ ++static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) ++{ ++ int mfr, id, i, j; ++ volatile char dummy; ++ unsigned long docptr = doc->virtadr; ++ ++ /* Page in the required floor/chip */ ++ DoC_SelectFloor(docptr, floor); ++ DoC_SelectChip(docptr, chip); ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ /* Read the NAND chip ID: 1. Send ReadID command */ ++ DoC_Command(docptr, NAND_CMD_READID, 0); ++ ++ /* Read the NAND chip ID: 2. Send address byte zero */ ++ DoC_Address(doc, 1, 0x00, 0, 0x00); ++ ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ /* Read the manufacturer and device id codes of the flash device through ++ CDSN IO register see Software Requirement 11.4 item 5.*/ ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ mfr = ReadDOC(docptr, Mil_CDSN_IO); ++ if (doc->interleave) ++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ ++ ++ id = ReadDOC(docptr, Mil_CDSN_IO); ++ if (doc->interleave) ++ dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ ++ ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ /* No response - return failure */ ++ if (mfr == 0xff || mfr == 0) ++ return 0; ++ ++ for (i = 0; nand_flash_ids[i].name != NULL; i++) { ++ if (id == nand_flash_ids[i].id) { ++ /* Try to identify manufacturer */ ++ for (j = 0; nand_manuf_ids[j].id != 0x0; j++) { ++ if (nand_manuf_ids[j].id == mfr) ++ break; ++ } ++ printk(KERN_INFO "Flash chip found: Manufacturer ID: %2.2X, " ++ "Chip ID: %2.2X (%s:%s)\n", mfr, id, ++ nand_manuf_ids[j].name, nand_flash_ids[i].name); ++ doc->mfr = mfr; ++ doc->id = id; ++ doc->chipshift = nand_flash_ids[i].chipshift; ++ doc->erasesize = nand_flash_ids[i].erasesize << doc->interleave; ++ break; ++ } ++ } ++ ++ if (nand_flash_ids[i].name == NULL) ++ return 0; ++ return 1; ++} ++ ++/* DoC_ScanChips: Find all NAND chips present in a DiskOnChip, and identify them */ ++static void DoC_ScanChips(struct DiskOnChip *this) ++{ ++ int floor, chip; ++ int numchips[MAX_FLOORS_MPLUS]; ++ int ret; ++ ++ this->numchips = 0; ++ this->mfr = 0; ++ this->id = 0; ++ ++ /* Work out the intended interleave setting */ ++ this->interleave = 0; ++ if (this->ChipID == DOC_ChipID_DocMilPlus32) ++ this->interleave = 1; ++ ++ /* Check the ASIC agrees */ ++ if ( (this->interleave << 2) != ++ (ReadDOC(this->virtadr, Mplus_Configuration) & 4)) { ++ u_char conf = ReadDOC(this->virtadr, Mplus_Configuration); ++ printk(KERN_NOTICE "Setting DiskOnChip Millennium Plus interleave to %s\n", ++ this->interleave?"on (16-bit)":"off (8-bit)"); ++ conf ^= 4; ++ WriteDOC(this->virtadr, conf, Mplus_Configuration); ++ } ++ ++ /* For each floor, find the number of valid chips it contains */ ++ for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { ++ numchips[floor] = 0; ++ for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) { ++ ret = DoC_IdentChip(this, floor, chip); ++ if (ret) { ++ numchips[floor]++; ++ this->numchips++; ++ } ++ } ++ } ++ /* If there are none at all that we recognise, bail */ ++ if (!this->numchips) { ++ printk("No flash chips recognised.\n"); ++ return; ++ } ++ ++ /* Allocate an array to hold the information for each chip */ ++ this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); ++ if (!this->chips){ ++ printk("MTD: No memory for allocating chip info structures\n"); ++ return; ++ } ++ ++ /* Fill out the chip array with {floor, chipno} for each ++ * detected chip in the device. */ ++ for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) { ++ for (chip = 0 ; chip < numchips[floor] ; chip++) { ++ this->chips[ret].floor = floor; ++ this->chips[ret].chip = chip; ++ this->chips[ret].curadr = 0; ++ this->chips[ret].curmode = 0x50; ++ ret++; ++ } ++ } ++ ++ /* Calculate and print the total size of the device */ ++ this->totlen = this->numchips * (1 << this->chipshift); ++ printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", ++ this->numchips ,this->totlen >> 20); ++} ++ ++static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) ++{ ++ int tmp1, tmp2, retval; ++ ++ if (doc1->physadr == doc2->physadr) ++ return 1; ++ ++ /* Use the alias resolution register which was set aside for this ++ * purpose. If it's value is the same on both chips, they might ++ * be the same chip, and we write to one and check for a change in ++ * the other. It's unclear if this register is usuable in the ++ * DoC 2000 (it's in the Millennium docs), but it seems to work. */ ++ tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution); ++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); ++ if (tmp1 != tmp2) ++ return 0; ++ ++ WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution); ++ tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); ++ if (tmp2 == (tmp1+1) % 0xff) ++ retval = 1; ++ else ++ retval = 0; ++ ++ /* Restore register contents. May not be necessary, but do it just to ++ * be safe. */ ++ WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution); ++ ++ return retval; ++} ++ ++static const char im_name[] = "DoCMilPlus_init"; ++ ++/* This routine is made available to other mtd code via ++ * inter_module_register. It must only be accessed through ++ * inter_module_get which will bump the use count of this module. The ++ * addresses passed back in mtd are valid as long as the use count of ++ * this module is non-zero, i.e. between inter_module_get and ++ * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. ++ */ ++static void DoCMilPlus_init(struct mtd_info *mtd) ++{ ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ struct DiskOnChip *old = NULL; ++ ++ /* We must avoid being called twice for the same device. */ ++ if (docmilpluslist) ++ old = (struct DiskOnChip *)docmilpluslist->priv; ++ ++ while (old) { ++ if (DoCMilPlus_is_alias(this, old)) { ++ printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " ++ "Plus at 0x%lX - already configured\n", ++ this->physadr); ++ iounmap((void *)this->virtadr); ++ kfree(mtd); ++ return; ++ } ++ if (old->nextdoc) ++ old = (struct DiskOnChip *)old->nextdoc->priv; ++ else ++ old = NULL; ++ } ++ ++ mtd->name = "DiskOnChip Millennium Plus"; ++ printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " ++ "address 0x%lX\n", this->physadr); ++ ++ mtd->type = MTD_NANDFLASH; ++ mtd->flags = MTD_CAP_NANDFLASH; ++ mtd->ecctype = MTD_ECC_RS_DiskOnChip; ++ mtd->size = 0; ++ ++ mtd->erasesize = 0; ++ mtd->oobblock = 512; ++ mtd->oobsize = 16; ++ mtd->owner = THIS_MODULE; ++ mtd->erase = doc_erase; ++ mtd->point = NULL; ++ mtd->unpoint = NULL; ++ mtd->read = doc_read; ++ mtd->write = doc_write; ++ mtd->read_ecc = doc_read_ecc; ++ mtd->write_ecc = doc_write_ecc; ++ mtd->read_oob = doc_read_oob; ++ mtd->write_oob = doc_write_oob; ++ mtd->sync = NULL; ++ ++ this->totlen = 0; ++ this->numchips = 0; ++ this->curfloor = -1; ++ this->curchip = -1; ++ ++ /* Ident all the chips present. */ ++ DoC_ScanChips(this); ++ ++ if (!this->totlen) { ++ kfree(mtd); ++ iounmap((void *)this->virtadr); ++ } else { ++ this->nextdoc = docmilpluslist; ++ docmilpluslist = mtd; ++ mtd->size = this->totlen; ++ mtd->erasesize = this->erasesize; ++ add_mtd_device(mtd); ++ return; ++ } ++} ++ ++#if 0 ++static int doc_dumpblk(struct mtd_info *mtd, loff_t from) ++{ ++ int i; ++ loff_t fofs; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[from >> (this->chipshift)]; ++ unsigned char *bp, buf[1056]; ++ char c[32]; ++ ++ from &= ~0x3ff; ++ ++ /* Don't allow read past end of device */ ++ if (from >= this->totlen) ++ return -EINVAL; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ fofs = from; ++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ /* Read the data via the internal pipeline through CDSN IO ++ register, see Pipelined Read Operations 11.3 */ ++ MemReadDOC(docptr, buf, 1054); ++ buf[1054] = ReadDOC(docptr, Mplus_LastDataRead); ++ buf[1055] = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ memset(&c[0], 0, sizeof(c)); ++ printk("DUMP OFFSET=%x:\n", (int)from); ++ ++ for (i = 0, bp = &buf[0]; (i < 1056); i++) { ++ if ((i % 16) == 0) ++ printk("%08x: ", i); ++ printk(" %02x", *bp); ++ c[(i & 0xf)] = ((*bp >= 0x20) && (*bp <= 0x7f)) ? *bp : '.'; ++ bp++; ++ if (((i + 1) % 16) == 0) ++ printk(" %s\n", c); ++ } ++ printk("\n"); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ return 0; ++} ++#endif ++ ++static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ /* Just a special case of doc_read_ecc */ ++ return doc_read_ecc(mtd, from, len, retlen, buf, NULL, NULL); ++} ++ ++static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel) ++{ ++ int ret, i; ++ volatile char dummy; ++ loff_t fofs; ++ unsigned char syndrome[6]; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[from >> (this->chipshift)]; ++ ++ /* Don't allow read past end of device */ ++ if (from >= this->totlen) ++ return -EINVAL; ++ ++ /* Don't allow a single read to cross a 512-byte block boundary */ ++ if (from + len > ((from | 0x1ff) + 1)) ++ len = ((from | 0x1ff) + 1) - from; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ fofs = from; ++ DoC_Command(docptr, DoC_GetDataOffset(mtd, &fofs), 0); ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ if (eccbuf) { ++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ WriteDOC(DOC_ECC_EN, docptr, Mplus_ECCConf); ++ } else { ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ } ++ ++ /* Let the caller know we completed it */ ++ *retlen = len; ++ ret = 0; ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ++ if (eccbuf) { ++ /* Read the data via the internal pipeline through CDSN IO ++ register, see Pipelined Read Operations 11.3 */ ++ MemReadDOC(docptr, buf, len); ++ ++ /* Read the ECC data following raw data */ ++ MemReadDOC(docptr, eccbuf, 4); ++ eccbuf[4] = ReadDOC(docptr, Mplus_LastDataRead); ++ eccbuf[5] = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Flush the pipeline */ ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ dummy = ReadDOC(docptr, Mplus_ECCConf); ++ ++ /* Check the ECC Status */ ++ if (ReadDOC(docptr, Mplus_ECCConf) & 0x80) { ++ int nb_errors; ++ /* There was an ECC error */ ++#ifdef ECC_DEBUG ++ printk("DiskOnChip ECC Error: Read at %lx\n", (long)from); ++#endif ++ /* Read the ECC syndrom through the DiskOnChip ECC logic. ++ These syndrome will be all ZERO when there is no error */ ++ for (i = 0; i < 6; i++) ++ syndrome[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); ++ ++ nb_errors = doc_decode_ecc(buf, syndrome); ++#ifdef ECC_DEBUG ++ printk("ECC Errors corrected: %x\n", nb_errors); ++#endif ++ if (nb_errors < 0) { ++ /* We return error, but have actually done the read. Not that ++ this can be told to user-space, via sys_read(), but at least ++ MTD-aware stuff can know about it by checking *retlen */ ++#ifdef ECC_DEBUG ++ printk("%s(%d): Millennium Plus ECC error (from=0x%x:\n", ++ __FILE__, __LINE__, (int)from); ++ printk(" syndrome= %02x:%02x:%02x:%02x:%02x:" ++ "%02x\n", ++ syndrome[0], syndrome[1], syndrome[2], ++ syndrome[3], syndrome[4], syndrome[5]); ++ printk(" eccbuf= %02x:%02x:%02x:%02x:%02x:" ++ "%02x\n", ++ eccbuf[0], eccbuf[1], eccbuf[2], ++ eccbuf[3], eccbuf[4], eccbuf[5]); ++#endif ++ ret = -EIO; ++ } ++ } ++ ++#ifdef PSYCHO_DEBUG ++ printk("ECC DATA at %lx: %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", ++ (long)from, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], ++ eccbuf[4], eccbuf[5]); ++#endif ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_DIS, docptr , Mplus_ECCConf); ++ } else { ++ /* Read the data via the internal pipeline through CDSN IO ++ register, see Pipelined Read Operations 11.3 */ ++ MemReadDOC(docptr, buf, len-2); ++ buf[len-2] = ReadDOC(docptr, Mplus_LastDataRead); ++ buf[len-1] = ReadDOC(docptr, Mplus_LastDataRead); ++ } ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ return ret; ++} ++ ++static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ char eccbuf[6]; ++ return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, NULL); ++} ++ ++static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf, u_char *eccbuf, ++ struct nand_oobinfo *oobsel) ++{ ++ int i, before, ret = 0; ++ loff_t fto; ++ volatile char dummy; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[to >> (this->chipshift)]; ++ ++ /* Don't allow write past end of device */ ++ if (to >= this->totlen) ++ return -EINVAL; ++ ++ /* Don't allow writes which aren't exactly one block (512 bytes) */ ++ if ((to & 0x1ff) || (len != 0x200)) ++ return -EINVAL; ++ ++ /* Determine position of OOB flags, before or after data */ ++ before = (this->interleave && (to & 0x200)); ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); ++ ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ /* Set device to appropriate plane of flash */ ++ fto = to; ++ WriteDOC(DoC_GetDataOffset(mtd, &fto), docptr, Mplus_FlashCmd); ++ ++ /* On interleaved devices the flags for 2nd half 512 are before data */ ++ if (eccbuf && before) ++ fto -= 2; ++ ++ /* issue the Serial Data In command to initial the Page Program process */ ++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); ++ DoC_Address(this, 3, fto, 0x00, 0x00); ++ ++ /* Disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ ++ if (eccbuf) { ++ if (before) { ++ /* Write the block status BLOCK_USED (0x5555) */ ++ WriteDOC(0x55, docptr, Mil_CDSN_IO); ++ WriteDOC(0x55, docptr, Mil_CDSN_IO); ++ } ++ ++ /* init the ECC engine, see Reed-Solomon EDC/ECC 11.1 .*/ ++ WriteDOC(DOC_ECC_EN | DOC_ECC_RW, docptr, Mplus_ECCConf); ++ } ++ ++ MemWriteDOC(docptr, (unsigned char *) buf, len); ++ ++ if (eccbuf) { ++ /* Write ECC data to flash, the ECC info is generated by ++ the DiskOnChip ECC logic see Reed-Solomon EDC/ECC 11.1 */ ++ DoC_Delay(docptr, 3); ++ ++ /* Read the ECC data through the DiskOnChip ECC logic */ ++ for (i = 0; i < 6; i++) ++ eccbuf[i] = ReadDOC(docptr, Mplus_ECCSyndrome0 + i); ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_DIS, docptr, Mplus_ECCConf); ++ ++ /* Write the ECC data to flash */ ++ MemWriteDOC(docptr, eccbuf, 6); ++ ++ if (!before) { ++ /* Write the block status BLOCK_USED (0x5555) */ ++ WriteDOC(0x55, docptr, Mil_CDSN_IO+6); ++ WriteDOC(0x55, docptr, Mil_CDSN_IO+7); ++ } ++ ++#ifdef PSYCHO_DEBUG ++ printk("OOB data at %lx is %2.2X %2.2X %2.2X %2.2X %2.2X %2.2X\n", ++ (long) to, eccbuf[0], eccbuf[1], eccbuf[2], eccbuf[3], ++ eccbuf[4], eccbuf[5]); ++#endif ++ } ++ ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ ++ /* Commit the Page Program command and wait for ready ++ see Software Requirement 11.4 item 1.*/ ++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); ++ DoC_WaitReady(docptr); ++ ++ /* Read the status of the flash device through CDSN IO register ++ see Software Requirement 11.4 item 5.*/ ++ DoC_Command(docptr, NAND_CMD_STATUS, 0); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ DoC_Delay(docptr, 2); ++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { ++ printk("MTD: Error 0x%x programming at 0x%x\n", dummy, (int)to); ++ /* Error in programming ++ FIXME: implement Bad Block Replacement (in nftl.c ??) */ ++ *retlen = 0; ++ ret = -EIO; ++ } ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ /* Let the caller know we completed it */ ++ *retlen = len; ++ ++ return ret; ++} ++ ++static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ loff_t fofs, base; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[ofs >> this->chipshift]; ++ size_t i, size, got, want; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC((DOC_FLASH_CE | DOC_FLASH_WP), docptr, Mplus_FlashSelect); ++ ++ /* disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ DoC_WaitReady(docptr); ++ ++ /* Maximum of 16 bytes in the OOB region, so limit read to that */ ++ if (len > 16) ++ len = 16; ++ got = 0; ++ want = len; ++ ++ for (i = 0; ((i < 3) && (want > 0)); i++) { ++ /* Figure out which region we are accessing... */ ++ fofs = ofs; ++ base = ofs & 0xf; ++ if (!this->interleave) { ++ DoC_Command(docptr, NAND_CMD_READOOB, 0); ++ size = 16 - base; ++ } else if (base < 6) { ++ DoC_Command(docptr, DoC_GetECCOffset(mtd, &fofs), 0); ++ size = 6 - base; ++ } else if (base < 8) { ++ DoC_Command(docptr, DoC_GetFlagsOffset(mtd, &fofs), 0); ++ size = 8 - base; ++ } else { ++ DoC_Command(docptr, DoC_GetHdrOffset(mtd, &fofs), 0); ++ size = 16 - base; ++ } ++ if (size > want) ++ size = want; ++ ++ /* Issue read command */ ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ WriteDOC(0, docptr, Mplus_FlashControl); ++ DoC_WaitReady(docptr); ++ ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ ReadDOC(docptr, Mplus_ReadPipeInit); ++ MemReadDOC(docptr, &buf[got], size - 2); ++ buf[got + size - 2] = ReadDOC(docptr, Mplus_LastDataRead); ++ buf[got + size - 1] = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ ofs += size; ++ got += size; ++ want -= size; ++ } ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ *retlen = len; ++ return 0; ++} ++ ++static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ volatile char dummy; ++ loff_t fofs, base; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[ofs >> this->chipshift]; ++ size_t i, size, got, want; ++ int ret = 0; ++ ++ DoC_CheckASIC(docptr); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); ++ ++ ++ /* Maximum of 16 bytes in the OOB region, so limit write to that */ ++ if (len > 16) ++ len = 16; ++ got = 0; ++ want = len; ++ ++ for (i = 0; ((i < 3) && (want > 0)); i++) { ++ /* Reset the chip, see Software Requirement 11.4 item 1. */ ++ DoC_Command(docptr, NAND_CMD_RESET, 0); ++ DoC_WaitReady(docptr); ++ ++ /* Figure out which region we are accessing... */ ++ fofs = ofs; ++ base = ofs & 0x0f; ++ if (!this->interleave) { ++ WriteDOC(NAND_CMD_READOOB, docptr, Mplus_FlashCmd); ++ size = 16 - base; ++ } else if (base < 6) { ++ WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); ++ size = 6 - base; ++ } else if (base < 8) { ++ WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); ++ size = 8 - base; ++ } else { ++ WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); ++ size = 16 - base; ++ } ++ if (size > want) ++ size = want; ++ ++ /* Issue the Serial Data In command to initial the Page Program process */ ++ DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); ++ DoC_Address(this, 3, fofs, 0, 0x00); ++ ++ /* Disable the ECC engine */ ++ WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); ++ ++ /* Write the data via the internal pipeline through CDSN IO ++ register, see Pipelined Write Operations 11.2 */ ++ MemWriteDOC(docptr, (unsigned char *) &buf[got], size); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ WriteDOC(0x00, docptr, Mplus_WritePipeTerm); ++ ++ /* Commit the Page Program command and wait for ready ++ see Software Requirement 11.4 item 1.*/ ++ DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); ++ DoC_WaitReady(docptr); ++ ++ /* Read the status of the flash device through CDSN IO register ++ see Software Requirement 11.4 item 5.*/ ++ DoC_Command(docptr, NAND_CMD_STATUS, 0x00); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ DoC_Delay(docptr, 2); ++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { ++ printk("MTD: Error 0x%x programming oob at 0x%x\n", ++ dummy, (int)ofs); ++ /* FIXME: implement Bad Block Replacement */ ++ *retlen = 0; ++ ret = -EIO; ++ } ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ ofs += size; ++ got += size; ++ want -= size; ++ } ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ *retlen = len; ++ return ret; ++} ++ ++int doc_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ volatile char dummy; ++ struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; ++ __u32 ofs = instr->addr; ++ __u32 len = instr->len; ++ unsigned long docptr = this->virtadr; ++ struct Nand *mychip = &this->chips[ofs >> this->chipshift]; ++ ++ DoC_CheckASIC(docptr); ++ ++ if (len != mtd->erasesize) ++ printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n", ++ len, mtd->erasesize); ++ ++ /* Find the chip which is to be used and select it */ ++ if (this->curfloor != mychip->floor) { ++ DoC_SelectFloor(docptr, mychip->floor); ++ DoC_SelectChip(docptr, mychip->chip); ++ } else if (this->curchip != mychip->chip) { ++ DoC_SelectChip(docptr, mychip->chip); ++ } ++ this->curfloor = mychip->floor; ++ this->curchip = mychip->chip; ++ ++ instr->state = MTD_ERASE_PENDING; ++ ++ /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ ++ WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); ++ ++ DoC_Command(docptr, NAND_CMD_RESET, 0x00); ++ DoC_WaitReady(docptr); ++ ++ DoC_Command(docptr, NAND_CMD_ERASE1, 0); ++ DoC_Address(this, 2, ofs, 0, 0x00); ++ DoC_Command(docptr, NAND_CMD_ERASE2, 0); ++ DoC_WaitReady(docptr); ++ instr->state = MTD_ERASING; ++ ++ /* Read the status of the flash device through CDSN IO register ++ see Software Requirement 11.4 item 5. */ ++ DoC_Command(docptr, NAND_CMD_STATUS, 0); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ dummy = ReadDOC(docptr, Mplus_ReadPipeInit); ++ if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { ++ printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); ++ /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ ++ instr->state = MTD_ERASE_FAILED; ++ } else { ++ instr->state = MTD_ERASE_DONE; ++ } ++ dummy = ReadDOC(docptr, Mplus_LastDataRead); ++ ++ /* Disable flash internally */ ++ WriteDOC(0, docptr, Mplus_FlashSelect); ++ ++ if (instr->callback) ++ instr->callback(instr); ++ ++ return 0; ++} ++ ++/**************************************************************************** ++ * ++ * Module stuff ++ * ++ ****************************************************************************/ ++ ++int __init init_doc2001plus(void) ++{ ++ inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init); ++ return 0; ++} ++ ++static void __exit cleanup_doc2001plus(void) ++{ ++ struct mtd_info *mtd; ++ struct DiskOnChip *this; ++ ++ while ((mtd=docmilpluslist)) { ++ this = (struct DiskOnChip *)mtd->priv; ++ docmilpluslist = this->nextdoc; ++ ++ del_mtd_device(mtd); ++ ++ iounmap((void *)this->virtadr); ++ kfree(this->chips); ++ kfree(mtd); ++ } ++ inter_module_unregister(im_name); ++} ++ ++module_exit(cleanup_doc2001plus); ++module_init(init_doc2001plus); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al."); ++MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/docecc.c linux/drivers/mtd/devices/docecc.c +--- linux-mips-2.4.27/drivers/mtd/devices/docecc.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/devices/docecc.c 2004-11-19 10:25:11.851207088 +0100 +@@ -7,7 +7,7 @@ + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * +- * $Id: docecc.c,v 1.4 2001/10/02 15:05:13 dwmw2 Exp $ ++ * $Id: docecc.c,v 1.5 2003/05/21 15:15:06 dwmw2 Exp $ + * + * 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 +@@ -519,6 +519,8 @@ + return nb_errors; + } + ++EXPORT_SYMBOL_GPL(doc_decode_ecc); ++ + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Fabrice Bellard <fabrice.bellard@netgem.com>"); + MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/docprobe.c linux/drivers/mtd/devices/docprobe.c +--- linux-mips-2.4.27/drivers/mtd/devices/docprobe.c 2003-06-16 01:42:21.000000000 +0200 ++++ linux/drivers/mtd/devices/docprobe.c 2004-11-19 10:25:11.853206784 +0100 +@@ -4,7 +4,7 @@ + /* (C) 1999 Machine Vision Holdings, Inc. */ + /* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */ + +-/* $Id: docprobe.c,v 1.33 2003/01/24 14:02:47 dwmw2 Exp $ */ ++/* $Id: docprobe.c,v 1.41 2003/12/03 10:19:57 dwmw2 Exp $ */ + + + +@@ -31,14 +31,12 @@ + /* DOC_SINGLE_DRIVER: + Millennium driver has been merged into DOC2000 driver. + +- The newly-merged driver doesn't appear to work for writing. It's the +- same with the DiskOnChip 2000 and the Millennium. If you have a +- Millennium and you want write support to work, remove the definition +- of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver. +- +- Otherwise, it's left on in the hope that it'll annoy someone with +- a Millennium enough that they go through and work out what the +- difference is :) ++ The old Millennium-only driver has been retained just in case there ++ are problems with the new code. If the combined driver doesn't work ++ for you, you can try the old one by undefining DOC_SINGLE_DRIVER ++ below and also enabling it in your configuration. If this fixes the ++ problems, please send a report to the MTD mailing list at ++ <linux-mtd@lists.infradead.org>. + */ + #define DOC_SINGLE_DRIVER + +@@ -47,18 +45,15 @@ + #include <linux/module.h> + #include <asm/errno.h> + #include <asm/io.h> +-#include <asm/uaccess.h> +-#include <linux/miscdevice.h> +-#include <linux/pci.h> + #include <linux/delay.h> + #include <linux/slab.h> +-#include <linux/sched.h> + #include <linux/init.h> + #include <linux/types.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/nand.h> + #include <linux/mtd/doc2000.h> ++#include <linux/mtd/compatmac.h> + + /* Where to look for the devices? */ + #ifndef CONFIG_MTD_DOCPROBE_ADDRESS +@@ -92,17 +87,17 @@ + 0xff000000, + #elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C) + 0xff000000, +-#else ++##else + #warning Unknown architecture for DiskOnChip. No default probe locations defined + #endif +- 0 }; ++ 0xffffffff }; + + /* doccheck: Probe a given memory window to see if there's a DiskOnChip present */ + + static inline int __init doccheck(unsigned long potential, unsigned long physadr) + { + unsigned long window=potential; +- unsigned char tmp, ChipID; ++ unsigned char tmp, tmpb, tmpc, ChipID; + #ifndef DOC_PASSIVE_PROBE + unsigned char tmp2; + #endif +@@ -140,26 +135,80 @@ + window, DOCControl); + #endif /* !DOC_PASSIVE_PROBE */ + ++ /* We need to read the ChipID register four times. For some ++ newer DiskOnChip 2000 units, the first three reads will ++ return the DiskOnChip Millennium ident. Don't ask. */ + ChipID = ReadDOC(window, ChipID); + + switch (ChipID) { + case DOC_ChipID_Doc2k: + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; +- if ((ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT) != tmp) ++ tmpb = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; ++ if (tmp != tmpb && tmp == tmpc) + return ChipID; + break; + + case DOC_ChipID_DocMil: ++ /* Check for the new 2000 with Millennium ASIC */ ++ ReadDOC(window, ChipID); ++ ReadDOC(window, ChipID); ++ if (ReadDOC(window, ChipID) != DOC_ChipID_DocMil) ++ ChipID = DOC_ChipID_Doc2kTSOP; ++ + /* Check the TOGGLE bit in the ECC register */ + tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; +- if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp) ++ tmpb = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; ++ if (tmp != tmpb && tmp == tmpc) ++ return ChipID; ++ break; ++ ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ case 0: ++ /* Possible Millennium+, need to do more checks */ ++#ifndef DOC_PASSIVE_PROBE ++ /* Possibly release from power down mode */ ++ for (tmp = 0; (tmp < 4); tmp++) ++ ReadDOC(window, Mplus_Power); ++ ++ /* Reset the DiskOnChip ASIC */ ++ tmp = DOC_MODE_RESET | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | ++ DOC_MODE_BDECT; ++ WriteDOC(tmp, window, Mplus_DOCControl); ++ WriteDOC(~tmp, window, Mplus_CtrlConfirm); ++ ++ mdelay(1); ++ /* Enable the DiskOnChip ASIC */ ++ tmp = DOC_MODE_NORMAL | DOC_MODE_MDWREN | DOC_MODE_RST_LAT | ++ DOC_MODE_BDECT; ++ WriteDOC(tmp, window, Mplus_DOCControl); ++ WriteDOC(~tmp, window, Mplus_CtrlConfirm); ++ mdelay(1); ++#endif /* !DOC_PASSIVE_PROBE */ ++ ++ ChipID = ReadDOC(window, ChipID); ++ ++ switch (ChipID) { ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ /* Check the TOGGLE bit in the toggle register */ ++ tmp = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; ++ tmpb = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; ++ tmpc = ReadDOC(window, Mplus_Toggle) & DOC_TOGGLE_BIT; ++ if (tmp != tmpb && tmp == tmpc) + return ChipID; ++ default: + break; ++ } ++ /* FALL TRHU */ + + default: +-#ifndef CONFIG_MTD_DOCPROBE_55AA +- printk(KERN_WARNING "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", ++ ++#ifdef CONFIG_MTD_DOCPROBE_55AA ++ printk(KERN_DEBUG "Possible DiskOnChip with unknown ChipID %2.2X found at 0x%lx\n", + ChipID, physadr); + #endif + #ifndef DOC_PASSIVE_PROBE +@@ -200,6 +249,12 @@ + return; + + if ((ChipID = doccheck(docptr, physadr))) { ++ if (ChipID == DOC_ChipID_Doc2kTSOP) { ++ /* Remove this at your own peril. The hardware driver works but nothing prevents you from erasing bad blocks */ ++ printk(KERN_NOTICE "Refusing to drive DiskOnChip 2000 TSOP until Bad Block Table is correctly supported by INFTL\n"); ++ iounmap((void *)docptr); ++ return; ++ } + docfound = 1; + mtd = kmalloc(sizeof(struct DiskOnChip) + sizeof(struct mtd_info), GFP_KERNEL); + +@@ -221,6 +276,12 @@ + sprintf(namebuf, "with ChipID %2.2X", ChipID); + + switch(ChipID) { ++ case DOC_ChipID_Doc2kTSOP: ++ name="2000 TSOP"; ++ im_funcname = "DoC2k_init"; ++ im_modname = "doc2000"; ++ break; ++ + case DOC_ChipID_Doc2k: + name="2000"; + im_funcname = "DoC2k_init"; +@@ -237,6 +298,13 @@ + im_modname = "doc2001"; + #endif /* DOC_SINGLE_DRIVER */ + break; ++ ++ case DOC_ChipID_DocMilPlus16: ++ case DOC_ChipID_DocMilPlus32: ++ name="MillenniumPlus"; ++ im_funcname = "DoCMilPlus_init"; ++ im_modname = "doc2001plus"; ++ break; + } + + if (im_funcname) +@@ -248,6 +316,7 @@ + return; + } + printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr); ++ kfree(mtd); + } + iounmap((void *)docptr); + } +@@ -267,7 +336,7 @@ + printk(KERN_INFO "Using configured DiskOnChip probe address 0x%lx\n", doc_config_location); + DoC_Probe(doc_config_location); + } else { +- for (i=0; doc_locations[i]; i++) { ++ for (i=0; (doc_locations[i] != 0xffffffff); i++) { + DoC_Probe(doc_locations[i]); + } + } +@@ -275,11 +344,7 @@ + found, so the user knows we at least tried. */ + if (!docfound) + printk(KERN_INFO "No recognised DiskOnChip devices found\n"); +- /* So it looks like we've been used and we get unloaded */ +- MOD_INC_USE_COUNT; +- MOD_DEC_USE_COUNT; +- return 0; +- ++ return -EAGAIN; + } + + module_init(init_doc); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/lart.c linux/drivers/mtd/devices/lart.c +--- linux-mips-2.4.27/drivers/mtd/devices/lart.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/devices/lart.c 2004-11-19 10:25:11.854206632 +0100 +@@ -2,7 +2,7 @@ + /* + * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. + * +- * $Id: lart.c,v 1.2 2001/10/02 15:05:13 dwmw2 Exp $ ++ * $Id: lart.c,v 1.5 2003/05/20 21:03:07 dwmw2 Exp $ + * + * Author: Abraham vd Merwe <abraham@2d3d.co.za> + * +@@ -584,45 +584,40 @@ + + static struct mtd_info mtd; + +-static struct mtd_erase_region_info erase_regions[] = +-{ ++static struct mtd_erase_region_info erase_regions[] = { + /* parameter blocks */ + { +- offset: 0x00000000, +- erasesize: FLASH_BLOCKSIZE_PARAM, +- numblocks: FLASH_NUMBLOCKS_16m_PARAM ++ .offset = 0x00000000, ++ .erasesize = FLASH_BLOCKSIZE_PARAM, ++ .numblocks = FLASH_NUMBLOCKS_16m_PARAM, + }, + /* main blocks */ + { +- offset: FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM, +- erasesize: FLASH_BLOCKSIZE_MAIN, +- numblocks: FLASH_NUMBLOCKS_16m_MAIN ++ .offset = FLASH_BLOCKSIZE_PARAM * FLASH_NUMBLOCKS_16m_PARAM, ++ .erasesize = FLASH_BLOCKSIZE_MAIN, ++ .numblocks = FLASH_NUMBLOCKS_16m_MAIN, + } + }; + + #ifdef HAVE_PARTITIONS +-static struct mtd_partition lart_partitions[] = +-{ ++static struct mtd_partition lart_partitions[] = { + /* blob */ + { +- name: "blob", +- offset: BLOB_START, +- size: BLOB_LEN, +- mask_flags: 0 ++ .name = "blob", ++ .offset = BLOB_START, ++ .size = BLOB_LEN, + }, + /* kernel */ + { +- name: "kernel", +- offset: KERNEL_START, /* MTDPART_OFS_APPEND */ +- size: KERNEL_LEN, +- mask_flags: 0 ++ .name = "kernel", ++ .offset = KERNEL_START, /* MTDPART_OFS_APPEND */ ++ .size = KERNEL_LEN, + }, + /* initial ramdisk / file system */ + { +- name: "file system", +- offset: INITRD_START, /* MTDPART_OFS_APPEND */ +- size: INITRD_LEN, /* MTDPART_SIZ_FULL */ +- mask_flags: 0 ++ .name = "file system", ++ .offset = INITRD_START, /* MTDPART_OFS_APPEND */ ++ .size = INITRD_LEN, /* MTDPART_SIZ_FULL */ + } + }; + #endif +@@ -646,10 +641,10 @@ + mtd.erasesize = FLASH_BLOCKSIZE_MAIN; + mtd.numeraseregions = NB_OF (erase_regions); + mtd.eraseregions = erase_regions; +- mtd.module = THIS_MODULE; + mtd.erase = flash_erase; + mtd.read = flash_read; + mtd.write = flash_write; ++ mtd.owner = THIS_MODULE; + + #ifdef LART_DEBUG + printk (KERN_DEBUG +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/ms02-nv.c linux/drivers/mtd/devices/ms02-nv.c +--- linux-mips-2.4.27/drivers/mtd/devices/ms02-nv.c 2004-07-30 12:22:40.000000000 +0200 ++++ linux/drivers/mtd/devices/ms02-nv.c 2004-11-19 10:25:11.856206328 +0100 +@@ -6,7 +6,7 @@ + * as published by the Free Software Foundation; either version + * 2 of the License, or (at your option) any later version. + * +- * $Id: ms02-nv.c,v 1.2 2003/01/24 14:05:17 dwmw2 Exp $ ++ * $Id: ms02-nv.c,v 1.6 2003/08/19 09:25:36 dwmw2 Exp $ + */ + + #include <linux/init.h> +@@ -31,7 +31,7 @@ + static char version[] __initdata = + "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n"; + +-MODULE_AUTHOR("Maciej W. Rozycki <macro@linux-mips.org>"); ++MODULE_AUTHOR("Maciej W. Rozycki <macro@ds2.pg.gda.pl>"); + MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver"); + MODULE_LICENSE("GPL"); + +@@ -222,7 +222,7 @@ + mtd->flags = MTD_CAP_RAM | MTD_XIP; + mtd->size = fixsize; + mtd->name = (char *)ms02nv_name; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->read = ms02nv_read; + mtd->write = ms02nv_write; + +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/mtdram.c linux/drivers/mtd/devices/mtdram.c +--- linux-mips-2.4.27/drivers/mtd/devices/mtdram.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/mtdram.c 2004-11-19 10:25:11.859205872 +0100 +@@ -1,6 +1,6 @@ + /* + * mtdram - a test mtd device +- * $Id: mtdram.c,v 1.29 2002/10/21 13:40:06 jocke Exp $ ++ * $Id: mtdram.c,v 1.32 2003/05/21 15:15:07 dwmw2 Exp $ + * Author: Alexander Larsson <alex@cendio.se> + * + * Copyright (c) 1999 Alexander Larsson <alex@cendio.se> +@@ -13,6 +13,8 @@ + #include <linux/module.h> + #include <linux/slab.h> + #include <linux/ioport.h> ++#include <linux/vmalloc.h> ++#include <linux/init.h> + #include <linux/mtd/compatmac.h> + #include <linux/mtd/mtd.h> + +@@ -136,7 +138,7 @@ + mtd->erasesize = MTDRAM_ERASE_SIZE; + mtd->priv = mapped_address; + +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->erase = ram_erase; + mtd->point = ram_point; + mtd->unpoint = ram_unpoint; +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/phram.c linux/drivers/mtd/devices/phram.c +--- linux-mips-2.4.27/drivers/mtd/devices/phram.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/devices/phram.c 2004-11-19 10:25:11.860205720 +0100 +@@ -0,0 +1,362 @@ ++/** ++ * ++ * $Id: phram.c,v 1.1 2003/08/21 17:52:30 joern Exp $ ++ * ++ * Copyright (c) Jochen Schaeuble <psionic@psionic.de> ++ * 07/2003 rewritten by Joern Engel <joern@wh.fh-wedel.de> ++ * ++ * DISCLAIMER: This driver makes use of Rusty's excellent module code, ++ * so it will not work for 2.4 without changes and it wont work for 2.4 ++ * as a module without major changes. Oh well! ++ * ++ * Usage: ++ * ++ * one commend line parameter per device, each in the form: ++ * phram=<name>,<start>,<len> ++ * <name> may be up to 63 characters. ++ * <start> and <len> can be octal, decimal or hexadecimal. If followed ++ * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or ++ * gigabytes. ++ * ++ */ ++ ++#include <asm/io.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/list.h> ++#include <linux/module.h> ++#include <linux/moduleparam.h> ++#include <linux/mtd/mtd.h> ++ ++#define ERROR(fmt, args...) printk(KERN_ERR "phram: " fmt , ## args) ++ ++struct phram_mtd_list { ++ struct list_head list; ++ struct mtd_info *mtdinfo; ++}; ++ ++static LIST_HEAD(phram_list); ++ ++ ++ ++int phram_erase(struct mtd_info *mtd, struct erase_info *instr) ++{ ++ u_char *start = (u_char *)mtd->priv; ++ ++ if (instr->addr + instr->len > mtd->size) ++ return -EINVAL; ++ ++ memset(start + instr->addr, 0xff, instr->len); ++ ++ /* This'll catch a few races. Free the thing before returning :) ++ * I don't feel at all ashamed. This kind of thing is possible anyway ++ * with flash, but unlikely. ++ */ ++ ++ instr->state = MTD_ERASE_DONE; ++ ++ if (instr->callback) ++ (*(instr->callback))(instr); ++ else ++ kfree(instr); ++ ++ return 0; ++} ++ ++int phram_point(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char **mtdbuf) ++{ ++ u_char *start = (u_char *)mtd->priv; ++ ++ if (from + len > mtd->size) ++ return -EINVAL; ++ ++ *mtdbuf = start + from; ++ *retlen = len; ++ return 0; ++} ++ ++void phram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) ++{ ++} ++ ++int phram_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t *retlen, u_char *buf) ++{ ++ u_char *start = (u_char *)mtd->priv; ++ ++ if (from + len > mtd->size) ++ return -EINVAL; ++ ++ memcpy(buf, start + from, len); ++ ++ *retlen = len; ++ return 0; ++} ++ ++int phram_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t *retlen, const u_char *buf) ++{ ++ u_char *start = (u_char *)mtd->priv; ++ ++ if (to + len > mtd->size) ++ return -EINVAL; ++ ++ memcpy(start + to, buf, len); ++ ++ *retlen = len; ++ return 0; ++} ++ ++ ++ ++static void unregister_devices(void) ++{ ++ struct phram_mtd_list *this; ++ ++ list_for_each_entry(this, &phram_list, list) { ++ del_mtd_device(this->mtdinfo); ++ iounmap(this->mtdinfo->priv); ++ kfree(this->mtdinfo); ++ kfree(this); ++ } ++} ++ ++static int register_device(char *name, unsigned long start, unsigned long len) ++{ ++ struct phram_mtd_list *new; ++ int ret = -ENOMEM; ++ ++ new = kmalloc(sizeof(*new), GFP_KERNEL); ++ if (!new) ++ goto out0; ++ ++ new->mtdinfo = kmalloc(sizeof(struct mtd_info), GFP_KERNEL); ++ if (!new->mtdinfo) ++ goto out1; ++ ++ memset(new->mtdinfo, 0, sizeof(struct mtd_info)); ++ ++ ret = -EIO; ++ new->mtdinfo->priv = ioremap(start, len); ++ if (!new->mtdinfo->priv) { ++ ERROR("ioremap failed\n"); ++ goto out2; ++ } ++ ++ ++ new->mtdinfo->name = name; ++ new->mtdinfo->size = len; ++ new->mtdinfo->flags = MTD_CAP_RAM | MTD_ERASEABLE | MTD_VOLATILE; ++ new->mtdinfo->erase = phram_erase; ++ new->mtdinfo->point = phram_point; ++ new->mtdinfo->unpoint = phram_unpoint; ++ new->mtdinfo->read = phram_read; ++ new->mtdinfo->write = phram_write; ++ new->mtdinfo->owner = THIS_MODULE; ++ new->mtdinfo->type = MTD_RAM; ++ new->mtdinfo->erasesize = 0x0; ++ ++ ret = -EAGAIN; ++ if (add_mtd_device(new->mtdinfo)) { ++ ERROR("Failed to register new device\n"); ++ goto out3; ++ } ++ ++ list_add_tail(&new->list, &phram_list); ++ return 0; ++ ++out3: ++ iounmap(new->mtdinfo->priv); ++out2: ++ kfree(new->mtdinfo); ++out1: ++ kfree(new); ++out0: ++ return ret; ++} ++ ++static int ustrtoul(const char *cp, char **endp, unsigned int base) ++{ ++ unsigned long result = simple_strtoul(cp, endp, base); ++ ++ switch (**endp) { ++ case 'G': ++ result *= 1024; ++ case 'M': ++ result *= 1024; ++ case 'k': ++ result *= 1024; ++ endp++; ++ } ++ return result; ++} ++ ++static int parse_num32(uint32_t *num32, const char *token) ++{ ++ char *endp; ++ unsigned long n; ++ ++ n = ustrtoul(token, &endp, 0); ++ if (*endp) ++ return -EINVAL; ++ ++ *num32 = n; ++ return 0; ++} ++ ++static int parse_name(char **pname, const char *token) ++{ ++ size_t len; ++ char *name; ++ ++ len = strlen(token) + 1; ++ if (len > 64) ++ return -ENOSPC; ++ ++ name = kmalloc(len, GFP_KERNEL); ++ if (!name) ++ return -ENOMEM; ++ ++ strcpy(name, token); ++ ++ *pname = name; ++ return 0; ++} ++ ++#define parse_err(fmt, args...) do { \ ++ ERROR(fmt , ## args); \ ++ return 0; \ ++} while (0) ++ ++static int phram_setup(const char *val, struct kernel_param *kp) ++{ ++ char buf[64+12+12], *str = buf; ++ char *token[3]; ++ char *name; ++ uint32_t start; ++ uint32_t len; ++ int i, ret; ++ ++ if (strnlen(val, sizeof(str)) >= sizeof(str)) ++ parse_err("parameter too long\n"); ++ ++ strcpy(str, val); ++ ++ for (i=0; i<3; i++) ++ token[i] = strsep(&str, ","); ++ ++ if (str) ++ parse_err("too many arguments\n"); ++ ++ if (!token[2]) ++ parse_err("not enough arguments\n"); ++ ++ ret = parse_name(&name, token[0]); ++ if (ret == -ENOMEM) ++ parse_err("out of memory\n"); ++ if (ret == -ENOSPC) ++ parse_err("name too long\n"); ++ if (ret) ++ return 0; ++ ++ ret = parse_num32(&start, token[1]); ++ if (ret) ++ parse_err("illegal start address\n"); ++ ++ ret = parse_num32(&len, token[2]); ++ if (ret) ++ parse_err("illegal device length\n"); ++ ++ register_device(name, start, len); ++ ++ return 0; ++} ++ ++module_param_call(phram, phram_setup, NULL, NULL, 000); ++MODULE_PARM_DESC(phram, "Memory region to map. \"map=<name>,<start><length>\""); ++ ++/* ++ * Just for compatibility with slram, this is horrible and should go someday. ++ */ ++static int __init slram_setup(const char *val, struct kernel_param *kp) ++{ ++ char buf[256], *str = buf; ++ ++ if (!val || !val[0]) ++ parse_err("no arguments to \"slram=\"\n"); ++ ++ if (strnlen(val, sizeof(str)) >= sizeof(str)) ++ parse_err("parameter too long\n"); ++ ++ strcpy(str, val); ++ ++ while (str) { ++ char *token[3]; ++ char *name; ++ uint32_t start; ++ uint32_t len; ++ int i, ret; ++ ++ for (i=0; i<3; i++) { ++ token[i] = strsep(&str, ","); ++ if (token[i]) ++ continue; ++ parse_err("wrong number of arguments to \"slram=\"\n"); ++ } ++ ++ /* name */ ++ ret = parse_name(&name, token[0]); ++ if (ret == -ENOMEM) ++ parse_err("of memory\n"); ++ if (ret == -ENOSPC) ++ parse_err("too long\n"); ++ if (ret) ++ return 1; ++ ++ /* start */ ++ ret = parse_num32(&start, token[1]); ++ if (ret) ++ parse_err("illegal start address\n"); ++ ++ /* len */ ++ if (token[2][0] == '+') ++ ret = parse_num32(&len, token[2] + 1); ++ else ++ ret = parse_num32(&len, token[2]); ++ ++ if (ret) ++ parse_err("illegal device length\n"); ++ ++ if (token[2][0] != '+') { ++ if (len < start) ++ parse_err("end < start\n"); ++ len -= start; ++ } ++ ++ register_device(name, start, len); ++ } ++ return 1; ++} ++ ++module_param_call(slram, slram_setup, NULL, NULL, 000); ++MODULE_PARM_DESC(slram, "List of memory regions to map. \"map=<name>,<start><length/end>\""); ++ ++ ++int __init init_phram(void) ++{ ++ printk(KERN_ERR "phram loaded\n"); ++ return 0; ++} ++ ++static void __exit cleanup_phram(void) ++{ ++ unregister_devices(); ++} ++ ++module_init(init_phram); ++module_exit(cleanup_phram); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Jörn Engel <joern@wh.fh-wedel.de>"); ++MODULE_DESCRIPTION("MTD driver for physical RAM"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/pmc551.c linux/drivers/mtd/devices/pmc551.c +--- linux-mips-2.4.27/drivers/mtd/devices/pmc551.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/pmc551.c 2004-11-19 10:25:11.862205416 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: pmc551.c,v 1.22 2003/01/24 13:34:30 dwmw2 Exp $ ++ * $Id: pmc551.c,v 1.25 2003/06/23 12:24:01 dwmw2 Exp $ + * + * PMC551 PCI Mezzanine Ram Device + * +@@ -681,11 +681,6 @@ + + printk(KERN_INFO PMC551_VERSION); + +- if(!pci_present()) { +- printk(KERN_NOTICE "pmc551: PCI not enabled.\n"); +- return -ENODEV; +- } +- + /* + * PCU-bus chipset probe. + */ +@@ -787,10 +782,10 @@ + mtd->write = pmc551_write; + mtd->point = pmc551_point; + mtd->unpoint = pmc551_unpoint; +- mtd->module = THIS_MODULE; + mtd->type = MTD_RAM; + mtd->name = "PMC551 RAM board"; + mtd->erasesize = 0x10000; ++ mtd->owner = THIS_MODULE; + + if (add_mtd_device(mtd)) { + printk(KERN_NOTICE "pmc551: Failed to register new device\n"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/devices/slram.c linux/drivers/mtd/devices/slram.c +--- linux-mips-2.4.27/drivers/mtd/devices/slram.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/devices/slram.c 2004-11-19 10:25:11.863205264 +0100 +@@ -1,6 +1,6 @@ + /*====================================================================== + +- $Id: slram.c,v 1.28 2003/01/24 13:35:34 dwmw2 Exp $ ++ $Id: slram.c,v 1.30 2003/05/20 21:03:08 dwmw2 Exp $ + + This driver provides a method to access memory not used by the kernel + itself (i.e. if the kernel commandline mem=xxx is used). To actually +@@ -199,7 +199,7 @@ + (*curmtd)->mtdinfo->unpoint = slram_unpoint; + (*curmtd)->mtdinfo->read = slram_read; + (*curmtd)->mtdinfo->write = slram_write; +- (*curmtd)->mtdinfo->module = THIS_MODULE; ++ (*curmtd)->mtdinfo->owner = THIS_MODULE; + (*curmtd)->mtdinfo->type = MTD_RAM; + (*curmtd)->mtdinfo->erasesize = 0x0; + +diff -Nurb linux-mips-2.4.27/drivers/mtd/ftl.c linux/drivers/mtd/ftl.c +--- linux-mips-2.4.27/drivers/mtd/ftl.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/ftl.c 2004-11-19 10:25:11.630240680 +0100 +@@ -1,5 +1,5 @@ + /* This version ported to the Linux-MTD system by dwmw2@infradead.org +- * $Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $ ++ * $Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $ + * + * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br> + * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups +@@ -55,8 +55,8 @@ + contact M-Systems (http://www.m-sys.com) directly. + + ======================================================================*/ ++#include <linux/mtd/blktrans.h> + #include <linux/module.h> +-#include <linux/mtd/compatmac.h> + #include <linux/mtd/mtd.h> + /*#define PSYCHO_DEBUG */ + +@@ -68,43 +68,13 @@ + #include <linux/timer.h> + #include <linux/major.h> + #include <linux/fs.h> +-#include <linux/ioctl.h> ++#include <linux/init.h> + #include <linux/hdreg.h> +- +-#if (LINUX_VERSION_CODE >= 0x20100) + #include <linux/vmalloc.h> +-#endif +-#if (LINUX_VERSION_CODE >= 0x20303) + #include <linux/blkpg.h> +-#endif ++#include <asm/uaccess.h> + + #include <linux/mtd/ftl.h> +-/*====================================================================*/ +-/* Stuff which really ought to be in compatmac.h */ +- +-#if (LINUX_VERSION_CODE < 0x20328) +-#define register_disk(dev, drive, minors, ops, size) \ +- do { (dev)->part[(drive)*(minors)].nr_sects = size; \ +- if (size == 0) (dev)->part[(drive)*(minors)].start_sect = -1; \ +- resetup_one_dev(dev, drive); } while (0) +-#endif +- +-#if (LINUX_VERSION_CODE < 0x20320) +-#define BLK_DEFAULT_QUEUE(n) blk_dev[n].request_fn +-#define blk_init_queue(q, req) q = (req) +-#define blk_cleanup_queue(q) q = NULL +-#define request_arg_t void +-#else +-#define request_arg_t request_queue_t *q +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif + + /*====================================================================*/ + +@@ -119,19 +89,6 @@ + #define FTL_MAJOR 44 + #endif + +-/* Funky stuff for setting up a block device */ +-#define MAJOR_NR FTL_MAJOR +-#define DEVICE_NAME "ftl" +-#define DEVICE_REQUEST do_ftl_request +-#define DEVICE_ON(device) +-#define DEVICE_OFF(device) +- +-#define DEVICE_NR(minor) ((minor)>>5) +-#define REGION_NR(minor) (((minor)>>3)&3) +-#define PART_NR(minor) ((minor)&7) +-#define MINOR_NR(dev,reg,part) (((dev)<<5)+((reg)<<3)+(part)) +- +-#include <linux/blk.h> + + /*====================================================================*/ + +@@ -142,8 +99,7 @@ + #define MAX_REGION 4 + + /* Maximum number of partitions in an FTL region */ +-#define PART_BITS 3 +-#define MAX_PART 8 ++#define PART_BITS 4 + + /* Maximum number of outstanding erase requests per socket */ + #define MAX_ERASE 8 +@@ -154,7 +110,7 @@ + + /* Each memory region corresponds to a minor device */ + typedef struct partition_t { +- struct mtd_info *mtd; ++ struct mtd_blktrans_dev mbd; + u_int32_t state; + u_int32_t *VirtualBlockMap; + u_int32_t *VirtualPageMap; +@@ -179,21 +135,10 @@ + region_info_t region; + memory_handle_t handle; + #endif +- atomic_t open; + } partition_t; + +-partition_t *myparts[MAX_MTD_DEVICES]; +- +-static void ftl_notify_add(struct mtd_info *mtd); +-static void ftl_notify_remove(struct mtd_info *mtd); +- + void ftl_freepart(partition_t *part); + +-static struct mtd_notifier ftl_notifier = { +- add: ftl_notify_add, +- remove: ftl_notify_remove, +-}; +- + /* Partition state flags */ + #define FTL_FORMATTED 0x01 + +@@ -204,51 +149,11 @@ + #define XFER_PREPARED 0x03 + #define XFER_FAILED 0x04 + +-static struct hd_struct ftl_hd[MINOR_NR(MAX_DEV, 0, 0)]; +-static int ftl_sizes[MINOR_NR(MAX_DEV, 0, 0)]; +-static int ftl_blocksizes[MINOR_NR(MAX_DEV, 0, 0)]; +- +-static struct gendisk ftl_gendisk = { +- major: FTL_MAJOR, +- major_name: "ftl", +- minor_shift: PART_BITS, +- max_p: MAX_PART, +-#if (LINUX_VERSION_CODE < 0x20328) +- max_nr: MAX_DEV*MAX_PART, +-#endif +- part: ftl_hd, +- sizes: ftl_sizes, +-}; +- + /*====================================================================*/ + +-static int ftl_ioctl(struct inode *inode, struct file *file, +- u_int cmd, u_long arg); +-static int ftl_open(struct inode *inode, struct file *file); +-static release_t ftl_close(struct inode *inode, struct file *file); +-static int ftl_reread_partitions(int minor); + + static void ftl_erase_callback(struct erase_info *done); + +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations ftl_blk_fops = { +- open: ftl_open, +- release: ftl_close, +- ioctl: ftl_ioctl, +- read: block_read, +- write: block_write, +- fsync: block_fsync +-}; +-#else +-static struct block_device_operations ftl_blk_fops = { +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: ftl_open, +- release: ftl_close, +- ioctl: ftl_ioctl, +-}; +-#endif + + /*====================================================================== + +@@ -264,13 +169,13 @@ + loff_t offset, max_offset; + int ret; + part->header.FormattedSize = 0; +- max_offset = (0x100000<part->mtd->size)?0x100000:part->mtd->size; ++ max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size; + /* Search first megabyte for a valid FTL header */ + for (offset = 0; + (offset + sizeof(header)) < max_offset; +- offset += part->mtd->erasesize ? : 0x2000) { ++ offset += part->mbd.mtd->erasesize ? : 0x2000) { + +- ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret, + (unsigned char *)&header); + + if (ret) +@@ -283,15 +188,15 @@ + printk(KERN_NOTICE "ftl_cs: FTL header not found.\n"); + return -ENOENT; + } +- if ((le16_to_cpu(header.NumEraseUnits) > 65536) || header.BlockSize != 9 || ++ if (header.BlockSize != 9 || + (header.EraseUnitSize < 10) || (header.EraseUnitSize > 31) || + (header.NumTransferUnits >= le16_to_cpu(header.NumEraseUnits))) { + printk(KERN_NOTICE "ftl_cs: FTL header corrupt!\n"); + return -1; + } +- if ((1 << header.EraseUnitSize) != part->mtd->erasesize) { ++ if ((1 << header.EraseUnitSize) != part->mbd.mtd->erasesize) { + printk(KERN_NOTICE "ftl: FTL EraseUnitSize %x != MTD erasesize %x\n", +- 1 << header.EraseUnitSize,part->mtd->erasesize); ++ 1 << header.EraseUnitSize,part->mbd.mtd->erasesize); + return -1; + } + part->header = header; +@@ -326,7 +231,7 @@ + for (i = 0; i < le16_to_cpu(part->header.NumEraseUnits); i++) { + offset = ((i + le16_to_cpu(part->header.FirstPhysicalEUN)) + << part->header.EraseUnitSize); +- ret = part->mtd->read(part->mtd, offset, sizeof(header), &retval, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &retval, + (unsigned char *)&header); + + if (ret) +@@ -391,7 +296,7 @@ + part->EUNInfo[i].Deleted = 0; + offset = part->EUNInfo[i].Offset + le32_to_cpu(header.BAMOffset); + +- ret = part->mtd->read(part->mtd, offset, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, + part->BlocksPerUnit * sizeof(u_int32_t), &retval, + (unsigned char *)part->bam_cache); + +@@ -456,7 +361,7 @@ + erase->len = 1 << part->header.EraseUnitSize; + erase->priv = (u_long)part; + +- ret = part->mtd->erase(part->mtd, erase); ++ ret = part->mbd.mtd->erase(part->mbd.mtd, erase); + + if (!ret) + xfer->EraseCount++; +@@ -523,7 +428,7 @@ + header.LogicalEUN = cpu_to_le16(0xffff); + header.EraseCount = cpu_to_le32(xfer->EraseCount); + +- ret = part->mtd->write(part->mtd, xfer->Offset, sizeof(header), ++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset, sizeof(header), + &retlen, (u_char *)&header); + + if (ret) { +@@ -539,7 +444,7 @@ + + for (i = 0; i < nbam; i++, offset += sizeof(u_int32_t)) { + +- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&ctl); + + if (ret) +@@ -586,7 +491,7 @@ + + offset = eun->Offset + le32_to_cpu(part->header.BAMOffset); + +- ret = part->mtd->read(part->mtd, offset, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, + part->BlocksPerUnit * sizeof(u_int32_t), + &retlen, (u_char *) (part->bam_cache)); + +@@ -604,7 +509,7 @@ + offset = xfer->Offset + 20; /* Bad! */ + unit = cpu_to_le16(0x7fff); + +- ret = part->mtd->write(part->mtd, offset, sizeof(u_int16_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int16_t), + &retlen, (u_char *) &unit); + + if (ret) { +@@ -624,7 +529,7 @@ + break; + case BLOCK_DATA: + case BLOCK_REPLACEMENT: +- ret = part->mtd->read(part->mtd, src, SECTOR_SIZE, ++ ret = part->mbd.mtd->read(part->mbd.mtd, src, SECTOR_SIZE, + &retlen, (u_char *) buf); + if (ret) { + printk(KERN_WARNING "ftl: Error reading old xfer unit in copy_erase_unit\n"); +@@ -632,7 +537,7 @@ + } + + +- ret = part->mtd->write(part->mtd, dest, SECTOR_SIZE, ++ ret = part->mbd.mtd->write(part->mbd.mtd, dest, SECTOR_SIZE, + &retlen, (u_char *) buf); + if (ret) { + printk(KERN_WARNING "ftl: Error writing new xfer unit in copy_erase_unit\n"); +@@ -651,7 +556,7 @@ + } + + /* Write the BAM to the transfer unit */ +- ret = part->mtd->write(part->mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), ++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + le32_to_cpu(part->header.BAMOffset), + part->BlocksPerUnit * sizeof(int32_t), &retlen, + (u_char *)part->bam_cache); + if (ret) { +@@ -661,7 +566,7 @@ + + + /* All clear? Then update the LogicalEUN again */ +- ret = part->mtd->write(part->mtd, xfer->Offset + 20, sizeof(u_int16_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, xfer->Offset + 20, sizeof(u_int16_t), + &retlen, (u_char *)&srcunitswap); + + if (ret) { +@@ -749,8 +654,8 @@ + if (queued) { + DEBUG(1, "ftl_cs: waiting for transfer " + "unit to be prepared...\n"); +- if (part->mtd->sync) +- part->mtd->sync(part->mtd); ++ if (part->mbd.mtd->sync) ++ part->mbd.mtd->sync(part->mbd.mtd); + } else { + static int ne = 0; + if (++ne < 5) +@@ -848,7 +753,7 @@ + /* Invalidate cache */ + part->bam_index = 0xffff; + +- ret = part->mtd->read(part->mtd, ++ ret = part->mbd.mtd->read(part->mbd.mtd, + part->EUNInfo[eun].Offset + le32_to_cpu(part->header.BAMOffset), + part->BlocksPerUnit * sizeof(u_int32_t), + &retlen, (u_char *) (part->bam_cache)); +@@ -877,78 +782,6 @@ + + } /* find_free */ + +-/*====================================================================== +- +- This gets a memory handle for the region corresponding to the +- minor device number. +- +-======================================================================*/ +- +-static int ftl_open(struct inode *inode, struct file *file) +-{ +- int minor = MINOR(inode->i_rdev); +- partition_t *partition; +- +- if (minor>>4 >= MAX_MTD_DEVICES) +- return -ENODEV; +- +- partition = myparts[minor>>4]; +- +- if (!partition) +- return -ENODEV; +- +- if (partition->state != FTL_FORMATTED) +- return -ENXIO; +- +- if (ftl_gendisk.part[minor].nr_sects == 0) +- return -ENXIO; +- +- BLK_INC_USE_COUNT; +- +- if (!get_mtd_device(partition->mtd, -1)) { +- BLK_DEC_USE_COUNT; +- return -ENXIO; +- } +- +- if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) { +- put_mtd_device(partition->mtd); +- BLK_DEC_USE_COUNT; +- return -EROFS; +- } +- +- DEBUG(0, "ftl_cs: ftl_open(%d)\n", minor); +- +- atomic_inc(&partition->open); +- +- return 0; +-} +- +-/*====================================================================*/ +- +-static release_t ftl_close(struct inode *inode, struct file *file) +-{ +- int minor = MINOR(inode->i_rdev); +- partition_t *part = myparts[minor >> 4]; +- int i; +- +- DEBUG(0, "ftl_cs: ftl_close(%d)\n", minor); +- +- /* Wait for any pending erase operations to complete */ +- if (part->mtd->sync) +- part->mtd->sync(part->mtd); +- +- for (i = 0; i < part->header.NumTransferUnits; i++) { +- if (part->XferInfo[i].state == XFER_ERASED) +- prepare_xfer(part, i); +- } +- +- atomic_dec(&part->open); +- +- put_mtd_device(part->mtd); +- BLK_DEC_USE_COUNT; +- release_return(0); +-} /* ftl_close */ +- + + /*====================================================================== + +@@ -983,7 +816,7 @@ + else { + offset = (part->EUNInfo[log_addr / bsize].Offset + + (log_addr % bsize)); +- ret = part->mtd->read(part->mtd, offset, SECTOR_SIZE, ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, SECTOR_SIZE, + &retlen, (u_char *) buffer); + + if (ret) { +@@ -1022,7 +855,7 @@ + le32_to_cpu(part->header.BAMOffset)); + + #ifdef PSYCHO_DEBUG +- ret = part->mtd->read(part->mtd, offset, sizeof(u_int32_t), ++ ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&old_addr); + if (ret) { + printk(KERN_WARNING"ftl: Error reading old_addr in set_bam_entry: %d\n",ret); +@@ -1059,7 +892,7 @@ + #endif + part->bam_cache[blk] = le_virt_addr; + } +- ret = part->mtd->write(part->mtd, offset, sizeof(u_int32_t), ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, sizeof(u_int32_t), + &retlen, (u_char *)&le_virt_addr); + + if (ret) { +@@ -1119,7 +952,7 @@ + part->EUNInfo[part->bam_index].Deleted++; + offset = (part->EUNInfo[part->bam_index].Offset + + blk * SECTOR_SIZE); +- ret = part->mtd->write(part->mtd, offset, SECTOR_SIZE, &retlen, ++ ret = part->mbd.mtd->write(part->mbd.mtd, offset, SECTOR_SIZE, &retlen, + buffer); + + if (ret) { +@@ -1151,164 +984,32 @@ + return 0; + } /* ftl_write */ + +-/*====================================================================== +- +- IOCTL calls for getting device parameters. +- +-======================================================================*/ +- +-static int ftl_ioctl(struct inode *inode, struct file *file, +- u_int cmd, u_long arg) ++static int ftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) + { +- struct hd_geometry *geo = (struct hd_geometry *)arg; +- int ret = 0, minor = MINOR(inode->i_rdev); +- partition_t *part= myparts[minor >> 4]; ++ partition_t *part = (void *)dev; + u_long sect; + +- if (!part) +- return -ENODEV; /* How? */ +- +- switch (cmd) { +- case HDIO_GETGEO: +- ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo)); +- if (ret) return ret; +- /* Sort of arbitrary: round size down to 4K boundary */ ++ /* Sort of arbitrary: round size down to 4KiB boundary */ + sect = le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE; +- put_user(1, (char *)&geo->heads); +- put_user(8, (char *)&geo->sectors); +- put_user((sect>>3), (short *)&geo->cylinders); +- put_user(ftl_hd[minor].start_sect, (u_long *)&geo->start); +- break; +- case BLKGETSIZE: +- ret = put_user(ftl_hd[minor].nr_sects, (unsigned long *)arg); +- break; +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- ret = put_user((u64)ftl_hd[minor].nr_sects << 9, (u64 *)arg); +- break; +-#endif +- case BLKRRPART: +- ret = ftl_reread_partitions(minor); +- break; +-#if (LINUX_VERSION_CODE < 0x20303) +- case BLKFLSBUF: +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if (!capable(CAP_SYS_ADMIN)) return -EACCES; +-#endif +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); +- break; +- RO_IOCTLS(inode->i_rdev, arg); +-#else +- case BLKROSET: +- case BLKROGET: +- case BLKFLSBUF: +- ret = blk_ioctl(inode->i_rdev, cmd, arg); +- break; +-#endif +- default: +- ret = -EINVAL; +- } +- +- return ret; +-} /* ftl_ioctl */ +- +-/*====================================================================== + +- Handler for block device requests ++ geo->heads = 1; ++ geo->sectors = 8; ++ geo->cylinders = sect >> 3; + +-======================================================================*/ +- +-static int ftl_reread_partitions(int minor) +-{ +- partition_t *part = myparts[minor >> 4]; +- int i, whole; +- +- DEBUG(0, "ftl_cs: ftl_reread_partition(%d)\n", minor); +- if ((atomic_read(&part->open) > 1)) { +- return -EBUSY; +- } +- whole = minor & ~(MAX_PART-1); +- +- i = MAX_PART - 1; +- while (i-- > 0) { +- if (ftl_hd[whole+i].nr_sects > 0) { +- kdev_t rdev = MKDEV(FTL_MAJOR, whole+i); +- +- invalidate_device(rdev, 1); +- } +- ftl_hd[whole+i].start_sect = 0; +- ftl_hd[whole+i].nr_sects = 0; +- } +- +- scan_header(part); +- +- register_disk(&ftl_gendisk, whole >> PART_BITS, MAX_PART, +- &ftl_blk_fops, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE); +- +-#ifdef PCMCIA_DEBUG +- for (i = 0; i < MAX_PART; i++) { +- if (ftl_hd[whole+i].nr_sects > 0) +- printk(KERN_INFO " %d: start %ld size %ld\n", i, +- ftl_hd[whole+i].start_sect, +- ftl_hd[whole+i].nr_sects); +- } +-#endif + return 0; + } + +-/*====================================================================== +- +- Handler for block device requests +- +-======================================================================*/ +- +-static void do_ftl_request(request_arg_t) ++static int ftl_readsect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) + { +- int ret, minor; +- partition_t *part; +- +- do { +- // sti(); +- INIT_REQUEST; +- +- minor = MINOR(CURRENT->rq_dev); +- +- part = myparts[minor >> 4]; +- if (part) { +- ret = 0; +- +- switch (CURRENT->cmd) { +- case READ: +- ret = ftl_read(part, CURRENT->buffer, +- CURRENT->sector+ftl_hd[minor].start_sect, +- CURRENT->current_nr_sectors); +- if (ret) printk("ftl_read returned %d\n", ret); +- break; +- +- case WRITE: +- ret = ftl_write(part, CURRENT->buffer, +- CURRENT->sector+ftl_hd[minor].start_sect, +- CURRENT->current_nr_sectors); +- if (ret) printk("ftl_write returned %d\n", ret); +- break; +- +- default: +- panic("ftl_cs: unknown block command!\n"); +- +- } +- } else { +- ret = 1; +- printk("NULL part in ftl_request\n"); +- } +- +- if (!ret) { +- CURRENT->sector += CURRENT->current_nr_sectors; +- } ++ return ftl_read((void *)dev, buf, block, 1); ++} + +- end_request((ret == 0) ? 1 : 0); +- } while (1); +-} /* do_ftl_request */ ++static int ftl_writesect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ return ftl_write((void *)dev, buf, block, 1); ++} + + /*====================================================================*/ + +@@ -1337,19 +1038,9 @@ + + } /* ftl_freepart */ + +-static void ftl_notify_add(struct mtd_info *mtd) ++static void ftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { + partition_t *partition; +- int device; +- +- for (device=0; device < MAX_MTD_DEVICES && myparts[device]; device++) +- ; +- +- if (device == MAX_MTD_DEVICES) { +- printk(KERN_NOTICE "Maximum number of FTL partitions reached\n" +- "Not scanning <%s>\n", mtd->name); +- return; +- } + + partition = kmalloc(sizeof(partition_t), GFP_KERNEL); + +@@ -1361,92 +1052,55 @@ + + memset(partition, 0, sizeof(partition_t)); + +- partition->mtd = mtd; ++ partition->mbd.mtd = mtd; + + if ((scan_header(partition) == 0) && + (build_maps(partition) == 0)) { + + partition->state = FTL_FORMATTED; +- atomic_set(&partition->open, 0); +- myparts[device] = partition; +- ftl_reread_partitions(device << 4); + #ifdef PCMCIA_DEBUG +- printk(KERN_INFO "ftl_cs: opening %d kb FTL partition\n", ++ printk(KERN_INFO "ftl_cs: opening %d KiB FTL partition\n", + le32_to_cpu(partition->header.FormattedSize) >> 10); + #endif ++ partition->mbd.size = le32_to_cpu(partition->header.FormattedSize) >> 9; ++ partition->mbd.blksize = SECTOR_SIZE; ++ partition->mbd.tr = tr; ++ partition->mbd.devnum = -1; ++ if (add_mtd_blktrans_dev((void *)partition)) ++ kfree(partition); ++ + } else + kfree(partition); + } + +-static void ftl_notify_remove(struct mtd_info *mtd) ++static void ftl_remove_dev(struct mtd_blktrans_dev *dev) + { +- int i,j; +- +- /* Q: What happens if you try to remove a device which has +- * a currently-open FTL partition on it? +- * +- * A: You don't. The ftl_open routine is responsible for +- * increasing the use count of the driver module which +- * it uses. +- */ +- +- /* That's the theory, anyway :) */ +- +- for (i=0; i< MAX_MTD_DEVICES; i++) +- if (myparts[i] && myparts[i]->mtd == mtd) { +- +- if (myparts[i]->state == FTL_FORMATTED) +- ftl_freepart(myparts[i]); +- +- myparts[i]->state = 0; +- for (j=0; j<16; j++) { +- ftl_gendisk.part[j].nr_sects=0; +- ftl_gendisk.part[j].start_sect=0; +- } +- kfree(myparts[i]); +- myparts[i] = NULL; +- } ++ del_mtd_blktrans_dev(dev); ++ kfree(dev); + } + ++struct mtd_blktrans_ops ftl_tr = { ++ .name = "ftl", ++ .major = FTL_MAJOR, ++ .part_bits = PART_BITS, ++ .readsect = ftl_readsect, ++ .writesect = ftl_writesect, ++ .getgeo = ftl_getgeo, ++ .add_mtd = ftl_add_mtd, ++ .remove_dev = ftl_remove_dev, ++ .owner = THIS_MODULE, ++}; ++ + int init_ftl(void) + { +- int i; +- +- memset(myparts, 0, sizeof(myparts)); ++ DEBUG(0, "$Id: ftl.c,v 1.52 2003/08/11 09:00:44 dwmw2 Exp $\n"); + +- DEBUG(0, "$Id: ftl.c,v 1.45 2003/01/24 23:31:27 dwmw2 Exp $\n"); +- +- if (register_blkdev(FTL_MAJOR, "ftl", &ftl_blk_fops)) { +- printk(KERN_NOTICE "ftl_cs: unable to grab major " +- "device number!\n"); +- return -EAGAIN; +- } +- +- for (i = 0; i < MINOR_NR(MAX_DEV, 0, 0); i++) +- ftl_blocksizes[i] = 1024; +- for (i = 0; i < MAX_DEV*MAX_PART; i++) { +- ftl_hd[i].nr_sects = 0; +- ftl_hd[i].start_sect = 0; +- } +- blksize_size[FTL_MAJOR] = ftl_blocksizes; +- ftl_gendisk.major = FTL_MAJOR; +- blk_init_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR), &do_ftl_request); +- add_gendisk(&ftl_gendisk); +- +- register_mtd_user(&ftl_notifier); +- +- return 0; ++ return register_mtd_blktrans(&ftl_tr); + } + + static void __exit cleanup_ftl(void) + { +- unregister_mtd_user(&ftl_notifier); +- +- unregister_blkdev(FTL_MAJOR, "ftl"); +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(FTL_MAJOR)); +- blksize_size[FTL_MAJOR] = NULL; +- +- del_gendisk(&ftl_gendisk); ++ deregister_mtd_blktrans(&ftl_tr); + } + + module_init(init_ftl); +diff -Nurb linux-mips-2.4.27/drivers/mtd/inftlcore.c linux/drivers/mtd/inftlcore.c +--- linux-mips-2.4.27/drivers/mtd/inftlcore.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/inftlcore.c 2004-11-19 10:25:11.632240376 +0100 +@@ -0,0 +1,900 @@ ++/* ++ * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL) ++ * ++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) ++ * ++ * Based heavily on the nftlcore.c code which is: ++ * (c) 1999 Machine Vision Holdings, Inc. ++ * Author: David Woodhouse <dwmw2@infradead.org> ++ * ++ * $Id: inftlcore.c,v 1.14 2003/06/26 08:28:26 dwmw2 Exp $ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/config.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/kmod.h> ++#include <linux/hdreg.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nftl.h> ++#include <linux/mtd/inftl.h> ++#include <asm/uaccess.h> ++#include <asm/errno.h> ++#include <asm/io.h> ++ ++/* ++ * Maximum number of loops while examining next block, to have a ++ * chance to detect consistency problems (they should never happen ++ * because of the checks done in the mounting. ++ */ ++#define MAX_LOOPS 10000 ++ ++extern void INFTL_dumptables(struct INFTLrecord *inftl); ++extern void INFTL_dumpVUchains(struct INFTLrecord *inftl); ++ ++static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) ++{ ++ struct INFTLrecord *inftl; ++ unsigned long temp; ++ ++ if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) ++ return; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name); ++ ++ inftl = kmalloc(sizeof(*inftl), GFP_KERNEL); ++ ++ if (!inftl) { ++ printk(KERN_WARNING "INFTL: Out of memory for data structures\n"); ++ return; ++ } ++ memset(inftl, 0, sizeof(*inftl)); ++ ++ inftl->mbd.mtd = mtd; ++ inftl->mbd.devnum = -1; ++ inftl->mbd.blksize = 512; ++ inftl->mbd.tr = tr; ++ ++ if (INFTL_mount(inftl) < 0) { ++ printk(KERN_WARNING "INFTL: could not mount device\n"); ++ kfree(inftl); ++ return; ++ } ++ ++ /* OK, it's a new one. Set up all the data structures. */ ++ ++ /* Calculate geometry */ ++ inftl->cylinders = 1024; ++ inftl->heads = 16; ++ ++ temp = inftl->cylinders * inftl->heads; ++ inftl->sectors = inftl->mbd.size / temp; ++ if (inftl->mbd.size % temp) { ++ inftl->sectors++; ++ temp = inftl->cylinders * inftl->sectors; ++ inftl->heads = inftl->mbd.size / temp; ++ ++ if (inftl->mbd.size % temp) { ++ inftl->heads++; ++ temp = inftl->heads * inftl->sectors; ++ inftl->cylinders = inftl->mbd.size / temp; ++ } ++ } ++ ++ if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) { ++ /* ++ Oh no we don't have ++ mbd.size == heads * cylinders * sectors ++ */ ++ printk(KERN_WARNING "INFTL: cannot calculate a geometry to " ++ "match size of 0x%lx.\n", inftl->mbd.size); ++ printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d " ++ "(== 0x%lx sects)\n", ++ inftl->cylinders, inftl->heads , inftl->sectors, ++ (long)inftl->cylinders * (long)inftl->heads * ++ (long)inftl->sectors ); ++ } ++ ++ if (add_mtd_blktrans_dev(&inftl->mbd)) { ++ if (inftl->PUtable) ++ kfree(inftl->PUtable); ++ if (inftl->VUtable) ++ kfree(inftl->VUtable); ++ kfree(inftl); ++ return; ++ } ++#ifdef PSYCHO_DEBUG ++ printk(KERN_INFO "INFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); ++#endif ++ return; ++} ++ ++static void inftl_remove_dev(struct mtd_blktrans_dev *dev) ++{ ++ struct INFTLrecord *inftl = (void *)dev; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: remove_dev (i=%d)\n", dev->devnum); ++ ++ del_mtd_blktrans_dev(dev); ++ ++ if (inftl->PUtable) ++ kfree(inftl->PUtable); ++ if (inftl->VUtable) ++ kfree(inftl->VUtable); ++ kfree(inftl); ++} ++ ++/* ++ * Actual INFTL access routines. ++ */ ++ ++/* ++ * INFTL_findfreeblock: Find a free Erase Unit on the INFTL partition. ++ * This function is used when the give Virtual Unit Chain. ++ */ ++static u16 INFTL_findfreeblock(struct INFTLrecord *inftl, int desperate) ++{ ++ u16 pot = inftl->LastFreeEUN; ++ int silly = inftl->nb_blocks; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findfreeblock(inftl=0x%x," ++ "desperate=%d)\n", (int)inftl, desperate); ++ ++ /* ++ * Normally, we force a fold to happen before we run out of free ++ * blocks completely. ++ */ ++ if (!desperate && inftl->numfreeEUNs < 2) { ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: there are too few free " ++ "EUNs (%d)\n", inftl->numfreeEUNs); ++ return 0xffff; ++ } ++ ++ /* Scan for a free block */ ++ do { ++ if (inftl->PUtable[pot] == BLOCK_FREE) { ++ inftl->LastFreeEUN = pot; ++ return pot; ++ } ++ ++ if (++pot > inftl->lastEUN) ++ pot = 0; ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: no free blocks found! " ++ "EUN range = %d - %d\n", 0, inftl->LastFreeEUN); ++ return BLOCK_NIL; ++ } ++ } while (pot != inftl->LastFreeEUN); ++ ++ return BLOCK_NIL; ++} ++ ++static u16 INFTL_foldchain(struct INFTLrecord *inftl, unsigned thisVUC, unsigned pendingblock) ++{ ++ u16 BlockMap[MAX_SECTORS_PER_UNIT]; ++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; ++ unsigned int thisEUN, prevEUN, status; ++ int block, silly; ++ unsigned int targetEUN; ++ struct inftl_oob oob; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_foldchain(inftl=0x%x,thisVUC=%d," ++ "pending=%d)\n", (int)inftl, thisVUC, pendingblock); ++ ++ memset(BlockMap, 0xff, sizeof(BlockMap)); ++ memset(BlockDeleted, 0, sizeof(BlockDeleted)); ++ ++ thisEUN = targetEUN = inftl->VUtable[thisVUC]; ++ ++ if (thisEUN == BLOCK_NIL) { ++ printk(KERN_WARNING "INFTL: trying to fold non-existent " ++ "Virtual Unit Chain %d!\n", thisVUC); ++ return BLOCK_NIL; ++ } ++ ++ /* ++ * Scan to find the Erase Unit which holds the actual data for each ++ * 512-byte block within the Chain. ++ */ ++ silly = MAX_LOOPS; ++ while (thisEUN < inftl->nb_blocks) { ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block ++) { ++ if ((BlockMap[block] != 0xffff) || BlockDeleted[block]) ++ continue; ++ ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) ++ + (block * SECTORSIZE), 16 , &retlen, ++ (char *)&oob) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = oob.b.Status | oob.b.Status1; ++ ++ switch(status) { ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ case SECTOR_USED: ++ BlockMap[block] = thisEUN; ++ continue; ++ case SECTOR_DELETED: ++ BlockDeleted[block] = 1; ++ continue; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status " ++ "for block %d in EUN %d: %x\n", ++ block, thisEUN, status); ++ break; ++ } ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in Virtual " ++ "Unit Chain 0x%x\n", thisVUC); ++ return BLOCK_NIL; ++ } ++ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++ /* ++ * OK. We now know the location of every block in the Virtual Unit ++ * Chain, and the Erase Unit into which we are supposed to be copying. ++ * Go for it. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: folding chain %d into unit %d\n", ++ thisVUC, targetEUN); ++ ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE ; block++) { ++ unsigned char movebuf[SECTORSIZE]; ++ int ret; ++ ++ /* ++ * If it's in the target EUN already, or if it's pending write, ++ * do nothing. ++ */ ++ if (BlockMap[block] == targetEUN || (pendingblock == ++ (thisVUC * (inftl->EraseSize / SECTORSIZE) + block))) { ++ continue; ++ } ++ ++ /* ++ * Copy only in non free block (free blocks can only ++ * happen in case of media errors or deleted blocks). ++ */ ++ if (BlockMap[block] == BLOCK_NIL) ++ continue; ++ ++ ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize * ++ BlockMap[block]) + (block * SECTORSIZE), SECTORSIZE, ++ &retlen, movebuf, (char *)&oob, NULL); ++ if (ret < 0) { ++ ret = MTD_READECC(inftl->mbd.mtd, (inftl->EraseSize * ++ BlockMap[block]) + (block * SECTORSIZE), ++ SECTORSIZE, &retlen, movebuf, (char *)&oob, ++ NULL); ++ if (ret != -EIO) ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: error went " ++ "away on retry?\n"); ++ } ++ MTD_WRITEECC(inftl->mbd.mtd, (inftl->EraseSize * targetEUN) + ++ (block * SECTORSIZE), SECTORSIZE, &retlen, ++ movebuf, (char *)&oob, NULL); ++ } ++ ++ /* ++ * Newest unit in chain now contains data from _all_ older units. ++ * So go through and erase each unit in chain, oldest first. (This ++ * is important, by doing oldest first if we crash/reboot then it ++ * it is relatively simple to clean up the mess). ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: want to erase virtual chain %d\n", ++ thisVUC); ++ ++ for (;;) { ++ /* Find oldest unit in chain. */ ++ thisEUN = inftl->VUtable[thisVUC]; ++ prevEUN = BLOCK_NIL; ++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) { ++ prevEUN = thisEUN; ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++ /* Check if we are all done */ ++ if (thisEUN == targetEUN) ++ break; ++ ++ if (INFTL_formatblock(inftl, thisEUN) < 0) { ++ /* ++ * Could not erase : mark block as reserved. ++ * FixMe: Update Bad Unit Table on disk. ++ */ ++ inftl->PUtable[thisEUN] = BLOCK_RESERVED; ++ } else { ++ /* Correctly erased : mark it as free */ ++ inftl->PUtable[thisEUN] = BLOCK_FREE; ++ inftl->PUtable[prevEUN] = BLOCK_NIL; ++ inftl->numfreeEUNs++; ++ } ++ } ++ ++ return targetEUN; ++} ++ ++u16 INFTL_makefreeblock(struct INFTLrecord *inftl, unsigned pendingblock) ++{ ++ /* ++ * This is the part that needs some cleverness applied. ++ * For now, I'm doing the minimum applicable to actually ++ * get the thing to work. ++ * Wear-levelling and other clever stuff needs to be implemented ++ * and we also need to do some assessment of the results when ++ * the system loses power half-way through the routine. ++ */ ++ u16 LongestChain = 0; ++ u16 ChainLength = 0, thislen; ++ u16 chain, EUN; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_makefreeblock(inftl=0x%x," ++ "pending=%d)\n", (int)inftl, pendingblock); ++ ++ for (chain = 0; chain < inftl->nb_blocks; chain++) { ++ EUN = inftl->VUtable[chain]; ++ thislen = 0; ++ ++ while (EUN <= inftl->lastEUN) { ++ thislen++; ++ EUN = inftl->PUtable[EUN]; ++ if (thislen > 0xff00) { ++ printk(KERN_WARNING "INFTL: endless loop in " ++ "Virtual Chain %d: Unit %x\n", ++ chain, EUN); ++ /* ++ * Actually, don't return failure. ++ * Just ignore this chain and get on with it. ++ */ ++ thislen = 0; ++ break; ++ } ++ } ++ ++ if (thislen > ChainLength) { ++ ChainLength = thislen; ++ LongestChain = chain; ++ } ++ } ++ ++ if (ChainLength < 2) { ++ printk(KERN_WARNING "INFTL: no Virtual Unit Chains available " ++ "for folding. Failing request\n"); ++ return BLOCK_NIL; ++ } ++ ++ return INFTL_foldchain(inftl, LongestChain, pendingblock); ++} ++ ++static int nrbits(unsigned int val, int bitcount) ++{ ++ int i, total = 0; ++ ++ for (i = 0; (i < bitcount); i++) ++ total += (((0x1 << i) & val) ? 1 : 0); ++ return total; ++} ++ ++/* ++ * INFTL_findwriteunit: Return the unit number into which we can write ++ * for this block. Make it available if it isn't already. ++ */ ++static inline u16 INFTL_findwriteunit(struct INFTLrecord *inftl, unsigned block) ++{ ++ unsigned int thisVUC = block / (inftl->EraseSize / SECTORSIZE); ++ unsigned int thisEUN, writeEUN, prev_block, status; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize -1); ++ struct inftl_oob oob; ++ struct inftl_bci bci; ++ unsigned char anac, nacs, parity; ++ size_t retlen; ++ int silly, silly2 = 3; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_findwriteunit(inftl=0x%x," ++ "block=%d)\n", (int)inftl, block); ++ ++ do { ++ /* ++ * Scan the media to find a unit in the VUC which has ++ * a free space for the block in question. ++ */ ++ writeEUN = BLOCK_NIL; ++ thisEUN = inftl->VUtable[thisVUC]; ++ silly = MAX_LOOPS; ++ ++ while (thisEUN <= inftl->lastEUN) { ++ MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + ++ blockofs, 8, &retlen, (char *)&bci); ++ ++ status = bci.Status | bci.Status1; ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: status of block %d in " ++ "EUN %d is %x\n", block , writeEUN, status); ++ ++ switch(status) { ++ case SECTOR_FREE: ++ writeEUN = thisEUN; ++ break; ++ case SECTOR_DELETED: ++ case SECTOR_USED: ++ /* Can't go any further */ ++ goto hitused; ++ case SECTOR_IGNORE: ++ break; ++ default: ++ /* ++ * Invalid block. Don't use it any more. ++ * Must implement. ++ */ ++ break; ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in " ++ "Virtual Unit Chain 0x%x\n", thisVUC); ++ return 0xffff; ++ } ++ ++ /* Skip to next block in chain */ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++hitused: ++ if (writeEUN != BLOCK_NIL) ++ return writeEUN; ++ ++ ++ /* ++ * OK. We didn't find one in the existing chain, or there ++ * is no existing chain. Allocate a new one. ++ */ ++ writeEUN = INFTL_findfreeblock(inftl, 0); ++ ++ if (writeEUN == BLOCK_NIL) { ++ /* ++ * That didn't work - there were no free blocks just ++ * waiting to be picked up. We're going to have to fold ++ * a chain to make room. ++ */ ++ thisEUN = INFTL_makefreeblock(inftl, 0xffff); ++ ++ /* ++ * Hopefully we free something, lets try again. ++ * This time we are desperate... ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: using desperate==1 " ++ "to find free EUN to accommodate write to " ++ "VUC %d\n", thisVUC); ++ writeEUN = INFTL_findfreeblock(inftl, 1); ++ if (writeEUN == BLOCK_NIL) { ++ /* ++ * Ouch. This should never happen - we should ++ * always be able to make some room somehow. ++ * If we get here, we've allocated more storage ++ * space than actual media, or our makefreeblock ++ * routine is missing something. ++ */ ++ printk(KERN_WARNING "INFTL: cannot make free " ++ "space.\n"); ++#ifdef DEBUG ++ INFTL_dumptables(inftl); ++ INFTL_dumpVUchains(inftl); ++#endif ++ return BLOCK_NIL; ++ } ++ } ++ ++ /* ++ * Insert new block into virtual chain. Firstly update the ++ * block headers in flash... ++ */ ++ anac = 0; ++ nacs = 0; ++ thisEUN = inftl->VUtable[thisVUC]; ++ if (thisEUN != BLOCK_NIL) { ++ MTD_READOOB(inftl->mbd.mtd, thisEUN * inftl->EraseSize ++ + 8, 8, &retlen, (char *)&oob.u); ++ anac = oob.u.a.ANAC + 1; ++ nacs = oob.u.a.NACs + 1; ++ } ++ ++ prev_block = inftl->VUtable[thisVUC]; ++ if (prev_block < inftl->nb_blocks) ++ prev_block -= inftl->firstEUN; ++ ++ parity = (nrbits(thisVUC, 16) & 0x1) ? 0x1 : 0; ++ parity |= (nrbits(prev_block, 16) & 0x1) ? 0x2 : 0; ++ parity |= (nrbits(anac, 8) & 0x1) ? 0x4 : 0; ++ parity |= (nrbits(nacs, 8) & 0x1) ? 0x8 : 0; ++ ++ oob.u.a.virtualUnitNo = cpu_to_le16(thisVUC); ++ oob.u.a.prevUnitNo = cpu_to_le16(prev_block); ++ oob.u.a.ANAC = anac; ++ oob.u.a.NACs = nacs; ++ oob.u.a.parityPerField = parity; ++ oob.u.a.discarded = 0xaa; ++ ++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + 8, 8, ++ &retlen, (char *)&oob.u); ++ ++ /* Also back up header... */ ++ oob.u.b.virtualUnitNo = cpu_to_le16(thisVUC); ++ oob.u.b.prevUnitNo = cpu_to_le16(prev_block); ++ oob.u.b.ANAC = anac; ++ oob.u.b.NACs = nacs; ++ oob.u.b.parityPerField = parity; ++ oob.u.b.discarded = 0xaa; ++ ++ MTD_WRITEOOB(inftl->mbd.mtd, writeEUN * inftl->EraseSize + ++ SECTORSIZE * 4 + 8, 8, &retlen, (char *)&oob.u); ++ ++ inftl->PUtable[writeEUN] = inftl->VUtable[thisVUC]; ++ inftl->VUtable[thisVUC] = writeEUN; ++ ++ inftl->numfreeEUNs--; ++ return writeEUN; ++ ++ } while (silly2--); ++ ++ printk(KERN_WARNING "INFTL: error folding to make room for Virtual " ++ "Unit Chain 0x%x\n", thisVUC); ++ return 0xffff; ++} ++ ++/* ++ * Given a Virtual Unit Chain, see if it can be deleted, and if so do it. ++ */ ++static void INFTL_trydeletechain(struct INFTLrecord *inftl, unsigned thisVUC) ++{ ++ unsigned char BlockUsed[MAX_SECTORS_PER_UNIT]; ++ unsigned char BlockDeleted[MAX_SECTORS_PER_UNIT]; ++ unsigned int thisEUN, status; ++ int block, silly; ++ struct inftl_bci bci; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_trydeletechain(inftl=0x%x," ++ "thisVUC=%d)\n", (int)inftl, thisVUC); ++ ++ memset(BlockUsed, 0, sizeof(BlockUsed)); ++ memset(BlockDeleted, 0, sizeof(BlockDeleted)); ++ ++ thisEUN = inftl->VUtable[thisVUC]; ++ if (thisEUN == BLOCK_NIL) { ++ printk(KERN_WARNING "INFTL: trying to delete non-existent " ++ "Virtual Unit Chain %d!\n", thisVUC); ++ return; ++ } ++ ++ /* ++ * Scan through the Erase Units to determine whether any data is in ++ * each of the 512-byte blocks within the Chain. ++ */ ++ silly = MAX_LOOPS; ++ while (thisEUN < inftl->nb_blocks) { ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) { ++ if (BlockUsed[block] || BlockDeleted[block]) ++ continue; ++ ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) ++ + (block * SECTORSIZE), 8 , &retlen, ++ (char *)&bci) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = bci.Status | bci.Status1; ++ ++ switch(status) { ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ case SECTOR_USED: ++ BlockUsed[block] = 1; ++ continue; ++ case SECTOR_DELETED: ++ BlockDeleted[block] = 1; ++ continue; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status " ++ "for block %d in EUN %d: 0x%x\n", ++ block, thisEUN, status); ++ } ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in Virtual " ++ "Unit Chain 0x%x\n", thisVUC); ++ return; ++ } ++ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++ for (block = 0; block < inftl->EraseSize/SECTORSIZE; block++) ++ if (BlockUsed[block]) ++ return; ++ ++ /* ++ * For each block in the chain free it and make it available ++ * for future use. Erase from the oldest unit first. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL1, "INFTL: deleting empty VUC %d\n", thisVUC); ++ ++ for (;;) { ++ u16 *prevEUN = &inftl->VUtable[thisVUC]; ++ thisEUN = *prevEUN; ++ ++ /* If the chain is all gone already, we're done */ ++ if (thisEUN == BLOCK_NIL) { ++ DEBUG(MTD_DEBUG_LEVEL2, "INFTL: Empty VUC %d for deletion was already absent\n", thisEUN); ++ return; ++ } ++ ++ /* Find oldest unit in chain. */ ++ while (inftl->PUtable[thisEUN] != BLOCK_NIL) { ++ BUG_ON(thisEUN >= inftl->nb_blocks); ++ ++ prevEUN = &inftl->PUtable[thisEUN]; ++ thisEUN = *prevEUN; ++ } ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "Deleting EUN %d from VUC %d\n", ++ thisEUN, thisVUC); ++ ++ if (INFTL_formatblock(inftl, thisEUN) < 0) { ++ /* ++ * Could not erase : mark block as reserved. ++ * FixMe: Update Bad Unit Table on medium. ++ */ ++ inftl->PUtable[thisEUN] = BLOCK_RESERVED; ++ } else { ++ /* Correctly erased : mark it as free */ ++ inftl->PUtable[thisEUN] = BLOCK_FREE; ++ inftl->numfreeEUNs++; ++ } ++ ++ /* Now sort out whatever was pointing to it... */ ++ *prevEUN = BLOCK_NIL; ++ ++ /* Ideally we'd actually be responsive to new ++ requests while we're doing this -- if there's ++ free space why should others be made to wait? */ ++ cond_resched(); ++ } ++ ++ inftl->VUtable[thisVUC] = BLOCK_NIL; ++} ++ ++static int INFTL_deleteblock(struct INFTLrecord *inftl, unsigned block) ++{ ++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); ++ unsigned int status; ++ int silly = MAX_LOOPS; ++ size_t retlen; ++ struct inftl_bci bci; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_deleteblock(inftl=0x%x," ++ "block=%d)\n", (int)inftl, block); ++ ++ while (thisEUN < inftl->nb_blocks) { ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + ++ blockofs, 8, &retlen, (char *)&bci) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = bci.Status | bci.Status1; ++ ++ switch (status) { ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ case SECTOR_DELETED: ++ thisEUN = BLOCK_NIL; ++ goto foundit; ++ case SECTOR_USED: ++ goto foundit; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status for " ++ "block %d in EUN %d: 0x%x\n", ++ block, thisEUN, status); ++ break; ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in Virtual " ++ "Unit Chain 0x%x\n", ++ block / (inftl->EraseSize / SECTORSIZE)); ++ return 1; ++ } ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++foundit: ++ if (thisEUN != BLOCK_NIL) { ++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; ++ ++ if (MTD_READOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) ++ return -EIO; ++ bci.Status = bci.Status1 = SECTOR_DELETED; ++ if (MTD_WRITEOOB(inftl->mbd.mtd, ptr, 8, &retlen, (char *)&bci) < 0) ++ return -EIO; ++ INFTL_trydeletechain(inftl, block / (inftl->EraseSize / SECTORSIZE)); ++ } ++ return 0; ++} ++ ++static int inftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) ++{ ++ struct INFTLrecord *inftl = (void *)mbd; ++ unsigned int writeEUN; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); ++ size_t retlen; ++ u8 eccbuf[6]; ++ char *p, *pend; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_writeblock(inftl=0x%x,block=%d," ++ "buffer=0x%x)\n", (int)inftl, block, (int)buffer); ++ ++ /* Is block all zero? */ ++ pend = buffer + SECTORSIZE; ++ for (p = buffer; p < pend && !*p; p++) ++ ; ++ ++ if (p < pend) { ++ writeEUN = INFTL_findwriteunit(inftl, block); ++ ++ if (writeEUN == BLOCK_NIL) { ++ printk(KERN_WARNING "inftl_writeblock(): cannot find " ++ "block to write to\n"); ++ /* ++ * If we _still_ haven't got a block to use, ++ * we're screwed. ++ */ ++ return 1; ++ } ++ ++ MTD_WRITEECC(inftl->mbd.mtd, (writeEUN * inftl->EraseSize) + ++ blockofs, SECTORSIZE, &retlen, (char *)buffer, ++ (char *)eccbuf, NULL); ++ /* ++ * No need to write SECTOR_USED flags since they are written ++ * in mtd_writeecc ++ */ ++ } else { ++ INFTL_deleteblock(inftl, block); ++ } ++ ++ return 0; ++} ++ ++static int inftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) ++{ ++ struct INFTLrecord *inftl = (void *)mbd; ++ unsigned int thisEUN = inftl->VUtable[block / (inftl->EraseSize / SECTORSIZE)]; ++ unsigned long blockofs = (block * SECTORSIZE) & (inftl->EraseSize - 1); ++ unsigned int status; ++ int silly = MAX_LOOPS; ++ struct inftl_bci bci; ++ size_t retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: inftl_readblock(inftl=0x%x,block=%d," ++ "buffer=0x%x)\n", (int)inftl, block, (int)buffer); ++ ++ while (thisEUN < inftl->nb_blocks) { ++ if (MTD_READOOB(inftl->mbd.mtd, (thisEUN * inftl->EraseSize) + ++ blockofs, 8, &retlen, (char *)&bci) < 0) ++ status = SECTOR_IGNORE; ++ else ++ status = bci.Status | bci.Status1; ++ ++ switch (status) { ++ case SECTOR_DELETED: ++ thisEUN = BLOCK_NIL; ++ goto foundit; ++ case SECTOR_USED: ++ goto foundit; ++ case SECTOR_FREE: ++ case SECTOR_IGNORE: ++ break; ++ default: ++ printk(KERN_WARNING "INFTL: unknown status for " ++ "block %ld in EUN %d: 0x%04x\n", ++ block, thisEUN, status); ++ break; ++ } ++ ++ if (!silly--) { ++ printk(KERN_WARNING "INFTL: infinite loop in " ++ "Virtual Unit Chain 0x%lx\n", ++ block / (inftl->EraseSize / SECTORSIZE)); ++ return 1; ++ } ++ ++ thisEUN = inftl->PUtable[thisEUN]; ++ } ++ ++foundit: ++ if (thisEUN == BLOCK_NIL) { ++ /* The requested block is not on the media, return all 0x00 */ ++ memset(buffer, 0, SECTORSIZE); ++ } else { ++ size_t retlen; ++ loff_t ptr = (thisEUN * inftl->EraseSize) + blockofs; ++ u_char eccbuf[6]; ++ if (MTD_READECC(inftl->mbd.mtd, ptr, SECTORSIZE, &retlen, ++ buffer, eccbuf, NULL)) ++ return -EIO; ++ } ++ return 0; ++} ++ ++static int inftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) ++{ ++ struct INFTLrecord *inftl = (void *)dev; ++ ++ geo->heads = inftl->heads; ++ geo->sectors = inftl->sectors; ++ geo->cylinders = inftl->cylinders; ++ ++ return 0; ++} ++ ++struct mtd_blktrans_ops inftl_tr = { ++ .name = "inftl", ++ .major = INFTL_MAJOR, ++ .part_bits = INFTL_PARTN_BITS, ++ .getgeo = inftl_getgeo, ++ .readsect = inftl_readblock, ++ .writesect = inftl_writeblock, ++ .add_mtd = inftl_add_mtd, ++ .remove_dev = inftl_remove_dev, ++ .owner = THIS_MODULE, ++}; ++ ++extern char inftlmountrev[]; ++ ++int __init init_inftl(void) ++{ ++ printk(KERN_INFO "INFTL: inftlcore.c $Revision: 1.14 $, " ++ "inftlmount.c %s\n", inftlmountrev); ++ ++ return register_mtd_blktrans(&inftl_tr); ++} ++ ++static void __exit cleanup_inftl(void) ++{ ++ deregister_mtd_blktrans(&inftl_tr); ++} ++ ++module_init(init_inftl); ++module_exit(cleanup_inftl); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al."); ++MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/inftlmount.c linux/drivers/mtd/inftlmount.c +--- linux-mips-2.4.27/drivers/mtd/inftlmount.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/inftlmount.c 2004-11-19 10:25:11.633240224 +0100 +@@ -0,0 +1,817 @@ ++/* ++ * inftlmount.c -- INFTL mount code with extensive checks. ++ * ++ * Author: Greg Ungerer (gerg@snapgear.com) ++ * (C) Copyright 2002-2003, Greg Ungerer (gerg@snapgear.com) ++ * ++ * Based heavily on the nftlmount.c code which is: ++ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) ++ * Copyright (C) 2000 Netgem S.A. ++ * ++ * $Id: inftlmount.c,v 1.12 2003/06/26 07:31:36 dwmw2 Exp $ ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License as published by ++ * the Free Software Foundation; either version 2 of the License, or ++ * (at your option) any later version. ++ * ++ * This program is distributed in the hope that it will be useful, ++ * but WITHOUT ANY WARRANTY; without even the implied warranty of ++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ * GNU General Public License for more details. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <asm/errno.h> ++#include <asm/io.h> ++#include <asm/uaccess.h> ++#include <linux/miscdevice.h> ++#include <linux/pci.h> ++#include <linux/delay.h> ++#include <linux/slab.h> ++#include <linux/sched.h> ++#include <linux/init.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nftl.h> ++#include <linux/mtd/inftl.h> ++#include <linux/mtd/compatmac.h> ++ ++char inftlmountrev[]="$Revision: 1.12 $"; ++ ++/* ++ * find_boot_record: Find the INFTL Media Header and its Spare copy which ++ * contains the various device information of the INFTL partition and ++ * Bad Unit Table. Update the PUtable[] table according to the Bad ++ * Unit Table. PUtable[] is used for management of Erase Unit in ++ * other routines in inftlcore.c and inftlmount.c. ++ */ ++static int find_boot_record(struct INFTLrecord *inftl) ++{ ++ struct inftl_unittail h1; ++ //struct inftl_oob oob; ++ unsigned int i, block, boot_record_count = 0; ++ u8 buf[SECTORSIZE]; ++ struct INFTLMediaHeader *mh = &inftl->MediaHdr; ++ struct INFTLPartition *ip; ++ int retlen; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: find_boot_record(inftl=0x%x)\n", ++ (int)inftl); ++ ++ /* ++ * Assume logical EraseSize == physical erasesize for starting the ++ * scan. We'll sort it out later if we find a MediaHeader which says ++ * otherwise. ++ */ ++ inftl->EraseSize = inftl->mbd.mtd->erasesize; ++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; ++ ++ inftl->MediaUnit = BLOCK_NIL; ++ inftl->SpareMediaUnit = BLOCK_NIL; ++ ++ /* Search for a valid boot record */ ++ for (block = 0; block < inftl->nb_blocks; block++) { ++ int ret; ++ ++ /* ++ * Check for BNAND header first. Then whinge if it's found ++ * but later checks fail. ++ */ ++ if ((ret = MTD_READ(inftl->mbd.mtd, block * inftl->EraseSize, ++ SECTORSIZE, &retlen, buf))) { ++ static int warncount = 5; ++ ++ if (warncount) { ++ printk(KERN_WARNING "INFTL: block read at 0x%x " ++ "of mtd%d failed: %d\n", ++ block * inftl->EraseSize, ++ inftl->mbd.mtd->index, ret); ++ if (!--warncount) ++ printk(KERN_WARNING "INFTL: further " ++ "failures for this block will " ++ "not be printed\n"); ++ } ++ continue; ++ } ++ ++ if (retlen < 6 || memcmp(buf, "BNAND", 6)) { ++ /* BNAND\0 not found. Continue */ ++ continue; ++ } ++ ++ /* To be safer with BIOS, also use erase mark as discriminant */ ++ if ((ret = MTD_READOOB(inftl->mbd.mtd, block * inftl->EraseSize + ++ SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0)) { ++ printk(KERN_WARNING "INFTL: ANAND header found at " ++ "0x%x in mtd%d, but OOB data read failed " ++ "(err %d)\n", block * inftl->EraseSize, ++ inftl->mbd.mtd->index, ret); ++ continue; ++ } ++ ++ if (boot_record_count) { ++ /* ++ * We've already processed one. So we just check if ++ * this one is the same as the first one we found. ++ */ ++ if (memcmp(mh, buf, sizeof(struct INFTLMediaHeader))) { ++ printk(KERN_WARNING "INFTL: Media Headers at " ++ "0x%x and 0x%x disagree.\n", ++ inftl->MediaUnit * inftl->EraseSize, ++ block * inftl->EraseSize); ++ return -1; ++ } ++ if (boot_record_count == 1) ++ inftl->SpareMediaUnit = block; ++ ++ /* ++ * Mark this boot record (INFTL MediaHeader) block as ++ * reserved. ++ */ ++ inftl->PUtable[block] = BLOCK_RESERVED; ++ ++ boot_record_count++; ++ continue; ++ } ++ ++ /* ++ * This is the first we've seen. ++ * Copy the media header structure into place. ++ */ ++ memcpy(mh, buf, sizeof(struct INFTLMediaHeader)); ++ mh->NoOfBootImageBlocks = le32_to_cpu(mh->NoOfBootImageBlocks); ++ mh->NoOfBinaryPartitions = le32_to_cpu(mh->NoOfBinaryPartitions); ++ mh->NoOfBDTLPartitions = le32_to_cpu(mh->NoOfBDTLPartitions); ++ mh->BlockMultiplierBits = le32_to_cpu(mh->BlockMultiplierBits); ++ mh->FormatFlags = le32_to_cpu(mh->FormatFlags); ++ mh->PercentUsed = le32_to_cpu(mh->PercentUsed); ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { ++ printk("INFTL: Media Header ->\n" ++ " bootRecordID = %s\n" ++ " NoOfBootImageBlocks = %d\n" ++ " NoOfBinaryPartitions = %d\n" ++ " NoOfBDTLPartitions = %d\n" ++ " BlockMultiplerBits = %d\n" ++ " FormatFlgs = %d\n" ++ " OsakVersion = 0x%x\n" ++ " PercentUsed = %d\n", ++ mh->bootRecordID, mh->NoOfBootImageBlocks, ++ mh->NoOfBinaryPartitions, ++ mh->NoOfBDTLPartitions, ++ mh->BlockMultiplierBits, mh->FormatFlags, ++ mh->OsakVersion, mh->PercentUsed); ++ } ++#endif ++ ++ if (mh->NoOfBDTLPartitions == 0) { ++ printk(KERN_WARNING "INFTL: Media Header sanity check " ++ "failed: NoOfBDTLPartitions (%d) == 0, " ++ "must be at least 1\n", mh->NoOfBDTLPartitions); ++ return -1; ++ } ++ ++ if ((mh->NoOfBDTLPartitions + mh->NoOfBinaryPartitions) > 4) { ++ printk(KERN_WARNING "INFTL: Media Header sanity check " ++ "failed: Total Partitions (%d) > 4, " ++ "BDTL=%d Binary=%d\n", mh->NoOfBDTLPartitions + ++ mh->NoOfBinaryPartitions, ++ mh->NoOfBDTLPartitions, ++ mh->NoOfBinaryPartitions); ++ return -1; ++ } ++ ++ if (mh->BlockMultiplierBits > 1) { ++ printk(KERN_WARNING "INFTL: sorry, we don't support " ++ "UnitSizeFactor 0x%02x\n", ++ mh->BlockMultiplierBits); ++ return -1; ++ } else if (mh->BlockMultiplierBits == 1) { ++ printk(KERN_WARNING "INFTL: support for INFTL with " ++ "UnitSizeFactor 0x%02x is experimental\n", ++ mh->BlockMultiplierBits); ++ inftl->EraseSize = inftl->mbd.mtd->erasesize << ++ (0xff - mh->BlockMultiplierBits); ++ inftl->nb_blocks = inftl->mbd.mtd->size / inftl->EraseSize; ++ } ++ ++ /* Scan the partitions */ ++ for (i = 0; (i < 4); i++) { ++ ip = &mh->Partitions[i]; ++ ip->virtualUnits = le32_to_cpu(ip->virtualUnits); ++ ip->firstUnit = le32_to_cpu(ip->firstUnit); ++ ip->lastUnit = le32_to_cpu(ip->lastUnit); ++ ip->flags = le32_to_cpu(ip->flags); ++ ip->spareUnits = le32_to_cpu(ip->spareUnits); ++ ip->Reserved0 = le32_to_cpu(ip->Reserved0); ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) { ++ printk(" PARTITION[%d] ->\n" ++ " virtualUnits = %d\n" ++ " firstUnit = %d\n" ++ " lastUnit = %d\n" ++ " flags = 0x%x\n" ++ " spareUnits = %d\n", ++ i, ip->virtualUnits, ip->firstUnit, ++ ip->lastUnit, ip->flags, ++ ip->spareUnits); ++ } ++#endif ++ ++ if (ip->Reserved0 != ip->firstUnit) { ++ struct erase_info *instr = &inftl->instr; ++ ++ /* ++ * Most likely this is using the ++ * undocumented qiuck mount feature. ++ * We don't support that, we will need ++ * to erase the hidden block for full ++ * compatibility. ++ */ ++ instr->addr = ip->Reserved0 * inftl->EraseSize; ++ instr->len = inftl->EraseSize; ++ MTD_ERASE(inftl->mbd.mtd, instr); ++ } ++ if ((ip->lastUnit - ip->firstUnit + 1) < ip->virtualUnits) { ++ printk(KERN_WARNING "INFTL: Media Header " ++ "Partition %d sanity check failed\n" ++ " firstUnit %d : lastUnit %d > " ++ "virtualUnits %d\n", i, ip->lastUnit, ++ ip->firstUnit, ip->Reserved0); ++ return -1; ++ } ++ if (ip->Reserved1 != 0) { ++ printk(KERN_WARNING "INFTL: Media Header " ++ "Partition %d sanity check failed: " ++ "Reserved1 %d != 0\n", ++ i, ip->Reserved1); ++ return -1; ++ } ++ ++ if (ip->flags & INFTL_BDTL) ++ break; ++ } ++ ++ if (i >= 4) { ++ printk(KERN_WARNING "INFTL: Media Header Partition " ++ "sanity check failed:\n No partition " ++ "marked as Disk Partition\n"); ++ return -1; ++ } ++ ++ inftl->nb_boot_blocks = ip->firstUnit; ++ inftl->numvunits = ip->virtualUnits; ++ if (inftl->numvunits > (inftl->nb_blocks - ++ inftl->nb_boot_blocks - 2)) { ++ printk(KERN_WARNING "INFTL: Media Header sanity check " ++ "failed:\n numvunits (%d) > nb_blocks " ++ "(%d) - nb_boot_blocks(%d) - 2\n", ++ inftl->numvunits, inftl->nb_blocks, ++ inftl->nb_boot_blocks); ++ return -1; ++ } ++ ++ inftl->mbd.size = inftl->numvunits * ++ (inftl->EraseSize / SECTORSIZE); ++ ++ /* ++ * Block count is set to last used EUN (we won't need to keep ++ * any meta-data past that point). ++ */ ++ inftl->firstEUN = ip->firstUnit; ++ inftl->lastEUN = ip->lastUnit; ++ inftl->nb_blocks = ip->lastUnit + 1; ++ ++ /* Memory alloc */ ++ inftl->PUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); ++ if (!inftl->PUtable) { ++ printk(KERN_WARNING "INFTL: allocation of PUtable " ++ "failed (%d bytes)\n", ++ inftl->nb_blocks * sizeof(u16)); ++ return -ENOMEM; ++ } ++ ++ inftl->VUtable = kmalloc(inftl->nb_blocks * sizeof(u16), GFP_KERNEL); ++ if (!inftl->VUtable) { ++ kfree(inftl->PUtable); ++ printk(KERN_WARNING "INFTL: allocation of VUtable " ++ "failed (%d bytes)\n", ++ inftl->nb_blocks * sizeof(u16)); ++ return -ENOMEM; ++ } ++ ++ /* Mark the blocks before INFTL MediaHeader as reserved */ ++ for (i = 0; i < inftl->nb_boot_blocks; i++) ++ inftl->PUtable[i] = BLOCK_RESERVED; ++ /* Mark all remaining blocks as potentially containing data */ ++ for (; i < inftl->nb_blocks; i++) ++ inftl->PUtable[i] = BLOCK_NOTEXPLORED; ++ ++ /* Mark this boot record (NFTL MediaHeader) block as reserved */ ++ inftl->PUtable[block] = BLOCK_RESERVED; ++ ++#if 0 ++ /* Read Bad Erase Unit Table and modify PUtable[] accordingly */ ++ for (i = 0; i < inftl->nb_blocks; i++) { ++ if ((i & (SECTORSIZE - 1)) == 0) { ++ /* read one sector for every SECTORSIZE of blocks */ ++ if ((ret = MTD_READECC(inftl->mbd.mtd, ++ block * inftl->EraseSize + i + SECTORSIZE, ++ SECTORSIZE, &retlen, buf, ++ (char *)&oob, NULL)) < 0) { ++ printk(KERN_WARNING "INFTL: read of " ++ "bad sector table failed " ++ "(err %d)\n", ret); ++ kfree(inftl->VUtable); ++ kfree(inftl->PUtable); ++ return -1; ++ } ++ } ++ /* Mark the Bad Erase Unit as RESERVED in PUtable */ ++ if (buf[i & (SECTORSIZE - 1)] != 0xff) ++ inftl->PUtable[i] = BLOCK_RESERVED; ++ } ++#endif ++ ++ inftl->MediaUnit = block; ++ boot_record_count++; ++ } ++ ++ return boot_record_count ? 0 : -1; ++} ++ ++static int memcmpb(void *a, int c, int n) ++{ ++ int i; ++ for (i = 0; i < n; i++) { ++ if (c != ((unsigned char *)a)[i]) ++ return 1; ++ } ++ return 0; ++} ++ ++/* ++ * check_free_sector: check if a free sector is actually FREE, ++ * i.e. All 0xff in data and oob area. ++ */ ++static int check_free_sectors(struct INFTLrecord *inftl, unsigned int address, ++ int len, int check_oob) ++{ ++ int i, retlen; ++ u8 buf[SECTORSIZE]; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: check_free_sectors(inftl=0x%x," ++ "address=0x%x,len=%d,check_oob=%d)\n", (int)inftl, ++ address, len, check_oob); ++ ++ for (i = 0; i < len; i += SECTORSIZE) { ++ /* ++ * We want to read the sector without ECC check here since a ++ * free sector does not have ECC syndrome on it yet. ++ */ ++ if (MTD_READ(inftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0) ++ return -1; ++ if (memcmpb(buf, 0xff, SECTORSIZE) != 0) ++ return -1; ++ ++ if (check_oob) { ++ if (MTD_READOOB(inftl->mbd.mtd, address, ++ inftl->mbd.mtd->oobsize, &retlen, buf) < 0) ++ return -1; ++ if (memcmpb(buf, 0xff, inftl->mbd.mtd->oobsize) != 0) ++ return -1; ++ } ++ address += SECTORSIZE; ++ } ++ ++ return 0; ++} ++ ++/* ++ * INFTL_format: format a Erase Unit by erasing ALL Erase Zones in the Erase ++ * Unit and Update INFTL metadata. Each erase operation is ++ * checked with check_free_sectors. ++ * ++ * Return: 0 when succeed, -1 on error. ++ * ++ * ToDo: 1. Is it neceressary to check_free_sector after erasing ?? ++ * 2. UnitSizeFactor != 0xFF ++ */ ++int INFTL_formatblock(struct INFTLrecord *inftl, int block) ++{ ++ int retlen; ++ struct inftl_unittail uci; ++ struct erase_info *instr = &inftl->instr; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_formatblock(inftl=0x%x," ++ "block=%d)\n", (int)inftl, block); ++ ++ memset(instr, 0, sizeof(struct erase_info)); ++ ++ /* FIXME: Shouldn't we be setting the 'discarded' flag to zero ++ _first_? */ ++ ++ /* Use async erase interface, test return code */ ++ instr->addr = block * inftl->EraseSize; ++ instr->len = inftl->EraseSize; ++ MTD_ERASE(inftl->mbd.mtd, instr); ++ ++ if (instr->state == MTD_ERASE_FAILED) { ++ /* ++ * Could not format, FixMe: We should update the BadUnitTable ++ * both in memory and on disk. ++ */ ++ printk(KERN_WARNING "INFTL: error while formatting block %d\n", ++ block); ++ return -1; ++ } ++ ++ /* ++ * Check the "freeness" of Erase Unit before updating metadata. ++ * FixMe: is this check really necessary? Since we have check the ++ * return code after the erase operation. ++ */ ++ if (check_free_sectors(inftl, instr->addr, inftl->EraseSize, 1) != 0) ++ return -1; ++ ++ uci.EraseMark = cpu_to_le16(ERASE_MARK); ++ uci.EraseMark1 = cpu_to_le16(ERASE_MARK); ++ uci.Reserved[0] = 0; ++ uci.Reserved[1] = 0; ++ uci.Reserved[2] = 0; ++ uci.Reserved[3] = 0; ++ if (MTD_WRITEOOB(inftl->mbd.mtd, block * inftl->EraseSize + SECTORSIZE * 2 + ++ 8, 8, &retlen, (char *)&uci) < 0) ++ return -1; ++ return 0; ++} ++ ++/* ++ * format_chain: Format an invalid Virtual Unit chain. It frees all the Erase ++ * Units in a Virtual Unit Chain, i.e. all the units are disconnected. ++ * ++ * Since the chain is invalid then we will have to erase it from its ++ * head (normally for INFTL we go from the oldest). But if it has a ++ * loop then there is no oldest... ++ */ ++static void format_chain(struct INFTLrecord *inftl, unsigned int first_block) ++{ ++ unsigned int block = first_block, block1; ++ ++ printk(KERN_WARNING "INFTL: formatting chain at block %d\n", ++ first_block); ++ ++ for (;;) { ++ block1 = inftl->PUtable[block]; ++ ++ printk(KERN_WARNING "INFTL: formatting block %d\n", block); ++ if (INFTL_formatblock(inftl, block) < 0) { ++ /* ++ * Cannot format !!!! Mark it as Bad Unit, ++ * FixMe: update the BadUnitTable on disk. ++ */ ++ inftl->PUtable[block] = BLOCK_RESERVED; ++ } else { ++ inftl->PUtable[block] = BLOCK_FREE; ++ } ++ ++ /* Goto next block on the chain */ ++ block = block1; ++ ++ if (block == BLOCK_NIL || block >= inftl->lastEUN) ++ break; ++ } ++} ++ ++void INFTL_dumptables(struct INFTLrecord *s) ++{ ++ int i; ++ ++ printk("-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("VUtable[%d] ->", s->nb_blocks); ++ for (i = 0; i < s->nb_blocks; i++) { ++ if ((i % 8) == 0) ++ printk("\n%04x: ", i); ++ printk("%04x ", s->VUtable[i]); ++ } ++ ++ printk("\n-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("PUtable[%d-%d=%d] ->", s->firstEUN, s->lastEUN, s->nb_blocks); ++ for (i = 0; i <= s->lastEUN; i++) { ++ if ((i % 8) == 0) ++ printk("\n%04x: ", i); ++ printk("%04x ", s->PUtable[i]); ++ } ++ ++ printk("\n-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("INFTL ->\n" ++ " EraseSize = %d\n" ++ " h/s/c = %d/%d/%d\n" ++ " numvunits = %d\n" ++ " firstEUN = %d\n" ++ " lastEUN = %d\n" ++ " numfreeEUNs = %d\n" ++ " LastFreeEUN = %d\n" ++ " nb_blocks = %d\n" ++ " nb_boot_blocks = %d", ++ s->EraseSize, s->heads, s->sectors, s->cylinders, ++ s->numvunits, s->firstEUN, s->lastEUN, s->numfreeEUNs, ++ s->LastFreeEUN, s->nb_blocks, s->nb_boot_blocks); ++ ++ printk("\n-------------------------------------------" ++ "----------------------------------\n"); ++} ++ ++void INFTL_dumpVUchains(struct INFTLrecord *s) ++{ ++ int logical, block, i; ++ ++ printk("-------------------------------------------" ++ "----------------------------------\n"); ++ ++ printk("INFTL Virtual Unit Chains:\n"); ++ for (logical = 0; logical < s->nb_blocks; logical++) { ++ block = s->VUtable[logical]; ++ if (block > s->nb_blocks) ++ continue; ++ printk(" LOGICAL %d --> %d ", logical, block); ++ for (i = 0; i < s->nb_blocks; i++) { ++ if (s->PUtable[block] == BLOCK_NIL) ++ break; ++ block = s->PUtable[block]; ++ printk("%d ", block); ++ } ++ printk("\n"); ++ } ++ ++ printk("-------------------------------------------" ++ "----------------------------------\n"); ++} ++ ++int INFTL_mount(struct INFTLrecord *s) ++{ ++ unsigned int block, first_block, prev_block, last_block; ++ unsigned int first_logical_block, logical_block, erase_mark; ++ int chain_length, do_format_chain; ++ struct inftl_unithead1 h0; ++ struct inftl_unittail h1; ++ int i, retlen; ++ u8 *ANACtable, ANAC; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: INFTL_mount(inftl=0x%x)\n", (int)s); ++ ++ /* Search for INFTL MediaHeader and Spare INFTL Media Header */ ++ if (find_boot_record(s) < 0) { ++ printk(KERN_WARNING "INFTL: could not find valid boot record?\n"); ++ return -1; ++ } ++ ++ /* Init the logical to physical table */ ++ for (i = 0; i < s->nb_blocks; i++) ++ s->VUtable[i] = BLOCK_NIL; ++ ++ logical_block = block = BLOCK_NIL; ++ ++ /* Temporary buffer to store ANAC numbers. */ ++ ANACtable = kmalloc(s->nb_blocks * sizeof(u8), GFP_KERNEL); ++ memset(ANACtable, 0, s->nb_blocks); ++ ++ /* ++ * First pass is to explore each physical unit, and construct the ++ * virtual chains that exist (newest physical unit goes into VUtable). ++ * Any block that is in any way invalid will be left in the ++ * NOTEXPLORED state. Then at the end we will try to format it and ++ * mark it as free. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 1, explore each unit\n"); ++ for (first_block = s->firstEUN; first_block <= s->lastEUN; first_block++) { ++ if (s->PUtable[first_block] != BLOCK_NOTEXPLORED) ++ continue; ++ ++ do_format_chain = 0; ++ first_logical_block = BLOCK_NIL; ++ last_block = BLOCK_NIL; ++ block = first_block; ++ ++ for (chain_length = 0; ; chain_length++) { ++ ++ if ((chain_length == 0) && ++ (s->PUtable[block] != BLOCK_NOTEXPLORED)) { ++ /* Nothing to do here, onto next block */ ++ break; ++ } ++ ++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, ++ 8, &retlen, (char *)&h0) < 0 || ++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize + ++ 2 * SECTORSIZE + 8, 8, &retlen, (char *)&h1) < 0) { ++ /* Should never happen? */ ++ do_format_chain++; ++ break; ++ } ++ ++ logical_block = le16_to_cpu(h0.virtualUnitNo); ++ prev_block = le16_to_cpu(h0.prevUnitNo); ++ erase_mark = le16_to_cpu((h1.EraseMark | h1.EraseMark1)); ++ ANACtable[block] = h0.ANAC; ++ ++ /* Previous block is relative to start of Partition */ ++ if (prev_block < s->nb_blocks) ++ prev_block += s->firstEUN; ++ ++ /* Already explored partial chain? */ ++ if (s->PUtable[block] != BLOCK_NOTEXPLORED) { ++ /* Check if chain for this logical */ ++ if (logical_block == first_logical_block) { ++ if (last_block != BLOCK_NIL) ++ s->PUtable[last_block] = block; ++ } ++ break; ++ } ++ ++ /* Check for invalid block */ ++ if (erase_mark != ERASE_MARK) { ++ printk(KERN_WARNING "INFTL: corrupt block %d " ++ "in chain %d, chain length %d, erase " ++ "mark 0x%x?\n", block, first_block, ++ chain_length, erase_mark); ++ /* ++ * Assume end of chain, probably incomplete ++ * fold/erase... ++ */ ++ if (chain_length == 0) ++ do_format_chain++; ++ break; ++ } ++ ++ /* Check for it being free already then... */ ++ if ((logical_block == BLOCK_FREE) || ++ (logical_block == BLOCK_NIL)) { ++ s->PUtable[block] = BLOCK_FREE; ++ break; ++ } ++ ++ /* Sanity checks on block numbers */ ++ if ((logical_block >= s->nb_blocks) || ++ ((prev_block >= s->nb_blocks) && ++ (prev_block != BLOCK_NIL))) { ++ if (chain_length > 0) { ++ printk(KERN_WARNING "INFTL: corrupt " ++ "block %d in chain %d?\n", ++ block, first_block); ++ do_format_chain++; ++ } ++ break; ++ } ++ ++ if (first_logical_block == BLOCK_NIL) { ++ first_logical_block = logical_block; ++ } else { ++ if (first_logical_block != logical_block) { ++ /* Normal for folded chain... */ ++ break; ++ } ++ } ++ ++ /* ++ * Current block is valid, so if we followed a virtual ++ * chain to get here then we can set the previous ++ * block pointer in our PUtable now. Then move onto ++ * the previous block in the chain. ++ */ ++ s->PUtable[block] = BLOCK_NIL; ++ if (last_block != BLOCK_NIL) ++ s->PUtable[last_block] = block; ++ last_block = block; ++ block = prev_block; ++ ++ /* Check for end of chain */ ++ if (block == BLOCK_NIL) ++ break; ++ ++ /* Validate next block before following it... */ ++ if (block > s->lastEUN) { ++ printk(KERN_WARNING "INFTL: invalid previous " ++ "block %d in chain %d?\n", block, ++ first_block); ++ do_format_chain++; ++ break; ++ } ++ } ++ ++ if (do_format_chain) { ++ format_chain(s, first_block); ++ continue; ++ } ++ ++ /* ++ * Looks like a valid chain then. It may not really be the ++ * newest block in the chain, but it is the newest we have ++ * found so far. We might update it in later iterations of ++ * this loop if we find something newer. ++ */ ++ s->VUtable[first_logical_block] = first_block; ++ logical_block = BLOCK_NIL; ++ } ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) ++ INFTL_dumptables(s); ++#endif ++ ++ /* ++ * Second pass, check for infinite loops in chains. These are ++ * possible because we don't update the previous pointers when ++ * we fold chains. No big deal, just fix them up in PUtable. ++ */ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 2, validate virtual chains\n"); ++ for (logical_block = 0; logical_block < s->numvunits; logical_block++) { ++ block = s->VUtable[logical_block]; ++ last_block = BLOCK_NIL; ++ ++ /* Check for free/reserved/nil */ ++ if (block >= BLOCK_RESERVED) ++ continue; ++ ++ ANAC = ANACtable[block]; ++ for (i = 0; i < s->numvunits; i++) { ++ if (s->PUtable[block] == BLOCK_NIL) ++ break; ++ if (s->PUtable[block] > s->lastEUN) { ++ printk(KERN_WARNING "INFTL: invalid prev %d, " ++ "in virtual chain %d\n", ++ s->PUtable[block], logical_block); ++ s->PUtable[block] = BLOCK_NIL; ++ ++ } ++ if (ANACtable[block] != ANAC) { ++ /* ++ * Chain must point back to itself. This is ok, ++ * but we will need adjust the tables with this ++ * newest block and oldest block. ++ */ ++ s->VUtable[logical_block] = block; ++ s->PUtable[last_block] = BLOCK_NIL; ++ break; ++ } ++ ++ ANAC--; ++ last_block = block; ++ block = s->PUtable[block]; ++ } ++ ++ if (i >= s->nb_blocks) { ++ /* ++ * Uhoo, infinite chain with valid ANACS! ++ * Format whole chain... ++ */ ++ format_chain(s, first_block); ++ } ++ } ++ ++#ifdef CONFIG_MTD_DEBUG_VERBOSE ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) ++ INFTL_dumptables(s); ++ if (CONFIG_MTD_DEBUG_VERBOSE >= 2) ++ INFTL_dumpVUchains(s); ++#endif ++ ++ /* ++ * Third pass, format unreferenced blocks and init free block count. ++ */ ++ s->numfreeEUNs = 0; ++ s->LastFreeEUN = BLOCK_NIL; ++ ++ DEBUG(MTD_DEBUG_LEVEL3, "INFTL: pass 3, format unused blocks\n"); ++ for (block = s->firstEUN; block <= s->lastEUN; block++) { ++ if (s->PUtable[block] == BLOCK_NOTEXPLORED) { ++ printk("INFTL: unreferenced block %d, formatting it\n", ++ block); ++ if (INFTL_formatblock(s, block) < 0) ++ s->PUtable[block] = BLOCK_RESERVED; ++ else ++ s->PUtable[block] = BLOCK_FREE; ++ } ++ if (s->PUtable[block] == BLOCK_FREE) { ++ s->numfreeEUNs++; ++ if (s->LastFreeEUN == BLOCK_NIL) ++ s->LastFreeEUN = block; ++ } ++ } ++ ++ kfree(ANACtable); ++ return 0; ++} +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/Config.in linux/drivers/mtd/maps/Config.in +--- linux-mips-2.4.27/drivers/mtd/maps/Config.in 2004-02-26 01:46:35.000000000 +0100 ++++ linux/drivers/mtd/maps/Config.in 2004-11-19 10:25:11.888201464 +0100 +@@ -1,17 +1,14 @@ + # drivers/mtd/maps/Config.in + +-# $Id: Config.in,v 1.43 2003/01/24 14:26:38 dwmw2 Exp $ ++# $Id: Config.in,v 1.63 2003/11/26 21:26:09 dsaxena Exp $ + + mainmenu_option next_comment + + comment 'Mapping drivers for chip access' + +-dep_tristate ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE +-if [ "$CONFIG_MTD_PHYSMAP" = "y" -o "$CONFIG_MTD_PHYSMAP" = "m" ]; then +- hex ' Physical start address of flash mapping' CONFIG_MTD_PHYSMAP_START 0x8000000 +- hex ' Physical length of flash mapping' CONFIG_MTD_PHYSMAP_LEN 0x4000000 +- int ' Bus width in octets' CONFIG_MTD_PHYSMAP_BUSWIDTH 2 +-fi ++bool ' Support for non-linear mappings of flash chips' CONFIG_MTD_COMPLEX_MAPPINGS ++ ++bool ' CFI Flash device in physical memory map' CONFIG_MTD_PHYSMAP $CONFIG_MTD_GEN_PROBE + + if [ "$CONFIG_SPARC" = "y" -o "$CONFIG_SPARC64" = "y" ]; then + dep_tristate ' Sun Microsystems userflash support' CONFIG_MTD_SUN_UFLASH $CONFIG_MTD_CFI +@@ -21,56 +18,68 @@ + dep_tristate ' CFI Flash device mapped on Photron PNC-2000' CONFIG_MTD_PNC2000 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS + dep_tristate ' CFI Flash device mapped on AMD SC520 CDP' CONFIG_MTD_SC520CDP $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on AMD NetSc520' CONFIG_MTD_NETSC520 $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS ++ dep_tristate ' CFI Flash device mapped on Arcom SBC-GXx boards' CONFIG_MTD_SBC_GXX $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS ++ dep_tristate ' CFI Flash device mapped on Arcom ELAN-104NC' CONFIG_MTD_ELAN_104NC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on DIL/Net PC' CONFIG_MTD_DILNETPC $CONFIG_MTD_CFI_INTELEXT $CONFIG_MTD_PARTITIONS $CONFIG_MTD_CONCAT + if [ "$CONFIG_MTD_DILNETPC" = "y" -o "$CONFIG_MTD_DILNETPC" = "m" ]; then + hex ' Size of boot partition' CONFIG_MTD_DILNETPC_BOOTSIZE 0x80000 + fi +- dep_tristate ' JEDEC Flash device mapped on Mixcom piggyback card' CONFIG_MTD_MIXMEM $CONFIG_MTD_JEDEC +- dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC +- dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC ++ dep_tristate ' JEDEC Flash device mapped on Octagon 5066 SBC' CONFIG_MTD_OCTAGON $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS ++ dep_tristate ' JEDEC Flash device mapped on Tempustech VMAX SBC301' CONFIG_MTD_VMAX $CONFIG_MTD_JEDEC $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' Flash device mapped with DOCCS on NatSemi SCx200' CONFIG_MTD_SCx200_DOCFLASH $CONFIG_MTD_CFI + dep_tristate ' BIOS flash chip on Intel L440GX boards' CONFIG_MTD_L440GX $CONFIG_MTD_JEDECPROBE + dep_tristate ' ROM connected to AMD76X southbridge' CONFIG_MTD_AMD76XROM $CONFIG_MTD_GEN_PROBE +- dep_tristate ' ROM connected to Intel Hub Controller 2' CONFIG_MTD_ICH2ROM $CONFIG_MTD_JEDECPROBE ++ dep_tristate ' ROM connected to Intel Hub Controller 2/3/4/5' CONFIG_MTD_ICHXROM $CONFIG_MTD_JEDECPROBE $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on SnapGear/SecureEdge' CONFIG_MTD_NETtel $CONFIG_MTD_PARTITIONS + dep_tristate ' BIOS flash chip on Intel SCB2 boards' CONFIG_MTD_SCB2_FLASH $CONFIG_MTD_GEN_PROBE + fi + +-if [ "$CONFIG_PPC" = "y" ]; then +- dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI $CONFIG_TQM8xxL ++if [ "$CONFIG_PPC32" = "y" ]; then ++ if [ "$CONFIG_6xx" = "y" -a "$CONFIG_8260" = "y" ]; then ++ dep_tristate ' Flash device on SBC8240' CONFIG_MTD_SBC8240 $CONFIG_MTD_JEDECPROBE ++ fi ++ if [ "$CONFIG_8xx" = "y" ]; then ++ if [ "$CONFIG_TQM8xxL" = "y" ]; then ++ dep_tristate ' CFI Flash device mapped on TQM8XXL' CONFIG_MTD_TQM8XXL $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_RPXLITE" = "y" -o "$CONFIG_RPXCLASSIC" = "y" ]; then + dep_tristate ' CFI Flash device mapped on RPX Lite or CLLF' CONFIG_MTD_RPXLITE $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_MBX" = "y" ]; then + dep_tristate ' System flash on MBX860 board' CONFIG_MTD_MBX860 $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_DBOX2" = "y" ]; then + dep_tristate ' CFI Flash device mapped on D-Box2' CONFIG_MTD_DBOX2 $CONFIG_MTD_CFI ++ fi + dep_tristate ' CFI Flash device mapping on FlagaDM' CONFIG_MTD_CFI_FLAGADM $CONFIG_MTD_CFI +- dep_tristate ' CFI Flash device mapped on IBM Redwood-4/5' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI ++ fi ++ if [ "$CONFIG_4xx" = "y" ]; then ++ if [ "$CONFIG_40x" = "y" ]; then ++ if [ "$CONFIG_REDWOOD_4" = "y" -o "$CONFIG_REDWOOD_5" = "y" -o "$CONFIG_REDWOOD_6" = "y" ]; then ++ dep_tristate ' CFI Flash device mapped on IBM Redwood' CONFIG_MTD_REDWOOD $CONFIG_MTD_CFI ++ fi ++ dep_tristate ' CFI Flash device mapped on IBM Beech' CONFIG_MTD_BEECH $CONFIG_MTD_CFI $CONFIG_BEECH ++ dep_tristate ' CFI Flash device mapped on IBM Arctic' CONFIG_MTD_ARCTIC $CONFIG_MTD_CFI $CONFIG_ARCTIC2 ++ fi ++ if [ "$CONFIG_440" = "y" ]; then ++ dep_tristate ' Flash devices mapped on IBM Ebony' CONFIG_MTD_EBONY $CONFIG_MTD_CFI $CONFIG_EBONY ++ fi ++ fi + fi + +-if [ "$CONFIG_MIPS" = "y" ]; then +- dep_tristate ' Pb1000 MTD support' CONFIG_MTD_PB1000 $CONFIG_MIPS_PB1000 +- dep_tristate ' Pb1500 MTD support' CONFIG_MTD_PB1500 $CONFIG_MIPS_PB1500 +- dep_tristate ' Pb1100 MTD support' CONFIG_MTD_PB1100 $CONFIG_MIPS_PB1100 +- dep_tristate ' Bosporus MTD support' CONFIG_MTD_BOSPORUS $CONFIG_MIPS_BOSPORUS +- dep_tristate ' XXS1500 boot flash device' CONFIG_MTD_XXS1500 $CONFIG_MIPS_XXS1500 +- dep_tristate ' MTX-1 flash device' CONFIG_MTD_MTX1 $CONFIG_MIPS_MTX1 +- if [ "$CONFIG_MTD_PB1500" = "y" -o "$CONFIG_MTD_PB1500" = "m" \ +- -o "$CONFIG_MTD_PB1100" = "y" -o "$CONFIG_MTD_PB1100" = "m" ]; then +- bool ' Pb[15]00 boot flash device' CONFIG_MTD_PB1500_BOOT +- bool ' Pb[15]00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER ++if [ "$CONFIG_MIPS" = "y" -o "$CONFIG_MIPS64" = "y" ]; then ++ if [ "$CONFIG_MIPS_PB1000" = "y" -o "$CONFIG_MIPS_PB1100" = "y" -o "$CONFIG_MIPS_PB1500" = "y" ]; then ++ tristate ' Pb1x00 MTD support' CONFIG_MTD_PB1XXX ++ if [ "$CONFIG_MIPS_PB1500" = "y" -o "$CONFIG_MIPS_PB1100" = "m" ]; then ++ bool ' Pb1x00 boot flash device' CONFIG_MTD_PB1500_BOOT ++ bool ' Pb1x00 user flash device (2nd 32MiB bank)' CONFIG_MTD_PB1500_USER ++ fi + fi + tristate ' Db1x00 MTD support' CONFIG_MTD_DB1X00 + if [ "$CONFIG_MTD_DB1X00" = "y" -o "$CONFIG_MTD_DB1X00" = "m" ]; then + bool ' Db1x00 boot flash device' CONFIG_MTD_DB1X00_BOOT + bool ' Db1x00 user flash device (2nd bank)' CONFIG_MTD_DB1X00_USER + fi +- tristate ' Pb1550 MTD support' CONFIG_MTD_PB1550 +- if [ "$CONFIG_MTD_PB1550" = "y" -o "$CONFIG_MTD_PB1550" = "m" ]; then +- bool ' Pb1550 Boot Flash' CONFIG_MTD_PB1550_BOOT +- bool ' Pb1550 User Parameter Flash' CONFIG_MTD_PB1550_USER +- fi +- dep_tristate ' Hydrogen 3 MTD support' CONFIG_MTD_HYDROGEN3 $CONFIG_MIPS_HYDROGEN3 +- dep_tristate ' Mirage MTD support' CONFIG_MTD_MIRAGE $CONFIG_MIPS_MIRAGE + dep_tristate ' Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board' CONFIG_MTD_CSTM_MIPS_IXX $CONFIG_MTD_CFI $CONFIG_MTD_JEDEC $CONFIG_MTD_PARTITIONS + if [ "$CONFIG_MTD_CSTM_MIPS_IXX" = "y" -o "$CONFIG_MTD_CSTM_MIPS_IXX" = "m" ]; then + hex ' Physical start address of flash mapping' CONFIG_MTD_CSTM_MIPS_IXX_START 0x8000000 +@@ -78,7 +87,7 @@ + int ' Bus width in octets' CONFIG_MTD_CSTM_MIPS_IXX_BUSWIDTH 2 + fi + dep_tristate ' Momenco Ocelot boot flash device' CONFIG_MTD_OCELOT $CONFIG_MOMENCO_OCELOT +- dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_MTD_CFI $CONFIG_LASAT ++ dep_tristate ' LASAT flash device' CONFIG_MTD_LASAT $CONFIG_LASAT + fi + + if [ "$CONFIG_SUPERH" = "y" ]; then +@@ -90,21 +99,24 @@ + fi + + if [ "$CONFIG_ARM" = "y" ]; then +- dep_tristate ' CFI Flash device mapped on Nora' CONFIG_MTD_NORA $CONFIG_MTD_CFI + dep_tristate ' CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI + dep_tristate ' Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712 + dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS +- dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE ++ dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310 ++ dep_tristate ' CFI Flash device mapped on the XScale Lubbock board' CONFIG_MTD_LUBBOCK $CONFIG_MTD_CFI $CONFIG_ARCH_LUBBOCK ++ dep_tristate ' CFI Flash device mapped on XScale IXP425 systems' CONFIG_MTD_IXP425 $CONFIG_MTD_CFI $CONFIG_MTD_COMPLEX_MAPPINGS + dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT + dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET + dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12 + dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI ++ dep_tristate ' CFI Flash device mapped on Hynix evaluation boards' CONFIG_MTD_H720X $CONFIG_MTD_CFI + dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE + dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA ++ dep_tristate ' NOR Flash device on TOTO board' CONFIG_MTD_NOR_TOTO $CONFIG_MTD $CONFIG_OMAP_TOTO + fi + if [ "$CONFIG_ALPHA" = "y" ]; then +- dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE ++ dep_tristate ' Flash chip mapping on TSUNAMI' CONFIG_MTD_TSUNAMI $CONFIG_MTD_GENPROBE $CONFIG_MTD_COMPLEX_MAPPINGS + fi + + if [ "$CONFIG_UCLINUX" = "y" ]; then +@@ -112,7 +124,7 @@ + fi + + # This needs CFI or JEDEC, depending on the cards found. +-dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI +-dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA ++dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI $CONFIG_MTD_COMPLEX_MAPPINGS ++dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA $CONFIG_MTD_COMPLEX_MAPPINGS + + endmenu +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/Makefile linux/drivers/mtd/maps/Makefile +--- linux-mips-2.4.27/drivers/mtd/maps/Makefile 2004-02-26 01:46:35.000000000 +0100 ++++ linux/drivers/mtd/maps/Makefile 2004-11-19 10:25:11.890201160 +0100 +@@ -1,12 +1,16 @@ + # + # linux/drivers/maps/Makefile + # +-# $Id: Makefile,v 1.37 2003/01/24 14:26:38 dwmw2 Exp $ ++# $Id: Makefile.common,v 1.8 2003/11/26 21:26:09 dsaxena Exp $ + +-BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/) +- +-ifeq ($(BELOW25),y) ++ifeq ($(PATCHLEVEL),4) + O_TARGET := mapslink.o ++export-objs := map_funcs.o ++endif ++ ++ ++ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) ++obj-$(CONFIG_MTD) += map_funcs.o + endif + + # Chip mappings +@@ -21,19 +25,13 @@ + obj-$(CONFIG_MTD_IQ80310) += iq80310.o + obj-$(CONFIG_MTD_L440GX) += l440gx.o + obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o +-obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o ++obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o + obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o ++obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o + obj-$(CONFIG_MTD_MBX860) += mbx860.o +-obj-$(CONFIG_MTD_NORA) += nora.o + obj-$(CONFIG_MTD_CEIVA) += ceiva.o + obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o +-ifneq ($(CONFIG_MTD_PHYSMAP),n) +- ifeq ($(CONFIG_MTD_PHYSMAP_BUSWIDTH),8) +- obj-$(CONFIG_MTD_PHYSMAP) += physmap64.o +- else +- obj-$(CONFIG_MTD_PHYSMAP) += physmap.o +- endif +-endif ++obj-$(CONFIG_MTD_PHYSMAP) += physmap.o + obj-$(CONFIG_MTD_PNC2000) += pnc2000.o + obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o + obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o +@@ -49,16 +47,9 @@ + obj-$(CONFIG_MTD_OCELOT) += ocelot.o + obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o + obj-$(CONFIG_MTD_PCI) += pci.o +-obj-$(CONFIG_MTD_PB1000) += pb1xxx-flash.o +-obj-$(CONFIG_MTD_PB1100) += pb1xxx-flash.o +-obj-$(CONFIG_MTD_PB1500) += pb1xxx-flash.o +-obj-$(CONFIG_MTD_XXS1500) += xxs1500.o +-obj-$(CONFIG_MTD_MTX1) += mtx-1.o +-obj-$(CONFIG_MTD_LASAT) += lasat.o ++obj-$(CONFIG_MTD_PB1XXX) += pb1xxx-flash.o + obj-$(CONFIG_MTD_DB1X00) += db1x00-flash.o +-obj-$(CONFIG_MTD_PB1550) += pb1550-flash.o +-obj-$(CONFIG_MTD_HYDROGEN3) += hydrogen3-flash.o +-obj-$(CONFIG_MTD_BOSPORUS) += pb1xxx-flash.o ++obj-$(CONFIG_MTD_LASAT) += lasat.o + obj-$(CONFIG_MTD_AUTCPU12) += autcpu12-nvram.o + obj-$(CONFIG_MTD_EDB7312) += edb7312.o + obj-$(CONFIG_MTD_IMPA7) += impa7.o +@@ -67,6 +58,13 @@ + obj-$(CONFIG_MTD_UCLINUX) += uclinux.o + obj-$(CONFIG_MTD_NETtel) += nettel.o + obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o +-obj-$(CONFIG_MTD_MIRAGE) += mirage-flash.o ++obj-$(CONFIG_MTD_EBONY) += ebony.o ++obj-$(CONFIG_MTD_BEECH) += beech-mtd.o ++obj-$(CONFIG_MTD_ARCTIC) += arctic-mtd.o ++obj-$(CONFIG_MTD_H720X) += h720x-flash.o ++obj-$(CONFIG_MTD_SBC8240) += sbc8240.o ++obj-$(CONFIG_MTD_NOR_TOTO) += omap-toto-flash.o ++obj-$(CONFIG_MTD_MPC1211) += mpc1211.o ++obj-$(CONFIG_MTD_IXP425) += ixp425.o + +-include $(TOPDIR)/Rules.make ++-include $(TOPDIR)/Rules.make +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/amd76xrom.c linux/drivers/mtd/maps/amd76xrom.c +--- linux-mips-2.4.27/drivers/mtd/maps/amd76xrom.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/amd76xrom.c 2004-11-19 10:25:11.891201008 +0100 +@@ -2,12 +2,13 @@ + * amd76xrom.c + * + * Normal mappings of chips in physical memory +- * $Id: amd76xrom.c,v 1.1 2002/10/18 22:45:48 eric Exp $ ++ * $Id: amd76xrom.c,v 1.9 2003/10/23 23:10:59 thayne Exp $ + */ + + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -16,77 +17,59 @@ + #include <linux/pci_ids.h> + + ++#define xstr(s) str(s) ++#define str(s) #s ++#define MOD_NAME xstr(KBUILD_BASENAME) ++ ++#define MTD_DEV_NAME_LENGTH 16 ++ + struct amd76xrom_map_info { + struct map_info map; + struct mtd_info *mtd; + unsigned long window_addr; + u32 window_start, window_size; + struct pci_dev *pdev; ++ struct resource window_rsrc; ++ struct resource rom_rsrc; ++ char mtd_name[MTD_DEV_NAME_LENGTH]; + }; + +-static __u8 amd76xrom_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 amd76xrom_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} + +-static __u32 amd76xrom_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} ++static struct amd76xrom_map_info amd76xrom_map = { ++ .map = { ++ .name = MOD_NAME, ++ .size = 0, ++ .buswidth = 1, ++ } ++ /* remaining fields of structure are initialized to 0 */ ++}; + +-static void amd76xrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} + +-static void amd76xrom_write8(struct map_info *map, __u8 d, unsigned long adr) ++static void amd76xrom_cleanup(struct amd76xrom_map_info *info) + { +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} ++ u8 byte; + +-static void amd76xrom_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} ++ /* Disable writes through the rom window */ ++ pci_read_config_byte(info->pdev, 0x40, &byte); ++ pci_write_config_byte(info->pdev, 0x40, byte & ~1); + +-static void amd76xrom_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} ++ if (info->mtd) { ++ del_mtd_device(info->mtd); ++ map_destroy(info->mtd); ++ info->mtd = NULL; ++ info->map.virt = 0; ++ } ++ if (info->rom_rsrc.parent) ++ release_resource(&info->rom_rsrc); ++ if (info->window_rsrc.parent) ++ release_resource(&info->window_rsrc); + +-static void amd76xrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); ++ if (info->window_addr) { ++ iounmap((void *)(info->window_addr)); ++ info->window_addr = 0; ++ } + } + +-static struct amd76xrom_map_info amd76xrom_map = { +- map: { +- name: "AMD76X rom", +- size: 0, +- buswidth: 1, +- read8: amd76xrom_read8, +- read16: amd76xrom_read16, +- read32: amd76xrom_read32, +- copy_from: amd76xrom_copy_from, +- write8: amd76xrom_write8, +- write16: amd76xrom_write16, +- write32: amd76xrom_write32, +- copy_to: amd76xrom_copy_to, +- /* The standard rom socket is for single power supply chips +- * that don't have an extra vpp. +- */ +- }, +- mtd: 0, +- window_addr: 0, +-}; + + static int __devinit amd76xrom_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +@@ -97,6 +80,10 @@ + u8 segen_bits; + }; + static struct rom_window rom_window[] = { ++ /* ++ * Need the 5MiB window for chips that have block lock/unlock ++ * registers located below 4MiB window. ++ */ + { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), }, + { 0xffc00000, 4*1024*1024, (1<<7), }, + { 0xffff0000, 64*1024, 0 }, +@@ -112,19 +99,29 @@ + int i; + u32 rom_size; + ++ info->pdev = pdev; + window = &rom_window[0]; +-#if 0 +- while(window->size) { +- if (request_mem_region(window->start, window->size, "amd76xrom")) { +- break; +- } +- window++; +- } +- if (!window->size) { +- printk(KERN_ERR "amd76xrom: cannot reserve rom window\n"); +- goto err_out_none; ++ ++ while (window->size) { ++ /* ++ * Try to reserve the window mem region. If this fails then ++ * it is likely due to a fragment of the window being ++ * "reseved" by the BIOS. In the case that the ++ * request_mem_region() fails then once the rom size is ++ * discovered we will try to reserve the unreserved fragment. ++ */ ++ info->window_rsrc.name = MOD_NAME; ++ info->window_rsrc.start = window->start; ++ info->window_rsrc.end = window->start + window->size - 1; ++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&iomem_resource, &info->window_rsrc)) { ++ info->window_rsrc.parent = NULL; ++ printk(KERN_ERR MOD_NAME ++ " %s(): Unable to register resource" ++ " 0x%.08lx-0x%.08lx - kernel bug?\n", ++ __func__, ++ info->window_rsrc.start, info->window_rsrc.end); + } +-#endif + + /* Enable the selected rom window */ + pci_read_config_byte(pdev, 0x43, &byte); +@@ -136,49 +133,94 @@ + + /* FIXME handle registers 0x80 - 0x8C the bios region locks */ + +- printk(KERN_NOTICE "amd76xrom window : %x at %x\n", ++ printk(KERN_NOTICE MOD_NAME " window : %x at %x\n", + window->size, window->start); + /* For write accesses caches are useless */ +- info->window_addr = (unsigned long)ioremap_nocache(window->start, window->size); ++ info->window_addr = ++ (unsigned long)ioremap_nocache(window->start, ++ window->size); + + if (!info->window_addr) { + printk(KERN_ERR "Failed to ioremap\n"); +- goto err_out_free_mmio_region; ++ continue; + } +- info->mtd = 0; ++ ++ info->mtd = NULL; ++ + for(i = 0; (rom_size = rom_probe_sizes[i]); i++) { + char **chip_type; + if (rom_size > window->size) { + continue; + } +- info->map.map_priv_1 = ++ info->map.phys = window->start + window->size - rom_size; ++ info->map.virt = + info->window_addr + window->size - rom_size; + info->map.size = rom_size; ++ simple_map_init(&info->map); + chip_type = rom_probe_types; + for(; !info->mtd && *chip_type; chip_type++) { + info->mtd = do_map_probe(*chip_type, &amd76xrom_map.map); + } +- if (info->mtd) { +- break; +- } ++ if (info->mtd) goto found_mtd; + } +- if (!info->mtd) { +- goto err_out_iounmap; ++ iounmap((void *)(info->window_addr)); ++ info->window_addr = 0; ++ ++ /* Disable writes through the rom window */ ++ pci_read_config_byte(pdev, 0x40, &byte); ++ pci_write_config_byte(pdev, 0x40, byte & ~1); ++ ++ window++; + } +- printk(KERN_NOTICE "amd76xrom chip at offset: %x\n", ++ goto failed; ++ ++ found_mtd: ++ printk(KERN_NOTICE MOD_NAME " chip at offset: 0x%x\n", + window->size - rom_size); + +- info->mtd->module = THIS_MODULE; ++ info->mtd->owner = THIS_MODULE; ++ ++ if (!info->window_rsrc.parent) { ++ /* failed to reserve entire window - try fragments */ ++ info->window_rsrc.name = MOD_NAME; ++ info->window_rsrc.start = window->start; ++ info->window_rsrc.end = window->start + window->size - rom_size - 1; ++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&iomem_resource, &info->window_rsrc)) { ++ printk(KERN_ERR MOD_NAME ++ ": cannot reserve window resource fragment\n"); ++ goto failed; ++ } ++ } ++ + add_mtd_device(info->mtd); + info->window_start = window->start; + info->window_size = window->size; ++ ++ if (info->window_rsrc.parent) { ++ /* ++ * Registering the MTD device in iomem may not be possible ++ * if there is a BIOS "reserved" and BUSY range. If this ++ * fails then continue anyway. ++ */ ++ snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH, ++ "mtd%d", info->mtd->index); ++ ++ info->rom_rsrc.name = info->mtd_name; ++ info->rom_rsrc.start = window->start + window->size - rom_size; ++ info->rom_rsrc.end = window->start + window->size - 1; ++ info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&info->window_rsrc, &info->rom_rsrc)) { ++ printk(KERN_ERR MOD_NAME ++ ": cannot reserve MTD resource\n"); ++ info->rom_rsrc.parent = NULL; ++ } ++ } ++ + return 0; + +-err_out_iounmap: +- iounmap((void *)(info->window_addr)); +-err_out_free_mmio_region: +- release_mem_region(window->start, window->size); +-err_out_none: ++ failed: ++ amd76xrom_cleanup(info); + return -ENODEV; + } + +@@ -186,21 +228,8 @@ + static void __devexit amd76xrom_remove_one (struct pci_dev *pdev) + { + struct amd76xrom_map_info *info = &amd76xrom_map; +- u8 byte; +- +- del_mtd_device(info->mtd); +- map_destroy(info->mtd); +- info->mtd = 0; +- info->map.map_priv_1 = 0; +- +- iounmap((void *)(info->window_addr)); +- info->window_addr = 0; +- +- /* Disable writes through the rom window */ +- pci_read_config_byte(pdev, 0x40, &byte); +- pci_write_config_byte(pdev, 0x40, byte & ~1); + +- release_mem_region(info->window_start, info->window_size); ++ amd76xrom_cleanup(info); + } + + static struct pci_device_id amd76xrom_pci_tbl[] __devinitdata = { +@@ -208,6 +237,7 @@ + PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440, + PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */ + { 0, } + }; + +@@ -215,10 +245,10 @@ + + #if 0 + static struct pci_driver amd76xrom_driver = { +- name: "amd76xrom", +- id_table: amd76xrom_pci_tbl, +- probe: amd76xrom_init_one, +- remove: amd76xrom_remove_one, ++ .name = MOD_NAME, ++ .id_table = amd76xrom_pci_tbl, ++ .probe = amd76xrom_init_one, ++ .remove = amd76xrom_remove_one, + }; + #endif + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/arctic-mtd.c linux/drivers/mtd/maps/arctic-mtd.c +--- linux-mips-2.4.27/drivers/mtd/maps/arctic-mtd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/arctic-mtd.c 2004-11-19 10:25:11.893200704 +0100 +@@ -0,0 +1,135 @@ ++/* ++ * $Id: arctic-mtd.c,v 1.10 2003/06/02 16:37:59 trini Exp $ ++ * ++ * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for ++ * IBM 405LP Arctic 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved. ++ * ++ * Bishop Brock ++ * IBM Research, Austin Center for Low-Power Computing ++ * bcbrock@us.ibm.com ++ * March 2002 ++ * ++ * modified for Arctic by, ++ * David Gibson ++ * IBM OzLabs, Canberra, Australia ++ * <arctic@gibson.dropbear.id.au> ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/init.h> ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++ ++#include <asm/io.h> ++#include <asm/ibm4xx.h> ++ ++/* ++ * 0 : 0xFE00 0000 - 0xFEFF FFFF : Filesystem 1 (16MiB) ++ * 1 : 0xFF00 0000 - 0xFF4F FFFF : kernel (5.12MiB) ++ * 2 : 0xFF50 0000 - 0xFFF5 FFFF : Filesystem 2 (10.624MiB) (if non-XIP) ++ * 3 : 0xFFF6 0000 - 0xFFFF FFFF : PIBS Firmware (640KiB) ++ */ ++ ++#define FFS1_SIZE 0x01000000 /* 16MiB */ ++#define KERNEL_SIZE 0x00500000 /* 5.12MiB */ ++#define FFS2_SIZE 0x00a60000 /* 10.624MiB */ ++#define FIRMWARE_SIZE 0x000a0000 /* 640KiB */ ++ ++ ++#define NAME "Arctic Linux Flash" ++#define PADDR SUBZERO_BOOTFLASH_PADDR ++#define BUSWIDTH 2 ++#define SIZE SUBZERO_BOOTFLASH_SIZE ++#define PARTITIONS 4 ++ ++/* Flash memories on these boards are memory resources, accessed big-endian. */ ++ ++{ ++ /* do nothing for now */ ++} ++ ++static struct map_info arctic_mtd_map = { ++ .name = NAME, ++ .size = SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = PADDR, ++}; ++ ++static struct mtd_info *arctic_mtd; ++ ++static struct mtd_partition arctic_partitions[PARTITIONS] = { ++ { .name = "Filesystem", ++ .size = FFS1_SIZE, ++ .offset = 0,}, ++ { .name = "Kernel", ++ .size = KERNEL_SIZE, ++ .offset = FFS1_SIZE,}, ++ { .name = "Filesystem", ++ .size = FFS2_SIZE, ++ .offset = FFS1_SIZE + KERNEL_SIZE,}, ++ { .name = "Firmware", ++ .size = FIRMWARE_SIZE, ++ .offset = SUBZERO_BOOTFLASH_SIZE - FIRMWARE_SIZE,}, ++}; ++ ++static int __init ++init_arctic_mtd(void) ++{ ++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); ++ ++ arctic_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE); ++ ++ if (!arctic_mtd_map.virt) { ++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); ++ return -EIO; ++ } ++ simple_map_init(&arctic_mtd_map); ++ ++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); ++ arctic_mtd = do_map_probe("cfi_probe", &arctic_mtd_map); ++ ++ if (!arctic_mtd) ++ return -ENXIO; ++ ++ arctic_mtd->owner = THIS_MODULE; ++ ++ return add_mtd_partitions(arctic_mtd, arctic_partitions, PARTITIONS); ++} ++ ++static void __exit ++cleanup_arctic_mtd(void) ++{ ++ if (arctic_mtd) { ++ del_mtd_partitions(arctic_mtd); ++ map_destroy(arctic_mtd); ++ iounmap((void *) arctic_mtd_map.virt); ++ } ++} ++ ++module_init(init_arctic_mtd); ++module_exit(cleanup_arctic_mtd); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Gibson <arctic@gibson.dropbear.id.au>"); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/autcpu12-nvram.c linux/drivers/mtd/maps/autcpu12-nvram.c +--- linux-mips-2.4.27/drivers/mtd/maps/autcpu12-nvram.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/autcpu12-nvram.c 2004-11-19 10:25:11.894200552 +0100 +@@ -2,7 +2,7 @@ + * NV-RAM memory access on autcpu12 + * (C) 2002 Thomas Gleixner (gleixner@autronix.de) + * +- * $Id: autcpu12-nvram.c,v 1.1 2002/02/22 09:30:24 gleixner Exp $ ++ * $Id: autcpu12-nvram.c,v 1.5 2003/05/21 12:45:18 dwmw2 Exp $ + * + * 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 +@@ -24,6 +24,7 @@ + #include <linux/types.h> + #include <linux/kernel.h> + #include <linux/ioport.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <asm/sizes.h> + #include <asm/hardware.h> +@@ -32,80 +33,27 @@ + #include <linux/mtd/map.h> + #include <linux/mtd/partitions.h> + +-__u8 autcpu12_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 autcpu12_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 autcpu12_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- while(len) { +- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); +- from++; +- to++; +- len--; +- } +-} + + static struct mtd_info *sram_mtd; + + struct map_info autcpu12_sram_map = { +- name: "SRAM", +- size: 32768, +- buswidth: 8, +- read8: autcpu12_read8, +- read16: autcpu12_read16, +- read32: autcpu12_read32, +- copy_from: autcpu12_copy_from, +- write8: autcpu12_write8, +- write16: autcpu12_write16, +- write32: autcpu12_write32, +- copy_to: autcpu12_copy_to ++ .name = "SRAM", ++ .size = 32768, ++ .buswidth = 4, ++ .phys = 0x12000000, + }; + + static int __init init_autcpu12_sram (void) + { + int err, save0, save1; + +- autcpu12_sram_map.map_priv_1 = (unsigned long)ioremap(0x12000000, SZ_128K); +- if (!autcpu12_sram_map.map_priv_1) { ++ autcpu12_sram_map.virt = (unsigned long)ioremap(0x12000000, SZ_128K); ++ if (!autcpu12_sram_map.virt) { + printk("Failed to ioremap autcpu12 NV-RAM space\n"); + err = -EIO; + goto out; + } ++ simple_map_init(&autcpu_sram_map); + + /* + * Check for 32K/128K +@@ -115,20 +63,20 @@ + * Read and check result on ofs 0x0 + * Restore contents + */ +- save0 = autcpu12_read32(&autcpu12_sram_map,0); +- save1 = autcpu12_read32(&autcpu12_sram_map,0x10000); +- autcpu12_write32(&autcpu12_sram_map,~save0,0x10000); ++ save0 = map_read32(&autcpu12_sram_map,0); ++ save1 = map_read32(&autcpu12_sram_map,0x10000); ++ map_write32(&autcpu12_sram_map,~save0,0x10000); + /* if we find this pattern on 0x0, we have 32K size + * restore contents and exit + */ +- if ( autcpu12_read32(&autcpu12_sram_map,0) != save0) { +- autcpu12_write32(&autcpu12_sram_map,save0,0x0); ++ if ( map_read32(&autcpu12_sram_map,0) != save0) { ++ map_write32(&autcpu12_sram_map,save0,0x0); + goto map; + } + /* We have a 128K found, restore 0x10000 and set size + * to 128K + */ +- autcpu12_write32(&autcpu12_sram_map,save1,0x10000); ++ ma[_write32(&autcpu12_sram_map,save1,0x10000); + autcpu12_sram_map.size = SZ_128K; + + map: +@@ -139,7 +87,7 @@ + goto out_ioremap; + } + +- sram_mtd->module = THIS_MODULE; ++ sram_mtd->owner = THIS_MODULE; + sram_mtd->erasesize = 16; + + if (add_mtd_device(sram_mtd)) { +@@ -148,7 +96,7 @@ + goto out_probe; + } + +- printk("NV-RAM device size %ldK registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); ++ printk("NV-RAM device size %ldKiB registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); + + return 0; + +@@ -157,7 +105,7 @@ + sram_mtd = 0; + + out_ioremap: +- iounmap((void *)autcpu12_sram_map.map_priv_1); ++ iounmap((void *)autcpu12_sram_map.virt); + out: + return err; + } +@@ -167,7 +115,7 @@ + if (sram_mtd) { + del_mtd_device(sram_mtd); + map_destroy(sram_mtd); +- iounmap((void *)autcpu12_sram_map.map_priv_1); ++ iounmap((void *)autcpu12_sram_map.virt); + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/beech-mtd.c linux/drivers/mtd/maps/beech-mtd.c +--- linux-mips-2.4.27/drivers/mtd/maps/beech-mtd.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/beech-mtd.c 2004-11-19 10:25:11.895200400 +0100 +@@ -0,0 +1,112 @@ ++/* ++ * $Id: beech-mtd.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ ++ * ++ * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for ++ * IBM 405LP Beech 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. ++ * ++ * You should have received a copy of the GNU General Public License ++ * along with this program; if not, write to the Free Software ++ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ * ++ * Copyright (C) 2002, International Business Machines Corporation ++ * All Rights Reserved. ++ * ++ * Bishop Brock ++ * IBM Research, Austin Center for Low-Power Computing ++ * bcbrock@us.ibm.com ++ * March 2002 ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/init.h> ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++ ++#include <asm/io.h> ++#include <asm/ibm4xx.h> ++ ++#define NAME "Beech Linux Flash" ++#define PADDR BEECH_BIGFLASH_PADDR ++#define SIZE BEECH_BIGFLASH_SIZE ++#define BUSWIDTH 1 ++ ++/* Flash memories on these boards are memory resources, accessed big-endian. */ ++ ++ ++static struct map_info beech_mtd_map = { ++ .name = NAME, ++ .size = SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = PADDR ++}; ++ ++static struct mtd_info *beech_mtd; ++ ++static struct mtd_partition beech_partitions[2] = { ++ { ++ .name = "Linux Kernel", ++ .size = BEECH_KERNEL_SIZE, ++ .offset = BEECH_KERNEL_OFFSET ++ }, { ++ .name = "Free Area", ++ .size = BEECH_FREE_AREA_SIZE, ++ .offset = BEECH_FREE_AREA_OFFSET ++ } ++}; ++ ++static int __init ++init_beech_mtd(void) ++{ ++ printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); ++ ++ beech_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE); ++ ++ if (!beech_mtd_map.virt) { ++ printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); ++ return -EIO; ++ } ++ ++ simple_map_init(&beech_mtd_map); ++ ++ printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); ++ beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map); ++ ++ if (!beech_mtd) ++ return -ENXIO; ++ ++ beech_mtd->owner = THIS_MODULE; ++ ++ return add_mtd_partitions(beech_mtd, beech_partitions, 2); ++} ++ ++static void __exit ++cleanup_beech_mtd(void) ++{ ++ if (beech_mtd) { ++ del_mtd_partitions(beech_mtd); ++ map_destroy(beech_mtd); ++ iounmap((void *) beech_mtd_map.virt); ++ } ++} ++ ++module_init(init_beech_mtd); ++module_exit(cleanup_beech_mtd); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Bishop Brock <bcbrock@us.ibm.com>"); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/cdb89712.c linux/drivers/mtd/maps/cdb89712.c +--- linux-mips-2.4.27/drivers/mtd/maps/cdb89712.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/cdb89712.c 2004-11-19 10:25:11.897200096 +0100 +@@ -1,13 +1,14 @@ + /* + * Flash on Cirrus CDB89712 + * +- * $Id: cdb89712.c,v 1.3 2001/10/02 15:14:43 rmk Exp $ ++ * $Id: cdb89712.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ + */ + + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> + #include <linux/ioport.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <asm/arch/hardware.h> + #include <linux/mtd/mtd.h> +@@ -16,77 +17,21 @@ + + + +-__u8 cdb89712_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 cdb89712_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 cdb89712_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void cdb89712_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void cdb89712_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void cdb89712_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void cdb89712_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- // printk ("cdb89712_copy_from: 0x%x@0x%x -> 0x%x\n", len, from, to); +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void cdb89712_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- while(len) { +- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); +- from++; +- to++; +- len--; +- } +-} +- + + static struct mtd_info *flash_mtd; + + struct map_info cdb89712_flash_map = { +- name: "flash", +- size: FLASH_SIZE, +- buswidth: FLASH_WIDTH, +- read8: cdb89712_read8, +- read16: cdb89712_read16, +- read32: cdb89712_read32, +- copy_from: cdb89712_copy_from, +- write8: cdb89712_write8, +- write16: cdb89712_write16, +- write32: cdb89712_write32, +- copy_to: cdb89712_copy_to ++ .name = "flash", ++ .size = FLASH_SIZE, ++ .buswidth = FLASH_WIDTH, ++ .phys = FLASH_START, + }; + + struct resource cdb89712_flash_resource = { +- name: "Flash", +- start: FLASH_START, +- end: FLASH_START + FLASH_SIZE - 1, +- flags: IORESOURCE_IO | IORESOURCE_BUSY, ++ .name = "Flash", ++ .start = FLASH_START, ++ .end = FLASH_START + FLASH_SIZE - 1, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY, + }; + + static int __init init_cdb89712_flash (void) +@@ -99,13 +44,13 @@ + goto out; + } + +- cdb89712_flash_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); +- if (!cdb89712_flash_map.map_priv_1) { ++ cdb89712_flash_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); ++ if (!cdb89712_flash_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Cdb89712 FLASH space\n"); + err = -EIO; + goto out_resource; + } +- ++ simple_map_init(&cdb89712_flash_map); + flash_mtd = do_map_probe("cfi_probe", &cdb89712_flash_map); + if (!flash_mtd) { + flash_mtd = do_map_probe("map_rom", &cdb89712_flash_map); +@@ -118,7 +63,7 @@ + goto out_ioremap; + } + +- flash_mtd->module = THIS_MODULE; ++ flash_mtd->owner = THIS_MODULE; + + if (add_mtd_device(flash_mtd)) { + printk("FLASH device addition failed\n"); +@@ -132,7 +77,7 @@ + map_destroy(flash_mtd); + flash_mtd = 0; + out_ioremap: +- iounmap((void *)cdb89712_flash_map.map_priv_1); ++ iounmap((void *)cdb89712_flash_map.virt); + out_resource: + release_resource (&cdb89712_flash_resource); + out: +@@ -146,24 +91,17 @@ + static struct mtd_info *sram_mtd; + + struct map_info cdb89712_sram_map = { +- name: "SRAM", +- size: SRAM_SIZE, +- buswidth: SRAM_WIDTH, +- read8: cdb89712_read8, +- read16: cdb89712_read16, +- read32: cdb89712_read32, +- copy_from: cdb89712_copy_from, +- write8: cdb89712_write8, +- write16: cdb89712_write16, +- write32: cdb89712_write32, +- copy_to: cdb89712_copy_to ++ .name = "SRAM", ++ .size = SRAM_SIZE, ++ .buswidth = SRAM_WIDTH, ++ .phys = SRAM_START, + }; + + struct resource cdb89712_sram_resource = { +- name: "SRAM", +- start: SRAM_START, +- end: SRAM_START + SRAM_SIZE - 1, +- flags: IORESOURCE_IO | IORESOURCE_BUSY, ++ .name = "SRAM", ++ .start = SRAM_START, ++ .end = SRAM_START + SRAM_SIZE - 1, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY, + }; + + static int __init init_cdb89712_sram (void) +@@ -176,13 +114,13 @@ + goto out; + } + +- cdb89712_sram_map.map_priv_1 = (unsigned long)ioremap(SRAM_START, SRAM_SIZE); +- if (!cdb89712_sram_map.map_priv_1) { ++ cdb89712_sram_map.virt = (unsigned long)ioremap(SRAM_START, SRAM_SIZE); ++ if (!cdb89712_sram_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Cdb89712 SRAM space\n"); + err = -EIO; + goto out_resource; + } +- ++ simple_map_init(&cdb89712_sram_map); + sram_mtd = do_map_probe("map_ram", &cdb89712_sram_map); + if (!sram_mtd) { + printk("SRAM probe failed\n"); +@@ -190,7 +128,7 @@ + goto out_ioremap; + } + +- sram_mtd->module = THIS_MODULE; ++ sram_mtd->owner = THIS_MODULE; + sram_mtd->erasesize = 16; + + if (add_mtd_device(sram_mtd)) { +@@ -205,7 +143,7 @@ + map_destroy(sram_mtd); + sram_mtd = 0; + out_ioremap: +- iounmap((void *)cdb89712_sram_map.map_priv_1); ++ iounmap((void *)cdb89712_sram_map.virt); + out_resource: + release_resource (&cdb89712_sram_resource); + out: +@@ -221,20 +159,17 @@ + static struct mtd_info *bootrom_mtd; + + struct map_info cdb89712_bootrom_map = { +- name: "BootROM", +- size: BOOTROM_SIZE, +- buswidth: BOOTROM_WIDTH, +- read8: cdb89712_read8, +- read16: cdb89712_read16, +- read32: cdb89712_read32, +- copy_from: cdb89712_copy_from, ++ .name = "BootROM", ++ .size = BOOTROM_SIZE, ++ .buswidth = BOOTROM_WIDTH, ++ .phys = BOOTROM_START, + }; + + struct resource cdb89712_bootrom_resource = { +- name: "BootROM", +- start: BOOTROM_START, +- end: BOOTROM_START + BOOTROM_SIZE - 1, +- flags: IORESOURCE_IO | IORESOURCE_BUSY, ++ .name = "BootROM", ++ .start = BOOTROM_START, ++ .end = BOOTROM_START + BOOTROM_SIZE - 1, ++ .flags = IORESOURCE_IO | IORESOURCE_BUSY, + }; + + static int __init init_cdb89712_bootrom (void) +@@ -247,13 +182,13 @@ + goto out; + } + +- cdb89712_bootrom_map.map_priv_1 = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE); +- if (!cdb89712_bootrom_map.map_priv_1) { ++ cdb89712_bootrom_map.virt = (unsigned long)ioremap(BOOTROM_START, BOOTROM_SIZE); ++ if (!cdb89712_bootrom_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Cdb89712 BootROM space\n"); + err = -EIO; + goto out_resource; + } +- ++ simple_map_init(&cdb89712_bootrom_map); + bootrom_mtd = do_map_probe("map_rom", &cdb89712_bootrom_map); + if (!bootrom_mtd) { + printk("BootROM probe failed\n"); +@@ -261,7 +196,7 @@ + goto out_ioremap; + } + +- bootrom_mtd->module = THIS_MODULE; ++ bootrom_mtd->owner = THIS_MODULE; + bootrom_mtd->erasesize = 0x10000; + + if (add_mtd_device(bootrom_mtd)) { +@@ -276,7 +211,7 @@ + map_destroy(bootrom_mtd); + bootrom_mtd = 0; + out_ioremap: +- iounmap((void *)cdb89712_bootrom_map.map_priv_1); ++ iounmap((void *)cdb89712_bootrom_map.virt); + out_resource: + release_resource (&cdb89712_bootrom_resource); + out: +@@ -306,21 +241,21 @@ + if (sram_mtd) { + del_mtd_device(sram_mtd); + map_destroy(sram_mtd); +- iounmap((void *)cdb89712_sram_map.map_priv_1); ++ iounmap((void *)cdb89712_sram_map.virt); + release_resource (&cdb89712_sram_resource); + } + + if (flash_mtd) { + del_mtd_device(flash_mtd); + map_destroy(flash_mtd); +- iounmap((void *)cdb89712_flash_map.map_priv_1); ++ iounmap((void *)cdb89712_flash_map.virt); + release_resource (&cdb89712_flash_resource); + } + + if (bootrom_mtd) { + del_mtd_device(bootrom_mtd); + map_destroy(bootrom_mtd); +- iounmap((void *)cdb89712_bootrom_map.map_priv_1); ++ iounmap((void *)cdb89712_bootrom_map.virt); + release_resource (&cdb89712_bootrom_resource); + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ceiva.c linux/drivers/mtd/maps/ceiva.c +--- linux-mips-2.4.27/drivers/mtd/maps/ceiva.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/ceiva.c 2004-11-19 10:25:11.898199944 +0100 +@@ -11,7 +11,7 @@ + * + * (C) 2000 Nicolas Pitre <nico@cam.org> + * +- * $Id: ceiva.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ ++ * $Id: ceiva.c,v 1.8 2003/05/21 12:45:18 dwmw2 Exp $ + */ + + #include <linux/config.h> +@@ -19,6 +19,7 @@ + #include <linux/types.h> + #include <linux/ioport.h> + #include <linux/kernel.h> ++#include <linux/init.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -31,62 +32,10 @@ + #include <asm/sizes.h> + + /* +- * This isnt complete yet, so... ++ * This isn't complete yet, so... + */ + #define CONFIG_MTD_CEIVA_STATICMAP + +-static __u8 clps_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 clps_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 clps_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void clps_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void clps_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void clps_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void clps_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void clps_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} +- +-static struct map_info clps_map __initdata = { +- name: "clps flash", +- read8: clps_read8, +- read16: clps_read16, +- read32: clps_read32, +- copy_from: clps_copy_from, +- write8: clps_write8, +- write16: clps_write16, +- write32: clps_write32, +- copy_to: clps_copy_to, +-}; +- + #ifdef CONFIG_MTD_CEIVA_STATICMAP + /* + * See include/linux/mtd/partitions.h for definition of the mtd_partition +@@ -176,7 +125,7 @@ + maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); + if (!maps) + return -ENOMEM; +- ++ memset(maps, 0, sizeof(struct map_info) * nr); + /* + * Claim and then map the memory regions. + */ +@@ -191,7 +140,9 @@ + } + + clps[i].map = maps + i; +- memcpy(clps[i].map, &clps_map, sizeof(struct map_info)); ++ ++ clps[i].map->name = "clps flash"; ++ clps[i].map->phys = clps[i].base; + + clps[i].vbase = ioremap(clps[i].base, clps[i].size); + if (!clps[i].vbase) { +@@ -199,16 +150,18 @@ + break; + } + +- clps[i].map->map_priv_1 = (unsigned long)clps[i].vbase; ++ clps[i].map->virt = (unsigned long)clps[i].vbase; + clps[i].map->buswidth = clps[i].width; + clps[i].map->size = clps[i].size; + ++ simple_map_init(&clps[i].map); ++ + clps[i].mtd = do_map_probe("jedec_probe", clps[i].map); + if (clps[i].mtd == NULL) { + ret = -ENXIO; + break; + } +- clps[i].mtd->module = THIS_MODULE; ++ clps[i].mtd->owner = THIS_MODULE; + subdev[i] = clps[i].mtd; + + printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, " +@@ -318,10 +271,8 @@ + return nr; + } + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +-extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); +- + static struct mtd_partition *parsed_parts; ++static const char *probes[] = { "cmdlinepart", "RedBoot", NULL }; + + static void __init clps_locate_partitions(struct mtd_info *mtd) + { +@@ -331,20 +282,11 @@ + /* + * Partition selection stuff. + */ +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "clps"); ++ nr_parts = parse_mtd_partitions(mtd, probes, &parsed_parts, 0); + if (nr_parts > 0) { + part_type = "command line"; + break; + } +-#endif +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nr_parts = parse_redboot_partitions(mtd, &parsed_parts); +- if (nr_parts > 0) { +- part_type = "RedBoot"; +- break; +- } +-#endif + #ifdef CONFIG_MTD_CEIVA_STATICMAP + nr_parts = clps_static_partitions(&parsed_parts); + if (nr_parts > 0) { +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/cfi_flagadm.c linux/drivers/mtd/maps/cfi_flagadm.c +--- linux-mips-2.4.27/drivers/mtd/maps/cfi_flagadm.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/cfi_flagadm.c 2004-11-19 10:25:11.900199640 +0100 +@@ -1,7 +1,7 @@ + /* + * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is> + * +- * $Id: cfi_flagadm.c,v 1.7 2001/10/02 15:05:13 dwmw2 Exp $ ++ * $Id: cfi_flagadm.c,v 1.11 2003/05/21 12:45:18 dwmw2 Exp $ + * + * 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 +@@ -27,6 +27,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -55,83 +56,33 @@ + #define FLASH_PARTITION3_ADDR 0x00240000 + #define FLASH_PARTITION3_SIZE 0x001C0000 + +-__u8 flagadm_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 flagadm_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 flagadm_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void flagadm_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void flagadm_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void flagadm_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void flagadm_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void flagadm_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + struct map_info flagadm_map = { +- name: "FlagaDM flash device", +- size: FLASH_SIZE, +- buswidth: 2, +- read8: flagadm_read8, +- read16: flagadm_read16, +- read32: flagadm_read32, +- copy_from: flagadm_copy_from, +- write8: flagadm_write8, +- write16: flagadm_write16, +- write32: flagadm_write32, +- copy_to: flagadm_copy_to ++ .name = "FlagaDM flash device", ++ .size = FLASH_SIZE, ++ .buswidth = 2, + }; + + struct mtd_partition flagadm_parts[] = { + { +- name : "Bootloader", +- offset : FLASH_PARTITION0_ADDR, +- size : FLASH_PARTITION0_SIZE ++ .name = "Bootloader", ++ .offset = FLASH_PARTITION0_ADDR, ++ .size = FLASH_PARTITION0_SIZE + }, + { +- name : "Kernel image", +- offset : FLASH_PARTITION1_ADDR, +- size : FLASH_PARTITION1_SIZE ++ .name = "Kernel image", ++ .offset = FLASH_PARTITION1_ADDR, ++ .size = FLASH_PARTITION1_SIZE + }, + { +- name : "Initial ramdisk image", +- offset : FLASH_PARTITION2_ADDR, +- size : FLASH_PARTITION2_SIZE ++ .name = "Initial ramdisk image", ++ .offset = FLASH_PARTITION2_ADDR, ++ .size = FLASH_PARTITION2_SIZE + }, + { +- name : "Persistant storage", +- offset : FLASH_PARTITION3_ADDR, +- size : FLASH_PARTITION3_SIZE ++ .name = "Persistant storage", ++ .offset = FLASH_PARTITION3_ADDR, ++ .size = FLASH_PARTITION3_SIZE + } + }; + +@@ -144,22 +95,26 @@ + printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n", + FLASH_SIZE, FLASH_PHYS_ADDR); + +- flagadm_map.map_priv_1 = (unsigned long)ioremap(FLASH_PHYS_ADDR, ++ flagadm_map.phys = FLASH_PHYS_ADDR; ++ flagadm_map.virt = (unsigned long)ioremap(FLASH_PHYS_ADDR, + FLASH_SIZE); + +- if (!flagadm_map.map_priv_1) { ++ if (!flagadm_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ ++ simple_map_init(&flagadm_map); ++ + mymtd = do_map_probe("cfi_probe", &flagadm_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_partitions(mymtd, flagadm_parts, PARTITION_COUNT); + printk(KERN_NOTICE "FlagaDM flash device initialized\n"); + return 0; + } + +- iounmap((void *)flagadm_map.map_priv_1); ++ iounmap((void *)flagadm_map.virt); + return -ENXIO; + } + +@@ -169,9 +124,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (flagadm_map.map_priv_1) { +- iounmap((void *)flagadm_map.map_priv_1); +- flagadm_map.map_priv_1 = 0; ++ if (flagadm_map.virt) { ++ iounmap((void *)flagadm_map.virt); ++ flagadm_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/cstm_mips_ixx.c linux/drivers/mtd/maps/cstm_mips_ixx.c +--- linux-mips-2.4.27/drivers/mtd/maps/cstm_mips_ixx.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/cstm_mips_ixx.c 2004-11-19 10:25:11.901199488 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: cstm_mips_ixx.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: cstm_mips_ixx.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $ + * + * Mapping of a custom board with both AMD CFI and JEDEC flash in partitions. + * Config with both CFI and JEDEC device support. +@@ -33,55 +33,13 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> + #include <linux/mtd/partitions.h> + #include <linux/config.h> +- +-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + #include <linux/delay.h> +-#endif +- +-__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(__u32 *)(map->map_priv_1 + ofs); +-} +- +-void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + #define CC_GCR 0xB4013818 +@@ -97,10 +55,17 @@ + #define CC_GPAICR 0xB4013804 + #endif /* defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) */ + ++#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + void cstm_mips_ixx_set_vpp(struct map_info *map,int vpp) + { ++ static spinlock_t vpp_lock = SPIN_LOCK_UNLOCKED; ++ static int vpp_count = 0; ++ unsigned long flags; ++ ++ spin_lock_irqsave(&vpp_lock, flags); ++ + if (vpp) { +-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) ++ if (!vpp_count++) { + __u16 data; + __u8 data1; + static u8 first = 1; +@@ -116,10 +81,9 @@ + enabling vpp after powerup */ + udelay(40); + } +-#endif /* CONFIG_MIPS_ITE8172 */ + } +- else { +-#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) ++ } else { ++ if (!--vpp_count) { + __u16 data; + + // Set GPIO port B pin3 to high +@@ -127,26 +91,11 @@ + data = (data & 0xff3f) | 0x0040; + *(__u16 *)CC_GPBCR = data; + *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7; +-#endif /* CONFIG_MIPS_ITE8172 */ + } ++ } ++ spin_unlock_irqrestore(&vpp_lock, flags); + } +- +-const struct map_info basic_cstm_mips_ixx_map = { +- NULL, +- 0, +- 0, +- cstm_mips_ixx_read8, +- cstm_mips_ixx_read16, +- cstm_mips_ixx_read32, +- cstm_mips_ixx_copy_from, +- cstm_mips_ixx_write8, +- cstm_mips_ixx_write16, +- cstm_mips_ixx_write32, +- cstm_mips_ixx_copy_to, +- cstm_mips_ixx_set_vpp, +- 0, +- 0 +-}; ++#endif + + /* board and partition description */ + +@@ -175,9 +124,9 @@ + static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { + { // 28F128J3A in 2x16 configuration + { +- name: "main partition ", +- size: 0x02000000, // 128 x 2 x 128k byte sectors +- offset: 0, ++ .name = "main partition ", ++ .size = 0x02000000, // 128 x 2 x 128k byte sectors ++ .offset = 0, + }, + }, + }; +@@ -197,9 +146,9 @@ + static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { + { + { +- name: "main partition", +- size: CONFIG_MTD_CSTM_MIPS_IXX_LEN, +- offset: 0, ++ .name = "main partition", ++ .size = CONFIG_MTD_CSTM_MIPS_IXX_LEN, ++ .offset = 0, + }, + }, + }; +@@ -216,17 +165,24 @@ + + /* Initialize mapping */ + for (i=0;i<PHYSMAP_NUMBER;i++) { +- printk(KERN_NOTICE "cstm_mips_ixx flash device: %lx at %lx\n", cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr); +- memcpy((char *)&cstm_mips_ixx_map[i],(char *)&basic_cstm_mips_ixx_map,sizeof(struct map_info)); +- cstm_mips_ixx_map[i].map_priv_1 = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size); +- if (!cstm_mips_ixx_map[i].map_priv_1) { ++ printk(KERN_NOTICE "cstm_mips_ixx flash device: 0x%lx at 0x%lx\n", ++ cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr); ++ ++ ++ cstm_mips_ixx_map[i].phys = cstm_mips_ixx_board_desc[i].window_addr; ++ cstm_mips_ixx_map[i].virt = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size); ++ if (!cstm_mips_ixx_map[i].virt) { + printk(KERN_WARNING "Failed to ioremap\n"); + return -EIO; + } + cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name; + cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size; + cstm_mips_ixx_map[i].buswidth = cstm_mips_ixx_board_desc[i].buswidth; +- //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].map_priv_1)); ++#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) ++ cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp; ++#endif ++ simple_map_init(&cstm_mips_ixx_map[i]); ++ //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt)); + } + + #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) +@@ -244,7 +200,7 @@ + printk(KERN_NOTICE "cstm_mips_ixx %d jedec: mymtd is %x\n",i,(unsigned int)mymtd); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd; + add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions); +@@ -266,9 +222,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (cstm_mips_ixx_map[i].map_priv_1) { +- iounmap((void *)cstm_mips_ixx_map[i].map_priv_1); +- cstm_mips_ixx_map[i].map_priv_1 = 0; ++ if (cstm_mips_ixx_map[i].virt) { ++ iounmap((void *)cstm_mips_ixx_map[i].virt); ++ cstm_mips_ixx_map[i].virt = 0; + } + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/db1x00-flash.c linux/drivers/mtd/maps/db1x00-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/db1x00-flash.c 2003-02-16 07:25:24.000000000 +0100 ++++ linux/drivers/mtd/maps/db1x00-flash.c 2004-11-19 10:25:11.903199184 +0100 +@@ -8,6 +8,7 @@ + #include <linux/config.h> + #include <linux/module.h> + #include <linux/types.h> ++#include <linux/init.h> + #include <linux/kernel.h> + + #include <linux/mtd/mtd.h> +@@ -29,76 +30,6 @@ + static unsigned long flash_size; + + static BCSR * const bcsr = (BCSR *)0xAE000000; +- +-__u8 physmap_read8(struct map_info *map, unsigned long ofs) +-{ +- __u8 ret; +- ret = __raw_readb(map->map_priv_1 + ofs); +- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u16 physmap_read16(struct map_info *map, unsigned long ofs) +-{ +- __u16 ret; +- ret = __raw_readw(map->map_priv_1 + ofs); +- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u32 physmap_read32(struct map_info *map, unsigned long ofs) +-{ +- __u32 ret; +- ret = __raw_readl(map->map_priv_1 + ofs); +- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to); +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from); +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- +-static struct map_info db1x00_map = { +- name: "Db1x00 flash", +- read8: physmap_read8, +- read16: physmap_read16, +- read32: physmap_read32, +- copy_from: physmap_copy_from, +- write8: physmap_write8, +- write16: physmap_write16, +- write32: physmap_write32, +- copy_to: physmap_copy_to, +-}; +- + static unsigned char flash_buswidth = 4; + + /* +@@ -115,58 +46,62 @@ + */ + static struct mtd_partition db1x00_partitions[] = { + { +- name: "User FS", +- size: 0x1c00000, +- offset: 0x0000000 ++ .name = "User FS", ++ .size = 0x1c00000, ++ .offset = 0x0000000 + },{ +- name: "yamon", +- size: 0x0100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE ++ .name = "yamon", ++ .size = 0x0100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE + },{ +- name: "raw kernel", +- size: (0x300000-0x40000), /* last 256KB is yamon env */ +- offset: MTDPART_OFS_APPEND, ++ .name = "raw kernel", ++ .size = (0x300000-0x40000), /* last 256KB is env */ ++ .offset = MTDPART_OFS_APPEND, + } + }; + #elif defined(DB1X00_BOOT_ONLY) + static struct mtd_partition db1x00_partitions[] = { + { +- name: "User FS", +- size: 0x00c00000, +- offset: 0x0000000 ++ .name = "User FS", ++ .size = 0x00c00000, ++ .offset = 0x0000000 + },{ +- name: "yamon", +- size: 0x0100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE ++ .name = "yamon", ++ .size = 0x0100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE + },{ +- name: "raw kernel", +- size: (0x300000-0x40000), /* last 256KB is yamon env */ +- offset: MTDPART_OFS_APPEND, ++ .name = "raw kernel", ++ .size = (0x300000-0x40000), /* last 256KB is env */ ++ .offset = MTDPART_OFS_APPEND, + } + }; + #elif defined(DB1X00_USER_ONLY) + static struct mtd_partition db1x00_partitions[] = { + { +- name: "User FS", +- size: 0x0e00000, +- offset: 0x0000000 ++ .name = "User FS", ++ .size = 0x0e00000, ++ .offset = 0x0000000 + },{ +- name: "raw kernel", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "raw kernel", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #else + #error MTD_DB1X00 define combo error /* should never happen */ + #endif ++#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + ++#define NAME "Db1x00 Linux Flash" + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) ++static struct map_info db1xxx_mtd_map = { ++ .name = NAME, ++}; + + static struct mtd_partition *parsed_parts; +-static struct mtd_info *mymtd; ++static struct mtd_info *db1xxx_mtd; + + /* + * Probe the flash density and setup window address and size +@@ -174,7 +109,7 @@ + * want the MTD driver to be probing the boot or user flash, + * so having the option to enable only one bank is important. + */ +-int setup_flash_params() ++int setup_flash_params(void) + { + switch ((bcsr->status >> 14) & 0x3) { + case 0: /* 64Mbit devices */ +@@ -228,6 +163,10 @@ + default: + return 1; + } ++ db1xxx_mtd_map.size = window_size; ++ db1xxx_mtd_map.buswidth = flash_buswidth; ++ db1xxx_mtd_map.phys = window_addr; ++ db1xxx_mtd_map.buswidth = flash_buswidth; + return 0; + } + +@@ -235,10 +174,6 @@ + { + struct mtd_partition *parts; + int nb_parts = 0; +- char *part_type; +- +- /* Default flash buswidth */ +- db1x00_map.buswidth = flash_buswidth; + + if (setup_flash_params()) + return -ENXIO; +@@ -246,32 +181,29 @@ + /* + * Static partition definition selection + */ +- part_type = "static"; + parts = db1x00_partitions; + nb_parts = NB_OF(db1x00_partitions); +- db1x00_map.size = window_size; + + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "Db1xxx flash: probing %d-bit flash bus\n", +- db1x00_map.buswidth*8); +- db1x00_map.map_priv_1 = +- (unsigned long)ioremap(window_addr, window_size); +- mymtd = do_map_probe("cfi_probe", &db1x00_map); +- if (!mymtd) return -ENXIO; +- mymtd->module = THIS_MODULE; ++ db1xxx_mtd_map.buswidth*8); ++ db1xxx_mtd_map.virt = (unsigned long)ioremap(window_addr, window_size); ++ db1xxx_mtd = do_map_probe("cfi_probe", &db1xxx_mtd_map); ++ if (!db1xxx_mtd) return -ENXIO; ++ db1xxx_mtd->owner = THIS_MODULE; + +- add_mtd_partitions(mymtd, parts, nb_parts); ++ add_mtd_partitions(db1xxx_mtd, parts, nb_parts); + return 0; + } + + static void __exit db1x00_mtd_cleanup(void) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); ++ if (db1xxx_mtd) { ++ del_mtd_partitions(db1xxx_mtd); ++ map_destroy(db1xxx_mtd); + if (parsed_parts) + kfree(parsed_parts); + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/dbox2-flash.c linux/drivers/mtd/maps/dbox2-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/dbox2-flash.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/dbox2-flash.c 2004-11-19 10:25:11.904199032 +0100 +@@ -1,12 +1,13 @@ + /* +- * $Id: dbox2-flash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: dbox2-flash.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $ + * +- * Nokia / Sagem D-Box 2 flash driver ++ * D-Box 2 flash driver + */ + + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -16,22 +17,44 @@ + /* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ +-static struct mtd_partition partition_info[]= {{name: "BR bootloader", // raw +- size: 128 * 1024, +- offset: 0, +- mask_flags: MTD_WRITEABLE}, +- {name: "PPC bootloader", // flfs +- size: 128 * 1024, +- offset: MTDPART_OFS_APPEND, +- mask_flags: 0}, +- {name: "Kernel", // idxfs +- size: 768 * 1024, +- offset: MTDPART_OFS_APPEND, +- mask_flags: 0}, +- {name: "System", // jffs +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- mask_flags: 0}}; ++static struct mtd_partition partition_info[]= { ++ { ++ .name = "BR bootloader", ++ .size = 128 * 1024, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE ++ }, ++ { ++ .name = "flfs (ppcboot)", ++ .size = 128 * 1024, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "root (cramfs)", ++ .size = 7040 * 1024, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "var (jffs2)", ++ .size = 896 * 1024, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "flash without bootloader", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 128 * 1024, ++ .mask_flags = 0 ++ }, ++ { ++ .name = "complete flash", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE ++ } ++}; + + #define NUM_PARTITIONS (sizeof(partition_info) / sizeof(partition_info[0])) + +@@ -40,72 +63,24 @@ + + static struct mtd_info *mymtd; + +-__u8 dbox2_flash_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 dbox2_flash_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 dbox2_flash_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void dbox2_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void dbox2_flash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void dbox2_flash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void dbox2_flash_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void dbox2_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + struct map_info dbox2_flash_map = { +- name: "D-Box 2 flash memory", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: dbox2_flash_read8, +- read16: dbox2_flash_read16, +- read32: dbox2_flash_read32, +- copy_from: dbox2_flash_copy_from, +- write8: dbox2_flash_write8, +- write16: dbox2_flash_write16, +- write32: dbox2_flash_write32, +- copy_to: dbox2_flash_copy_to ++ .name = "D-Box 2 flash memory", ++ .size = WINDOW_SIZE, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR, + }; + + int __init init_dbox2_flash(void) + { + printk(KERN_NOTICE "D-Box 2 flash driver (size->0x%X mem->0x%X)\n", WINDOW_SIZE, WINDOW_ADDR); +- dbox2_flash_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ dbox2_flash_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + +- if (!dbox2_flash_map.map_priv_1) { ++ if (!dbox2_flash_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&dbox2_flash_map); + + // Probe for dual Intel 28F320 or dual AMD + mymtd = do_map_probe("cfi_probe", &dbox2_flash_map); +@@ -117,7 +92,7 @@ + } + + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); +@@ -125,7 +100,7 @@ + return 0; + } + +- iounmap((void *)dbox2_flash_map.map_priv_1); ++ iounmap((void *)dbox2_flash_map.virt); + return -ENXIO; + } + +@@ -135,9 +110,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (dbox2_flash_map.map_priv_1) { +- iounmap((void *)dbox2_flash_map.map_priv_1); +- dbox2_flash_map.map_priv_1 = 0; ++ if (dbox2_flash_map.virt) { ++ iounmap((void *)dbox2_flash_map.virt); ++ dbox2_flash_map.virt = 0; + } + } + +@@ -146,5 +121,5 @@ + + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>"); +-MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board"); ++MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>"); ++MODULE_DESCRIPTION("MTD map driver for D-Box 2 board"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/dc21285.c linux/drivers/mtd/maps/dc21285.c +--- linux-mips-2.4.27/drivers/mtd/maps/dc21285.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/dc21285.c 2004-11-19 10:25:11.906198728 +0100 +@@ -5,12 +5,13 @@ + * + * This code is GPL + * +- * $Id: dc21285.c,v 1.9 2002/10/14 12:22:10 rmk Exp $ ++ * $Id: dc21285.c,v 1.15 2003/05/21 12:45:18 dwmw2 Exp $ + */ + #include <linux/config.h> + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -92,26 +93,42 @@ + } + + struct map_info dc21285_map = { +- name: "DC21285 flash", +- size: 16*1024*1024, +- read8: dc21285_read8, +- read16: dc21285_read16, +- read32: dc21285_read32, +- copy_from: dc21285_copy_from, +- write8: dc21285_write8, +- write16: dc21285_write16, +- write32: dc21285_write32, +- copy_to: dc21285_copy_to ++ .name = "DC21285 flash", ++ .phys = NO_XIP, ++ .size = 16*1024*1024, ++ .read8 = dc21285_read8, ++ .read16 = dc21285_read16, ++ .read32 = dc21285_read32, ++ .copy_from = dc21285_copy_from, ++ .write8 = dc21285_write8, ++ .write16 = dc21285_write16, ++ .write32 = dc21285_write32, ++ .copy_to = dc21285_copy_to + }; + + + /* Partition stuff */ + static struct mtd_partition *dc21285_parts; +- +-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); ++#ifdef CONFIG_MTD_PARTITIONS ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++#endif + + int __init init_dc21285(void) + { ++ ++ /* ++ * Flash timing is determined with bits 19-16 of the ++ * CSR_SA110_CNTL. The value is the number of wait cycles, or ++ * 0 for 16 cycles (the default). Cycles are 20 ns. ++ * Here we use 7 for 140 ns flash chips. ++ */ ++ /* access time */ ++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16)); ++ /* burst time */ ++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); ++ /* tristate time */ ++ *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); ++ + /* Determine buswidth */ + switch (*CSR_SA110_CNTL & (3<<14)) { + case SA110_CNTL_ROMWIDTH_8: +@@ -141,33 +158,18 @@ + if (mymtd) { + int nrparts = 0; + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* partition fixup */ + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); +-#endif ++#ifdef CONFIG_MTD_PARTITIONS ++ nrparts = parse_mtd_partitions(mymtd, probes, &dc21285_parts, (void *)0); + if (nrparts > 0) { + add_mtd_partitions(mymtd, dc21285_parts, nrparts); +- } else if (nrparts == 0) { +- printk(KERN_NOTICE "RedBoot partition table failed\n"); +- add_mtd_device(mymtd); ++ return 0; + } +- +- /* +- * Flash timing is determined with bits 19-16 of the +- * CSR_SA110_CNTL. The value is the number of wait cycles, or +- * 0 for 16 cycles (the default). Cycles are 20 ns. +- * Here we use 7 for 140 ns flash chips. +- */ +- /* access time */ +- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16)); +- /* burst time */ +- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20)); +- /* tristate time */ +- *CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24)); +- ++#endif ++ add_mtd_device(mymtd); + return 0; + } + +@@ -177,17 +179,16 @@ + + static void __exit cleanup_dc21285(void) + { +- if (mymtd) { ++#ifdef CONFIG_MTD_PARTITIONS ++ if (dc21285_parts) { ++ del_mtd_partitions(mymtd); ++ kfree(dc21285_parts); ++ } else ++#endif + del_mtd_device(mymtd); ++ + map_destroy(mymtd); +- mymtd = NULL; +- } +- if (dc21285_map.map_priv_1) { + iounmap((void *)dc21285_map.map_priv_1); +- dc21285_map.map_priv_1 = 0; +- } +- if(dc21285_parts) +- kfree(dc21285_parts); + } + + module_init(init_dc21285); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/dilnetpc.c linux/drivers/mtd/maps/dilnetpc.c +--- linux-mips-2.4.27/drivers/mtd/maps/dilnetpc.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/dilnetpc.c 2004-11-19 10:25:11.907198576 +0100 +@@ -14,7 +14,7 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * +- * $Id: dilnetpc.c,v 1.8 2002/03/12 13:07:26 rkaiser Exp $ ++ * $Id: dilnetpc.c,v 1.12 2003/05/21 12:45:18 dwmw2 Exp $ + * + * The DIL/Net PC is a tiny embedded PC board made by SSV Embedded Systems + * featuring the AMD Elan SC410 processor. There are two variants of this +@@ -29,6 +29,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -36,7 +37,7 @@ + #include <linux/mtd/concat.h> + + /* +-** The DIL/NetPC keeps it's BIOS in two distinct flash blocks. ++** The DIL/NetPC keeps its BIOS in two distinct flash blocks. + ** Destroying any of these blocks transforms the DNPC into + ** a paperweight (albeit not a very useful one, considering + ** it only weighs a few grams). +@@ -189,45 +190,6 @@ + } + + +-static __u8 dnpc_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 dnpc_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 dnpc_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void dnpc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void dnpc_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void dnpc_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void dnpc_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void dnpc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} + + /* + ************************************************************ +@@ -288,19 +250,11 @@ + #define WINDOW_ADDR FLASH_BASE + + static struct map_info dnpc_map = { +- name: "ADNP Flash Bank", +- size: ADNP_WINDOW_SIZE, +- buswidth: 1, +- read8: dnpc_read8, +- read16: dnpc_read16, +- read32: dnpc_read32, +- copy_from: dnpc_copy_from, +- write8: dnpc_write8, +- write16: dnpc_write16, +- write32: dnpc_write32, +- copy_to: dnpc_copy_to, +- set_vpp: adnp_set_vpp, +- map_priv_2: WINDOW_ADDR ++ .name = "ADNP Flash Bank", ++ .size = ADNP_WINDOW_SIZE, ++ .buswidth = 1, ++ .set_vpp = adnp_set_vpp, ++ .phys = WINDOW_ADDR + }; + + /* +@@ -316,29 +270,29 @@ + static struct mtd_partition partition_info[]= + { + { +- name: "ADNP boot", +- offset: 0, +- size: 0xf0000, ++ .name = "ADNP boot", ++ .offset = 0, ++ .size = 0xf0000, + }, + { +- name: "ADNP system BIOS", +- offset: MTDPART_OFS_NXTBLK, +- size: 0x10000, ++ .name = "ADNP system BIOS", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = 0x10000, + #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED +- mask_flags: MTD_WRITEABLE, ++ .mask_flags = MTD_WRITEABLE, + #endif + }, + { +- name: "ADNP file system", +- offset: MTDPART_OFS_NXTBLK, +- size: 0x2f0000, ++ .name = "ADNP file system", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = 0x2f0000, + }, + { +- name: "ADNP system BIOS entry", +- offset: MTDPART_OFS_NXTBLK, +- size: MTDPART_SIZ_FULL, ++ .name = "ADNP system BIOS entry", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, + #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED +- mask_flags: MTD_WRITEABLE, ++ .mask_flags = MTD_WRITEABLE, + #endif + }, + }; +@@ -369,21 +323,21 @@ + static struct mtd_partition higlvl_partition_info[]= + { + { +- name: "ADNP boot block", +- offset: 0, +- size: CONFIG_MTD_DILNETPC_BOOTSIZE, ++ .name = "ADNP boot block", ++ .offset = 0, ++ .size = CONFIG_MTD_DILNETPC_BOOTSIZE, + }, + { +- name: "ADNP file system space", +- offset: MTDPART_OFS_NXTBLK, +- size: ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000, ++ .name = "ADNP file system space", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = ADNP_WINDOW_SIZE-CONFIG_MTD_DILNETPC_BOOTSIZE-0x20000, + }, + { +- name: "ADNP system BIOS + BIOS Entry", +- offset: MTDPART_OFS_NXTBLK, +- size: MTDPART_SIZ_FULL, ++ .name = "ADNP system BIOS + BIOS Entry", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, + #ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED +- mask_flags: MTD_WRITEABLE, ++ .mask_flags = MTD_WRITEABLE, + #endif + }, + }; +@@ -447,18 +401,19 @@ + } + + printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n", +- is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.map_priv_2); ++ is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys); + +- dnpc_map.map_priv_1 = (unsigned long)ioremap_nocache(dnpc_map.map_priv_2, dnpc_map.size); ++ dnpc_map.virt = (unsigned long)ioremap_nocache(dnpc_map.phys, dnpc_map.size); + +- dnpc_map_flash(dnpc_map.map_priv_2, dnpc_map.size); ++ dnpc_map_flash(dnpc_map.phys, dnpc_map.size); + +- if (!dnpc_map.map_priv_1) { ++ if (!dnpc_map.virt) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } ++ simple_map_init(&dnpc_map); + +- printk("FLASH virtual address: 0x%lx\n", dnpc_map.map_priv_1); ++ printk("FLASH virtual address: 0x%lx\n", dnpc_map.virt); + + mymtd = do_map_probe("jedec_probe", &dnpc_map); + +@@ -475,11 +430,11 @@ + mymtd->erasesize = 0x10000; + + if (!mymtd) { +- iounmap((void *)dnpc_map.map_priv_1); ++ iounmap((void *)dnpc_map.virt); + return -ENXIO; + } + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* + ** Supply pointers to lowlvl_parts[] array to add_mtd_partitions() +@@ -525,10 +480,10 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (dnpc_map.map_priv_1) { +- iounmap((void *)dnpc_map.map_priv_1); ++ if (dnpc_map.virt) { ++ iounmap((void *)dnpc_map.virt); + dnpc_unmap_flash(); +- dnpc_map.map_priv_1 = 0; ++ dnpc_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ebony.c linux/drivers/mtd/maps/ebony.c +--- linux-mips-2.4.27/drivers/mtd/maps/ebony.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/ebony.c 2004-11-19 10:25:11.909198272 +0100 +@@ -0,0 +1,164 @@ ++/* ++ * $Id: ebony.c,v 1.8 2003/06/23 11:48:18 dwmw2 Exp $ ++ * ++ * Mapping for Ebony user flash ++ * ++ * Matt Porter <mporter@mvista.com> ++ * ++ * Copyright 2002 MontaVista Software Inc. ++ * ++ * 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/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <linux/config.h> ++#include <asm/io.h> ++#include <asm/ibm440.h> ++#include <platforms/ebony.h> ++ ++static struct mtd_info *flash; ++ ++static struct map_info ebony_small_map = { ++ .name = "Ebony small flash", ++ .size = EBONY_SMALL_FLASH_SIZE, ++ .buswidth = 1, ++}; ++ ++static struct map_info ebony_large_map = { ++ .name = "Ebony large flash", ++ .size = EBONY_LARGE_FLASH_SIZE, ++ .buswidth = 1, ++}; ++ ++static struct mtd_partition ebony_small_partitions[] = { ++ { ++ .name = "OpenBIOS", ++ .offset = 0x0, ++ .size = 0x80000, ++ } ++}; ++ ++static struct mtd_partition ebony_large_partitions[] = { ++ { ++ .name = "fs", ++ .offset = 0, ++ .size = 0x380000, ++ }, ++ { ++ .name = "firmware", ++ .offset = 0x380000, ++ .size = 0x80000, ++ } ++}; ++ ++int __init init_ebony(void) ++{ ++ u8 fpga0_reg; ++ unsigned long fpga0_adr; ++ unsigned long long small_flash_base, large_flash_base; ++ ++ fpga0_adr = ioremap64(EBONY_FPGA_ADDR, 16); ++ if (!fpga0_adr) ++ return -ENOMEM; ++ ++ fpga0_reg = readb(fpga0_adr); ++ iounmap64(fpga0_adr); ++ ++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ !EBONY_FLASH_SEL(fpga0_reg)) ++ small_flash_base = EBONY_SMALL_FLASH_HIGH2; ++ else if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ EBONY_FLASH_SEL(fpga0_reg)) ++ small_flash_base = EBONY_SMALL_FLASH_HIGH1; ++ else if (!EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ !EBONY_FLASH_SEL(fpga0_reg)) ++ small_flash_base = EBONY_SMALL_FLASH_LOW2; ++ else ++ small_flash_base = EBONY_SMALL_FLASH_LOW1; ++ ++ if (EBONY_BOOT_SMALL_FLASH(fpga0_reg) && ++ !EBONY_ONBRD_FLASH_EN(fpga0_reg)) ++ large_flash_base = EBONY_LARGE_FLASH_LOW; ++ else ++ large_flash_base = EBONY_LARGE_FLASH_HIGH; ++ ++ ebony_small_map.phys = small_flash_base; ++ ebony_small_map.virt = ++ (unsigned long)ioremap64(small_flash_base, ++ ebony_small_map.size); ++ ++ if (!ebony_small_map.virt) { ++ printk("Failed to ioremap flash\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&ebony_small_map); ++ ++ flash = do_map_probe("map_rom", &ebony_small_map); ++ if (flash) { ++ flash->owner = THIS_MODULE; ++ add_mtd_partitions(flash, ebony_small_partitions, ++ ARRAY_SIZE(ebony_small_partitions)); ++ } else { ++ printk("map probe failed for flash\n"); ++ return -ENXIO; ++ } ++ ++ ebony_large_map.phys = large_flash_base; ++ ebony_large_map.virt = ++ (unsigned long)ioremap64(large_flash_base, ++ ebony_large_map.size); ++ ++ if (!ebony_large_map.virt) { ++ printk("Failed to ioremap flash\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&ebony_large_map); ++ ++ flash = do_map_probe("cfi_probe", &ebony_large_map); ++ if (flash) { ++ flash->owner = THIS_MODULE; ++ add_mtd_partitions(flash, ebony_large_partitions, ++ ARRAY_SIZE(ebony_large_partitions)); ++ } else { ++ printk("map probe failed for flash\n"); ++ return -ENXIO; ++ } ++ ++ return 0; ++} ++ ++static void __exit cleanup_ebony(void) ++{ ++ if (flash) { ++ del_mtd_partitions(flash); ++ map_destroy(flash); ++ } ++ ++ if (ebony_small_map.virt) { ++ iounmap((void *)ebony_small_map.virt); ++ ebony_small_map.virt = 0; ++ } ++ ++ if (ebony_large_map.virt) { ++ iounmap((void *)ebony_large_map.virt); ++ ebony_large_map.virt = 0; ++ } ++} ++ ++module_init(init_ebony); ++module_exit(cleanup_ebony); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Matt Porter <mporter@mvista.com>"); ++MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/edb7312.c linux/drivers/mtd/maps/edb7312.c +--- linux-mips-2.4.27/drivers/mtd/maps/edb7312.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/edb7312.c 2004-11-19 10:25:11.910198120 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: edb7312.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ ++ * $Id: edb7312.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ + * + * Handle mapping of the NOR flash on Cogent EDB7312 boards + * +@@ -13,6 +13,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -35,61 +36,11 @@ + + static struct mtd_info *mymtd; + +-__u8 edb7312nor_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 edb7312nor_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 edb7312nor_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void edb7312nor_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void edb7312nor_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void edb7312nor_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void edb7312nor_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void edb7312nor_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- + struct map_info edb7312nor_map = { +- name: "NOR flash on EDB7312", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: edb7312nor_read8, +- read16: edb7312nor_read16, +- read32: edb7312nor_read32, +- copy_from: edb7312nor_copy_from, +- write8: edb7312nor_write8, +- write16: edb7312nor_write16, +- write32: edb7312nor_write32, +- copy_to: edb7312nor_copy_to ++ .name = "NOR flash on EDB7312", ++ .size = WINDOW_SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = WINDOW_ADDR, + }; + + #ifdef CONFIG_MTD_PARTITIONS +@@ -100,29 +51,23 @@ + static struct mtd_partition static_partitions[3] = + { + { +- name: "ARMboot", +- size: 0x40000, +- offset: 0 ++ .name = "ARMboot", ++ .size = 0x40000, ++ .offset = 0 + }, + { +- name: "Kernel", +- size: 0x200000, +- offset: 0x40000 ++ .name = "Kernel", ++ .size = 0x200000, ++ .offset = 0x40000 + }, + { +- name: "RootFS", +- size: 0xDC0000, +- offset: 0x240000 ++ .name = "RootFS", ++ .size = 0xDC0000, ++ .offset = 0x240000 + }, + }; + +-#define NB_OF(x) (sizeof (x) / sizeof (x[0])) +- +-#ifdef CONFIG_MTD_CMDLINE_PARTS +-int parse_cmdline_partitions(struct mtd_info *master, +- struct mtd_partition **pparts, +- const char *mtd_id); +-#endif ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + + #endif + +@@ -137,32 +82,33 @@ + + printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", + WINDOW_SIZE, WINDOW_ADDR); +- edb7312nor_map.map_priv_1 = (unsigned long) ++ edb7312nor_map.virt = (unsigned long) + ioremap(WINDOW_ADDR, WINDOW_SIZE); + +- if (!edb7312nor_map.map_priv_1) { ++ if (!edb7312nor_map.virt) { + printk(MSG_PREFIX "failed to ioremap\n"); + return -EIO; + } + ++ simple_map_init(&edb7312nor_map); ++ + mymtd = 0; + type = rom_probe_types; + for(; !mymtd && *type; type++) { + mymtd = do_map_probe(*type, &edb7312nor_map); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, MTDID); ++ mtd_parts_nb = parse_mtd_partitions(mymtd, probes, &mtd_parts, MTDID); + if (mtd_parts_nb > 0) +- part_type = "command line"; +-#endif ++ part_type = "detected"; ++ + if (mtd_parts_nb == 0) + { + mtd_parts = static_partitions; +- mtd_parts_nb = NB_OF(static_partitions); ++ mtd_parts_nb = ARRAY_SIZE(static_partitions); + part_type = "static"; + } + #endif +@@ -178,7 +124,7 @@ + return 0; + } + +- iounmap((void *)edb7312nor_map.map_priv_1); ++ iounmap((void *)edb7312nor_map.virt); + return -ENXIO; + } + +@@ -188,9 +134,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (edb7312nor_map.map_priv_1) { +- iounmap((void *)edb7312nor_map.map_priv_1); +- edb7312nor_map.map_priv_1 = 0; ++ if (edb7312nor_map.virt) { ++ iounmap((void *)edb7312nor_map.virt); ++ edb7312nor_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/elan-104nc.c linux/drivers/mtd/maps/elan-104nc.c +--- linux-mips-2.4.27/drivers/mtd/maps/elan-104nc.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/elan-104nc.c 2004-11-19 10:25:11.912197816 +0100 +@@ -16,7 +16,7 @@ + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +- $Id: elan-104nc.c,v 1.13 2002/02/13 15:30:20 dwmw2 Exp $ ++ $Id: elan-104nc.c,v 1.18 2003/06/23 07:37:02 dwmw2 Exp $ + + The ELAN-104NC has up to 8 Mibyte of Intel StrataFlash (28F320/28F640) in x16 + mode. This drivers uses the CFI probe and Intel Extended Command Set drivers. +@@ -40,6 +40,7 @@ + #include <asm/io.h> + + #include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> + + #define WINDOW_START 0xb0000 +@@ -59,14 +60,14 @@ + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ +- { name: "ELAN-104NC flash boot partition", +- offset: 0, +- size: 640*1024 }, +- { name: "ELAN-104NC flash partition 1", +- offset: 640*1024, +- size: 896*1024 }, +- { name: "ELAN-104NC flash partition 2", +- offset: (640+896)*1024 } ++ { .name = "ELAN-104NC flash boot partition", ++ .offset = 0, ++ .size = 640*1024 }, ++ { .name = "ELAN-104NC flash partition 1", ++ .offset = 640*1024, ++ .size = 896*1024 }, ++ { .name = "ELAN-104NC flash partition 2", ++ .offset = (640+896)*1024 } + }; + #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + +@@ -195,19 +196,20 @@ + } + + static struct map_info elan_104nc_map = { +- name: "ELAN-104NC flash", +- size: 8*1024*1024, /* this must be set to a maximum possible amount ++ .name = "ELAN-104NC flash", ++ .phys = NO_XIP, ++ .size = 8*1024*1024, /* this must be set to a maximum possible amount + of flash so the cfi probe routines find all + the chips */ +- buswidth: 2, +- read8: elan_104nc_read8, +- read16: elan_104nc_read16, +- read32: elan_104nc_read32, +- copy_from: elan_104nc_copy_from, +- write8: elan_104nc_write8, +- write16: elan_104nc_write16, +- write32: elan_104nc_write32, +- copy_to: elan_104nc_copy_to ++ .buswidth = 2, ++ .read8 = elan_104nc_read8, ++ .read16 = elan_104nc_read16, ++ .read32 = elan_104nc_read32, ++ .copy_from = elan_104nc_copy_from, ++ .write8 = elan_104nc_write8, ++ .write16 = elan_104nc_write16, ++ .write32 = elan_104nc_write32, ++ .copy_to = elan_104nc_copy_to + }; + + /* MTD device for all of the flash. */ +@@ -221,20 +223,13 @@ + } + + iounmap((void *)iomapadr); +- release_region(PAGE_IO,PAGE_IO_SIZE); + } + + int __init init_elan_104nc(void) + { +- /* Urg! We use I/O port 0x22 without request_region()ing it */ +- /* +- if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) { +- printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", +- elan_104nc_map.name, +- PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); +- return -EAGAIN; +- } +- */ ++ /* Urg! We use I/O port 0x22 without request_region()ing it, ++ because it's already allocated to the PIC. */ ++ + iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); + if (!iomapadr) { + printk( KERN_ERR"%s: failed to ioremap memory region\n", +@@ -242,10 +237,6 @@ + return -EIO; + } + +- /* +- request_region( PAGE_IO, PAGE_IO_SIZE, "ELAN-104NC flash" ); +- */ +- + printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", + elan_104nc_map.name, + PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1, +@@ -260,7 +251,7 @@ + return -ENXIO; + } + +- all_mtd->module=THIS_MODULE; ++ all_mtd->owner = THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions( all_mtd, partition_info, NUM_PARTITIONS ); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/epxa10db-flash.c linux/drivers/mtd/maps/epxa10db-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/epxa10db-flash.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/epxa10db-flash.c 2004-11-19 10:25:11.913197664 +0100 +@@ -5,7 +5,7 @@ + * Copyright (C) 2001 Altera Corporation + * Copyright (C) 2001 Red Hat, Inc. + * +- * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ ++ * $Id: epxa10db-flash.c,v 1.10 2003/05/21 12:45:18 dwmw2 Exp $ + * + * 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 +@@ -26,6 +26,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -43,87 +44,38 @@ + + static struct mtd_info *mymtd; + +-extern int parse_redboot_partitions(struct mtd_info *, struct mtd_partition **); + static int epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts); + +-static __u8 epxa_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 epxa_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 epxa_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void epxa_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void epxa_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void epxa_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void epxa_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void epxa_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- +- + + static struct map_info epxa_map = { +- name: "EPXA flash", +- size: FLASH_SIZE, +- buswidth: 2, +- read8: epxa_read8, +- read16: epxa_read16, +- read32: epxa_read32, +- copy_from: epxa_copy_from, +- write8: epxa_write8, +- write16: epxa_write16, +- write32: epxa_write32, +- copy_to: epxa_copy_to ++ .name = "EPXA flash", ++ .size = FLASH_SIZE, ++ .buswidth = 2, ++ .phys = FLASH_START, + }; + ++static const char *probes[] = { "RedBoot", "afs", NULL }; + + static int __init epxa_mtd_init(void) + { + int i; + +- printk(KERN_NOTICE "%s flash device: %x at %x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); +- epxa_map.map_priv_1 = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); +- if (!epxa_map.map_priv_1) { ++ printk(KERN_NOTICE "%s flash device: 0x%x at 0x%x\n", BOARD_NAME, FLASH_SIZE, FLASH_START); ++ ++ epxa_map.virt = (unsigned long)ioremap(FLASH_START, FLASH_SIZE); ++ if (!epxa_map.virt) { + printk("Failed to ioremap %s flash\n",BOARD_NAME); + return -EIO; + } ++ simple_map_init(&epxa_map); + + mymtd = do_map_probe("cfi_probe", &epxa_map); + if (!mymtd) { +- iounmap((void *)epxa_map.map_priv_1); ++ iounmap((void *)epxa_map.virt); + return -ENXIO; + } + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + /* Unlock the flash device. */ + if(mymtd->unlock){ +@@ -135,23 +87,14 @@ + } + } + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nr_parts = parse_redboot_partitions(mymtd, &parts); +- +- if (nr_parts > 0) { +- add_mtd_partitions(mymtd, parts, nr_parts); +- return 0; +- } +-#endif +-#ifdef CONFIG_MTD_AFS_PARTS +- nr_parts = parse_afs_partitions(mymtd, &parts); ++#ifdef CONFIG_MTD_PARTITIONS ++ nr_parts = parse_mtd_partitions(mymtd, probes, &parts, 0); + + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } + #endif +- + /* No recognised partitioning schemes found - use defaults */ + nr_parts = epxa_default_partitions(mymtd, &parts); + if (nr_parts > 0) { +@@ -173,9 +116,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (epxa_map.map_priv_1) { +- iounmap((void *)epxa_map.map_priv_1); +- epxa_map.map_priv_1 = 0; ++ if (epxa_map.virt) { ++ iounmap((void *)epxa_map.virt); ++ epxa_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/fortunet.c linux/drivers/mtd/maps/fortunet.c +--- linux-mips-2.4.27/drivers/mtd/maps/fortunet.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/fortunet.c 2004-11-19 10:25:11.915197360 +0100 +@@ -1,11 +1,12 @@ + /* fortunet.c memory map + * +- * $Id: fortunet.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ ++ * $Id: fortunet.c,v 1.6 2003/05/21 12:45:18 dwmw2 Exp $ + */ + + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -23,7 +24,7 @@ + + struct map_region + { +- int window_addr_phyical; ++ int window_addr_physical; + int altbuswidth; + struct map_info map_info; + struct mtd_info *mymtd; +@@ -37,57 +38,10 @@ + static int map_regions_parts[MAX_NUM_REGIONS] = {0,0,0,0}; + + +-__u8 fortunet_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-__u16 fortunet_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-__u32 fortunet_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(__u32 *)(map->map_priv_1 + ofs); +-} +- +-void fortunet_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void fortunet_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-void fortunet_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-void fortunet_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-void fortunet_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} + + struct map_info default_map = { +- size: DEF_WINDOW_SIZE, +- buswidth: 4, +- read8: fortunet_read8, +- read16: fortunet_read16, +- read32: fortunet_read32, +- copy_from: fortunet_copy_from, +- write8: fortunet_write8, +- write16: fortunet_write16, +- write32: fortunet_write32, +- copy_to: fortunet_copy_to ++ .size = DEF_WINDOW_SIZE, ++ .buswidth = 4, + }; + + static char * __init get_string_option(char *dest,int dest_size,char *sor) +@@ -147,7 +101,7 @@ + get_options (get_string_option(string,sizeof(string),line),6,params); + if(params[0]<1) + { +- printk(MTD_FORTUNET_PK "Bad paramters for MTD Region " ++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Region " + " name,region-number[,base,size,buswidth,altbuswidth]\n"); + return 1; + } +@@ -161,14 +115,14 @@ + memcpy(&map_regions[params[1]].map_info, + &default_map,sizeof(map_regions[params[1]].map_info)); + map_regions_set[params[1]] = 1; +- map_regions[params[1]].window_addr_phyical = DEF_WINDOW_ADDR_PHY; ++ map_regions[params[1]].window_addr_physical = DEF_WINDOW_ADDR_PHY; + map_regions[params[1]].altbuswidth = 2; + map_regions[params[1]].mymtd = NULL; + map_regions[params[1]].map_info.name = map_regions[params[1]].map_name; + strcpy(map_regions[params[1]].map_info.name,string); + if(params[0]>1) + { +- map_regions[params[1]].window_addr_phyical = params[2]; ++ map_regions[params[1]].window_addr_physical = params[2]; + } + if(params[0]>2) + { +@@ -185,14 +139,14 @@ + return 1; + } + +-static int __init MTD_New_Partion(char *line) ++static int __init MTD_New_Partition(char *line) + { + char string[MAX_NAME_SIZE]; + int params[4]; + get_options (get_string_option(string,sizeof(string),line),4,params); + if(params[0]<3) + { +- printk(MTD_FORTUNET_PK "Bad paramters for MTD Partion " ++ printk(MTD_FORTUNET_PK "Bad parameters for MTD Partition " + " name,region-number,size,offset\n"); + return 1; + } +@@ -204,7 +158,7 @@ + } + if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS) + { +- printk(MTD_FORTUNET_PK "Out of space for partion in this region\n"); ++ printk(MTD_FORTUNET_PK "Out of space for partition in this region\n"); + return 1; + } + map_regions[params[1]].parts[map_regions_parts[params[1]]].name = +@@ -220,7 +174,10 @@ + } + + __setup("MTD_Region=", MTD_New_Region); +-__setup("MTD_Partion=", MTD_New_Partion); ++__setup("MTD_Partition=", MTD_New_Partition); ++ ++/* Backwards-spelling-compatibility */ ++__setup("MTD_Partion=", MTD_New_Partition); + + int __init init_fortunet(void) + { +@@ -229,13 +186,13 @@ + { + if(map_regions_parts[ix]&&(!map_regions_set[ix])) + { +- printk(MTD_FORTUNET_PK "Region %d is not setup (Seting to default)\n", ++ printk(MTD_FORTUNET_PK "Region %d is not setup (Setting to default)\n", + ix); + memset(&map_regions[ix],0,sizeof(map_regions[ix])); + memcpy(&map_regions[ix].map_info,&default_map, + sizeof(map_regions[ix].map_info)); + map_regions_set[ix] = 1; +- map_regions[ix].window_addr_phyical = DEF_WINDOW_ADDR_PHY; ++ map_regions[ix].window_addr_physical = DEF_WINDOW_ADDR_PHY; + map_regions[ix].altbuswidth = 2; + map_regions[ix].mymtd = NULL; + map_regions[ix].map_info.name = map_regions[ix].map_name; +@@ -244,30 +201,35 @@ + if(map_regions_set[ix]) + { + iy++; +- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at phyicaly " ++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at physically " + " address %x size %x\n", + map_regions[ix].map_info.name, +- map_regions[ix].window_addr_phyical, ++ map_regions[ix].window_addr_physical, + map_regions[ix].map_info.size); +- map_regions[ix].map_info.map_priv_1 = ++ ++ map_regions[ix].map_info.phys = map_regions[ix].window_addr_physical, ++ ++ map_regions[ix].map_info.virt = + (int)ioremap_nocache( +- map_regions[ix].window_addr_phyical, ++ map_regions[ix].window_addr_physical, + map_regions[ix].map_info.size); +- if(!map_regions[ix].map_info.map_priv_1) ++ if(!map_regions[ix].map_info.virt) + { + printk(MTD_FORTUNET_PK "%s flash failed to ioremap!\n", + map_regions[ix].map_info.name); + return -ENXIO; + } +- printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is veritualy at: %x\n", ++ simple_map_init(&map_regions[ix].map_info); ++ ++ printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash is virtually at: %x\n", + map_regions[ix].map_info.name, +- map_regions[ix].map_info.map_priv_1); ++ map_regions[ix].map_info.virt); + map_regions[ix].mymtd = do_map_probe("cfi_probe", + &map_regions[ix].map_info); + if((!map_regions[ix].mymtd)&&( + map_regions[ix].altbuswidth!=map_regions[ix].map_info.buswidth)) + { +- printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternet buswidth " ++ printk(KERN_NOTICE MTD_FORTUNET_PK "Trying alternate buswidth " + "for %s flash.\n", + map_regions[ix].map_info.name); + map_regions[ix].map_info.buswidth = +@@ -275,7 +237,7 @@ + map_regions[ix].mymtd = do_map_probe("cfi_probe", + &map_regions[ix].map_info); + } +- map_regions[ix].mymtd->module = THIS_MODULE; ++ map_regions[ix].mymtd->owner = THIS_MODULE; + add_mtd_partitions(map_regions[ix].mymtd, + map_regions[ix].parts,map_regions_parts[ix]); + } +@@ -297,7 +259,7 @@ + del_mtd_partitions( map_regions[ix].mymtd ); + map_destroy( map_regions[ix].mymtd ); + } +- iounmap((void *)map_regions[ix].map_info.map_priv_1); ++ iounmap((void *)map_regions[ix].map_info.virt); + } + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/h720x-flash.c linux/drivers/mtd/maps/h720x-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/h720x-flash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/h720x-flash.c 2004-11-19 10:25:11.917197056 +0100 +@@ -0,0 +1,142 @@ ++/* ++ * Flash memory access on Hynix GMS30C7201/HMS30C7202 based ++ * evaluation boards ++ * ++ * (C) 2002 Jungjun Kim <jungjun.kim@hynix.com> ++ * 2003 Thomas Gleixner <tglx@linutronix.de> ++*/ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/slab.h> ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <asm/hardware.h> ++#include <asm/io.h> ++ ++static struct mtd_info *mymtd; ++ ++static struct map_info h720x_map = { ++ .name = "H720X", ++ .buswidth = 4, ++ .size = FLASH_SIZE, ++ .phys = FLASH_PHYS, ++}; ++ ++static struct mtd_partition h720x_partitions[] = { ++ { ++ .name = "ArMon", ++ .size = 0x00080000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "Env", ++ .size = 0x00040000, ++ .offset = 0x00080000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "Kernel", ++ .size = 0x00180000, ++ .offset = 0x000c0000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "Ramdisk", ++ .size = 0x00400000, ++ .offset = 0x00240000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND ++ } ++}; ++ ++#define NUM_PARTITIONS (sizeof(h720x_partitions)/sizeof(h720x_partitions[0])) ++ ++static int nr_mtd_parts; ++static struct mtd_partition *mtd_parts; ++static const char *probes[] = { "cmdlinepart", NULL }; ++ ++/* ++ * Initialize FLASH support ++ */ ++int __init h720x_mtd_init(void) ++{ ++ ++ char *part_type = NULL; ++ ++ h720x_map.virt = (unsigned long)ioremap(FLASH_PHYS, FLASH_SIZE); ++ ++ if (!h720x_map.virt) { ++ printk(KERN_ERR "H720x-MTD: ioremap failed\n"); ++ return -EIO; ++ } ++ ++ simple_map_init(&h720x_map); ++ ++ // Probe for flash buswidth 4 ++ printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n"); ++ mymtd = do_map_probe("cfi_probe", &h720x_map); ++ if (!mymtd) { ++ printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n"); ++ // Probe for buswidth 2 ++ h720x_map.buswidth = 2; ++ mymtd = do_map_probe("cfi_probe", &h720x_map); ++ } ++ ++ if (mymtd) { ++ mymtd->owner = THIS_MODULE; ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0); ++ if (nr_mtd_parts > 0) ++ part_type = "command line"; ++#endif ++ if (nr_mtd_parts <= 0) { ++ mtd_parts = h720x_partitions; ++ nr_mtd_parts = NUM_PARTITIONS; ++ part_type = "builtin"; ++ } ++ printk(KERN_INFO "Using %s partition table\n", part_type); ++ add_mtd_partitions(mymtd, mtd_parts, nr_mtd_parts); ++ return 0; ++ } ++ ++ iounmap((void *)h720x_map.virt); ++ return -ENXIO; ++} ++ ++/* ++ * Cleanup ++ */ ++static void __exit h720x_mtd_cleanup(void) ++{ ++ ++ if (mymtd) { ++ del_mtd_partitions(mymtd); ++ map_destroy(mymtd); ++ } ++ ++ /* Free partition info, if commandline partition was used */ ++ if (mtd_parts && (mtd_parts != h720x_partitions)) ++ kfree (mtd_parts); ++ ++ if (h720x_map.virt) { ++ iounmap((void *)h720x_map.virt); ++ h720x_map.virt = 0; ++ } ++} ++ ++ ++module_init(h720x_mtd_init); ++module_exit(h720x_mtd_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); ++MODULE_DESCRIPTION("MTD map driver for Hynix evaluation boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ichxrom.c linux/drivers/mtd/maps/ichxrom.c +--- linux-mips-2.4.27/drivers/mtd/maps/ichxrom.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/ichxrom.c 2004-11-19 10:25:11.918196904 +0100 +@@ -0,0 +1,380 @@ ++/* ++ * ichxrom.c ++ * ++ * Normal mappings of chips in physical memory ++ * $Id: ichxrom.c,v 1.1 2003/10/27 19:49:23 thayne Exp $ ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <asm/io.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/config.h> ++#include <linux/pci.h> ++#include <linux/pci_ids.h> ++ ++#define xstr(s) str(s) ++#define str(s) #s ++#define MOD_NAME xstr(KBUILD_BASENAME) ++ ++#define MTD_DEV_NAME_LENGTH 16 ++ ++#define RESERVE_MEM_REGION 0 ++ ++#define ICHX_FWH_REGION_START 0xFF000000UL ++#define ICHX_FWH_REGION_SIZE 0x01000000UL ++#define BIOS_CNTL 0x4e ++#define FWH_DEC_EN1 0xE3 ++#define FWH_DEC_EN2 0xF0 ++#define FWH_SEL1 0xE8 ++#define FWH_SEL2 0xEE ++ ++struct ichxrom_map_info { ++ struct map_info map; ++ struct mtd_info *mtd; ++ unsigned long window_addr; ++ struct pci_dev *pdev; ++ struct resource window_rsrc; ++ struct resource rom_rsrc; ++ char mtd_name[MTD_DEV_NAME_LENGTH]; ++}; ++ ++static inline unsigned long addr(struct map_info *map, unsigned long ofs) ++{ ++ unsigned long offset; ++ offset = ((8*1024*1024) - map->size) + ofs; ++ if (offset >= (4*1024*1024)) { ++ offset += 0x400000; ++ } ++ return map->map_priv_1 + 0x400000 + offset; ++} ++ ++static inline unsigned long dbg_addr(struct map_info *map, unsigned long addr) ++{ ++ return addr - map->map_priv_1 + ICHX_FWH_REGION_START; ++} ++ ++static __u8 ichxrom_read8(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readb(addr(map, ofs)); ++} ++ ++static __u16 ichxrom_read16(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readw(addr(map, ofs)); ++} ++ ++static __u32 ichxrom_read32(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readl(addr(map, ofs)); ++} ++ ++static void ichxrom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio(to, addr(map, from), len); ++} ++ ++static void ichxrom_write8(struct map_info *map, __u8 d, unsigned long ofs) ++{ ++ __raw_writeb(d, addr(map,ofs)); ++ mb(); ++} ++ ++static void ichxrom_write16(struct map_info *map, __u16 d, unsigned long ofs) ++{ ++ __raw_writew(d, addr(map, ofs)); ++ mb(); ++} ++ ++static void ichxrom_write32(struct map_info *map, __u32 d, unsigned long ofs) ++{ ++ __raw_writel(d, addr(map, ofs)); ++ mb(); ++} ++ ++static void ichxrom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio(addr(map, to), from, len); ++} ++ ++static struct ichxrom_map_info ichxrom_map = { ++ .map = { ++ .name = MOD_NAME, ++ .phys = NO_XIP, ++ .size = 0, ++ .buswidth = 1, ++ .read8 = ichxrom_read8, ++ .read16 = ichxrom_read16, ++ .read32 = ichxrom_read32, ++ .copy_from = ichxrom_copy_from, ++ .write8 = ichxrom_write8, ++ .write16 = ichxrom_write16, ++ .write32 = ichxrom_write32, ++ .copy_to = ichxrom_copy_to, ++ /* Firmware hubs only use vpp when being programmed ++ * in a factory setting. So in-place programming ++ * needs to use a different method. ++ */ ++ }, ++ /* remaining fields of structure are initialized to 0 */ ++}; ++ ++enum fwh_lock_state { ++ FWH_DENY_WRITE = 1, ++ FWH_IMMUTABLE = 2, ++ FWH_DENY_READ = 4, ++}; ++ ++static void ichxrom_cleanup(struct ichxrom_map_info *info) ++{ ++ u16 word; ++ ++ /* Disable writes through the rom window */ ++ pci_read_config_word(info->pdev, BIOS_CNTL, &word); ++ pci_write_config_word(info->pdev, BIOS_CNTL, word & ~1); ++ ++ if (info->mtd) { ++ del_mtd_device(info->mtd); ++ map_destroy(info->mtd); ++ info->mtd = NULL; ++ info->map.virt = 0; ++ } ++ if (info->rom_rsrc.parent) ++ release_resource(&info->rom_rsrc); ++ if (info->window_rsrc.parent) ++ release_resource(&info->window_rsrc); ++ ++ if (info->window_addr) { ++ iounmap((void *)(info->window_addr)); ++ info->window_addr = 0; ++ } ++} ++ ++ ++static int ichxrom_set_lock_state(struct mtd_info *mtd, loff_t ofs, size_t len, ++ enum fwh_lock_state state) ++{ ++ struct map_info *map = mtd->priv; ++ unsigned long start = ofs; ++ unsigned long end = start + len -1; ++ ++ /* FIXME do I need to guard against concurrency here? */ ++ /* round down to 64K boundaries */ ++ start = start & ~0xFFFF; ++ end = end & ~0xFFFF; ++ while (start <= end) { ++ unsigned long ctrl_addr; ++ ctrl_addr = addr(map, start) - 0x400000 + 2; ++ writeb(state, ctrl_addr); ++ start = start + 0x10000; ++ } ++ return 0; ++} ++ ++static int ichxrom_lock(struct mtd_info *mtd, loff_t ofs, size_t len) ++{ ++ return ichxrom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE); ++} ++ ++static int ichxrom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) ++{ ++ return ichxrom_set_lock_state(mtd, ofs, len, 0); ++} ++ ++static int __devinit ichxrom_init_one (struct pci_dev *pdev, ++ const struct pci_device_id *ent) ++{ ++ u16 word; ++ struct ichxrom_map_info *info = &ichxrom_map; ++ unsigned long map_size; ++ ++ /* For now I just handle the ichx and I assume there ++ * are not a lot of resources up at the top of the address ++ * space. It is possible to handle other devices in the ++ * top 16MB but it is very painful. Also since ++ * you can only really attach a FWH to an ICHX there ++ * a number of simplifications you can make. ++ * ++ * Also you can page firmware hubs if an 8MB window isn't enough ++ * but don't currently handle that case either. ++ */ ++ ++ info->pdev = pdev; ++ ++ /* ++ * Try to reserve the window mem region. If this fails then ++ * it is likely due to the window being "reseved" by the BIOS. ++ */ ++ info->window_rsrc.name = MOD_NAME; ++ info->window_rsrc.start = ICHX_FWH_REGION_START; ++ info->window_rsrc.end = ICHX_FWH_REGION_START + ICHX_FWH_REGION_SIZE - 1; ++ info->window_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&iomem_resource, &info->window_rsrc)) { ++ info->window_rsrc.parent = NULL; ++ printk(KERN_ERR MOD_NAME ++ " %s(): Unable to register resource" ++ " 0x%.08lx-0x%.08lx - kernel bug?\n", ++ __func__, ++ info->window_rsrc.start, info->window_rsrc.end); ++ } ++ ++ /* Enable writes through the rom window */ ++ pci_read_config_word(pdev, BIOS_CNTL, &word); ++ if (!(word & 1) && (word & (1<<1))) { ++ /* The BIOS will generate an error if I enable ++ * this device, so don't even try. ++ */ ++ printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n"); ++ goto failed; ++ } ++ pci_write_config_word(pdev, BIOS_CNTL, word | 1); ++ ++ ++ /* Map the firmware hub into my address space. */ ++ /* Does this use too much virtual address space? */ ++ info->window_addr = (unsigned long)ioremap( ++ ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE); ++ if (!info->window_addr) { ++ printk(KERN_ERR "Failed to ioremap\n"); ++ goto failed; ++ } ++ ++ /* For now assume the firmware has setup all relevant firmware ++ * windows. We don't have enough information to handle this case ++ * intelligently. ++ */ ++ ++ /* FIXME select the firmware hub and enable a window to it. */ ++ ++ info->mtd = 0; ++ info->map.map_priv_1 = info->window_addr; ++ ++ map_size = ICHX_FWH_REGION_SIZE; ++ while(!info->mtd && (map_size > 0)) { ++ info->map.size = map_size; ++ info->mtd = do_map_probe("jedec_probe", &ichxrom_map.map); ++ map_size -= 512*1024; ++ } ++ if (!info->mtd) { ++ goto failed; ++ } ++ /* I know I can only be a firmware hub here so put ++ * in the special lock and unlock routines. ++ */ ++ info->mtd->lock = ichxrom_lock; ++ info->mtd->unlock = ichxrom_unlock; ++ ++ info->mtd->owner = THIS_MODULE; ++ add_mtd_device(info->mtd); ++ ++ if (info->window_rsrc.parent) { ++ /* ++ * Registering the MTD device in iomem may not be possible ++ * if there is a BIOS "reserved" and BUSY range. If this ++ * fails then continue anyway. ++ */ ++ snprintf(info->mtd_name, MTD_DEV_NAME_LENGTH, ++ "mtd%d", info->mtd->index); ++ ++ info->rom_rsrc.name = info->mtd_name; ++ info->rom_rsrc.start = ICHX_FWH_REGION_START ++ + ICHX_FWH_REGION_SIZE - map_size; ++ info->rom_rsrc.end = ICHX_FWH_REGION_START ++ + ICHX_FWH_REGION_SIZE; ++ info->rom_rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY; ++ if (request_resource(&info->window_rsrc, &info->rom_rsrc)) { ++ printk(KERN_ERR MOD_NAME ++ ": cannot reserve MTD resource\n"); ++ info->rom_rsrc.parent = NULL; ++ } ++ } ++ ++ return 0; ++ ++ failed: ++ ichxrom_cleanup(info); ++ return -ENODEV; ++} ++ ++ ++static void __devexit ichxrom_remove_one (struct pci_dev *pdev) ++{ ++ struct ichxrom_map_info *info = &ichxrom_map; ++ u16 word; ++ ++ del_mtd_device(info->mtd); ++ map_destroy(info->mtd); ++ info->mtd = 0; ++ info->map.map_priv_1 = 0; ++ ++ iounmap((void *)(info->window_addr)); ++ info->window_addr = 0; ++ ++ /* Disable writes through the rom window */ ++ pci_read_config_word(pdev, BIOS_CNTL, &word); ++ pci_write_config_word(pdev, BIOS_CNTL, word & ~1); ++ ++#if RESERVE_MEM_REGION ++ release_mem_region(ICHX_FWH_REGION_START, ICHX_FWH_REGION_SIZE); ++#endif ++} ++ ++static struct pci_device_id ichxrom_pci_tbl[] __devinitdata = { ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0, ++ PCI_ANY_ID, PCI_ANY_ID, }, ++ { 0, }, ++}; ++ ++MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl); ++ ++#if 0 ++static struct pci_driver ichxrom_driver = { ++ .name = MOD_NAME, ++ .id_table = ichxrom_pci_tbl, ++ .probe = ichxrom_init_one, ++ .remove = ichxrom_remove_one, ++}; ++#endif ++ ++static struct pci_dev *mydev; ++int __init init_ichxrom(void) ++{ ++ struct pci_dev *pdev; ++ struct pci_device_id *id; ++ pdev = 0; ++ for(id = ichxrom_pci_tbl; id->vendor; id++) { ++ pdev = pci_find_device(id->vendor, id->device, 0); ++ if (pdev) { ++ break; ++ } ++ } ++ if (pdev) { ++ mydev = pdev; ++ return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]); ++ } ++ return -ENXIO; ++#if 0 ++ return pci_module_init(&ichxrom_driver); ++#endif ++} ++ ++static void __exit cleanup_ichxrom(void) ++{ ++ ichxrom_remove_one(mydev); ++} ++ ++module_init(init_ichxrom); ++module_exit(cleanup_ichxrom); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>"); ++MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/impa7.c linux/drivers/mtd/maps/impa7.c +--- linux-mips-2.4.27/drivers/mtd/maps/impa7.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/impa7.c 2004-11-19 10:25:11.920196600 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: impa7.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ ++ * $Id: impa7.c,v 1.9 2003/06/23 11:47:43 dwmw2 Exp $ + * + * Handle mapping of the NOR flash on implementa A7 boards + * +@@ -13,6 +13,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -37,75 +38,17 @@ + + static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 }; + +-__u8 impa7_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 impa7_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 impa7_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void impa7_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void impa7_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void impa7_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void impa7_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void impa7_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + static struct map_info impa7_map[NUM_FLASHBANKS] = { + { +- name: "impA7 NOR Flash Bank #0", +- size: WINDOW_SIZE0, +- buswidth: BUSWIDTH, +- read8: impa7_read8, +- read16: impa7_read16, +- read32: impa7_read32, +- copy_from: impa7_copy_from, +- write8: impa7_write8, +- write16: impa7_write16, +- write32: impa7_write32, +- copy_to: impa7_copy_to ++ .name = "impA7 NOR Flash Bank #0", ++ .size = WINDOW_SIZE0, ++ .buswidth = BUSWIDTH, + }, + { +- name: "impA7 NOR Flash Bank #1", +- size: WINDOW_SIZE1, +- buswidth: BUSWIDTH, +- read8: impa7_read8, +- read16: impa7_read16, +- read32: impa7_read32, +- copy_from: impa7_copy_from, +- write8: impa7_write8, +- write16: impa7_write16, +- write32: impa7_write32, +- copy_to: impa7_copy_to ++ .name = "impA7 NOR Flash Bank #1", ++ .size = WINDOW_SIZE1, ++ .buswidth = BUSWIDTH, + }, + }; + +@@ -117,24 +60,18 @@ + static struct mtd_partition static_partitions[] = + { + { +- name: "FileSystem", +- size: 0x800000, +- offset: 0x00000000 ++ .name = "FileSystem", ++ .size = 0x800000, ++ .offset = 0x00000000 + }, + }; + +-#define NB_OF(x) (sizeof (x) / sizeof (x[0])) ++static int mtd_parts_nb[NUM_FLASHBANKS]; ++static struct mtd_partition *mtd_parts[NUM_FLASHBANKS]; + +-#ifdef CONFIG_MTD_CMDLINE_PARTS +-int parse_cmdline_partitions(struct mtd_info *master, +- struct mtd_partition **pparts, +- const char *mtd_id); + #endif + +-#endif +- +-static int mtd_parts_nb = 0; +-static struct mtd_partition *mtd_parts = 0; ++static const char *probes[] = { "cmdlinepart", NULL }; + + int __init init_impa7(void) + { +@@ -146,20 +83,21 @@ + { WINDOW_ADDR0, WINDOW_SIZE0 }, + { WINDOW_ADDR1, WINDOW_SIZE1 }, + }; +- char mtdid[10]; + int devicesfound = 0; + + for(i=0; i<NUM_FLASHBANKS; i++) + { + printk(KERN_NOTICE MSG_PREFIX "probing 0x%08lx at 0x%08lx\n", + pt[i].size, pt[i].addr); +- impa7_map[i].map_priv_1 = (unsigned long) +- ioremap(pt[i].addr, pt[i].size); + +- if (!impa7_map[i].map_priv_1) { ++ impa7_map[i].phys = pt[i].addr; ++ impa7_map[i].virt = (unsigned long) ++ ioremap(pt[i].addr, pt[i].size); ++ if (!impa7_map[i].virt) { + printk(MSG_PREFIX "failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&impa7_map[i]); + + impa7_mtd[i] = 0; + type = rom_probe_types; +@@ -167,43 +105,34 @@ + impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]); + } + +- if (impa7_mtd[i]) +- { +- impa7_mtd[i]->module = THIS_MODULE; +- add_mtd_device(impa7_mtd[i]); ++ if (impa7_mtd[i]) { ++ impa7_mtd[i]->owner = THIS_MODULE; + devicesfound++; + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- sprintf(mtdid, MTDID, i); +- mtd_parts_nb = parse_cmdline_partitions(impa7_mtd[i], +- &mtd_parts, +- mtdid); +- if (mtd_parts_nb > 0) ++ mtd_parts_nb[i] = parse_mtd_partitions(impa7_mtd[i], ++ probes, ++ &mtd_parts[i], ++ 0); ++ if (mtd_parts_nb[i] > 0) { + part_type = "command line"; +-#endif +- if (mtd_parts_nb <= 0) +- { +- mtd_parts = static_partitions; +- mtd_parts_nb = NB_OF(static_partitions); ++ } else { ++ mtd_parts[i] = static_partitions; ++ mtd_parts_nb[i] = ARRAY_SIZE(static_partitions); + part_type = "static"; + } +- if (mtd_parts_nb <= 0) +- { +- printk(KERN_NOTICE MSG_PREFIX +- "no partition info available\n"); +- } +- else +- { ++ + printk(KERN_NOTICE MSG_PREFIX + "using %s partition definition\n", + part_type); + add_mtd_partitions(impa7_mtd[i], +- mtd_parts, mtd_parts_nb); +- } ++ mtd_parts[i], mtd_parts_nb[i]); ++#else ++ add_mtd_device(impa7_mtd[i]); ++ + #endif + } + else +- iounmap((void *)impa7_map[i].map_priv_1); ++ iounmap((void *)impa7_map[i].virt); + } + return devicesfound == 0 ? -ENXIO : 0; + } +@@ -211,17 +140,16 @@ + static void __exit cleanup_impa7(void) + { + int i; +- for (i=0; i<NUM_FLASHBANKS; i++) +- { +- if (impa7_mtd[i]) +- { ++ for (i=0; i<NUM_FLASHBANKS; i++) { ++ if (impa7_mtd[i]) { ++#ifdef CONFIG_MTD_PARTITIONS ++ del_mtd_partitions(impa7_mtd[i]); ++#else + del_mtd_device(impa7_mtd[i]); ++#endif + map_destroy(impa7_mtd[i]); +- } +- if (impa7_map[i].map_priv_1) +- { +- iounmap((void *)impa7_map[i].map_priv_1); +- impa7_map[i].map_priv_1 = 0; ++ iounmap((void *)impa7_map[i].virt); ++ impa7_map[i].virt = 0; + } + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/integrator-flash-v24.c linux/drivers/mtd/maps/integrator-flash-v24.c +--- linux-mips-2.4.27/drivers/mtd/maps/integrator-flash-v24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/integrator-flash-v24.c 2004-11-19 10:25:11.921196448 +0100 +@@ -0,0 +1,258 @@ ++/*====================================================================== ++ ++ drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning ++ ++ Copyright (C) 2000 ARM Limited ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ This is access code for flashes using ARM's flash partitioning ++ standards. ++ ++ $Id: integrator-flash-v24.c,v 1.12 2003/10/11 09:43:58 rmk Exp $ ++ ++======================================================================*/ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/ioport.h> ++#include <linux/init.h> ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++ ++#include <asm/hardware.h> ++#include <asm/io.h> ++#include <asm/system.h> ++ ++// board specific stuff - sorry, it should be in arch/arm/mach-*. ++#ifdef CONFIG_ARCH_INTEGRATOR ++ ++#define FLASH_BASE INTEGRATOR_FLASH_BASE ++#define FLASH_SIZE INTEGRATOR_FLASH_SIZE ++ ++#define FLASH_PART_SIZE 0x400000 ++ ++#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) ++#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) ++#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET) ++#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET) ++ ++/* ++ * Initialise the flash access systems: ++ * - Disable VPP ++ * - Assert WP ++ * - Set write enable bit in EBI reg ++ */ ++static void armflash_flash_init(void) ++{ ++ unsigned int tmp; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); ++ ++ tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE; ++ __raw_writel(tmp, EBI_CSR1); ++ ++ if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) { ++ __raw_writel(0xa05f, EBI_LOCK); ++ __raw_writel(tmp, EBI_CSR1); ++ __raw_writel(0, EBI_LOCK); ++ } ++} ++ ++/* ++ * Shutdown the flash access systems: ++ * - Disable VPP ++ * - Assert WP ++ * - Clear write enable bit in EBI reg ++ */ ++static void armflash_flash_exit(void) ++{ ++ unsigned int tmp; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); ++ ++ /* ++ * Clear the write enable bit in system controller EBI register. ++ */ ++ tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE; ++ __raw_writel(tmp, EBI_CSR1); ++ ++ if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) { ++ __raw_writel(0xa05f, EBI_LOCK); ++ __raw_writel(tmp, EBI_CSR1); ++ __raw_writel(0, EBI_LOCK); ++ } ++} ++ ++static void armflash_flash_wp(int on) ++{ ++ unsigned int reg; ++ ++ if (on) ++ reg = SC_CTRLC; ++ else ++ reg = SC_CTRLS; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg); ++} ++ ++static void armflash_set_vpp(struct map_info *map, int on) ++{ ++ unsigned int reg; ++ ++ if (on) ++ reg = SC_CTRLS; ++ else ++ reg = SC_CTRLC; ++ ++ __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg); ++} ++#endif ++ ++#ifdef CONFIG_ARCH_P720T ++ ++#define FLASH_BASE (0x04000000) ++#define FLASH_SIZE (64*1024*1024) ++ ++#define FLASH_PART_SIZE (4*1024*1024) ++#define FLASH_BLOCK_SIZE (128*1024) ++ ++static void armflash_flash_init(void) ++{ ++} ++ ++static void armflash_flash_exit(void) ++{ ++} ++ ++static void armflash_flash_wp(int on) ++{ ++} ++ ++static void armflash_set_vpp(struct map_info *map, int on) ++{ ++} ++#endif ++ ++ ++static struct map_info armflash_map = ++{ ++ .name = "AFS", ++ .set_vpp = armflash_set_vpp, ++ .phys = FLASH_BASE, ++}; ++ ++static struct mtd_info *mtd; ++static struct mtd_partition *parts; ++static const char *probes[] = { "RedBoot", "afs", NULL }; ++ ++static int __init armflash_cfi_init(void *base, u_int size) ++{ ++ int ret; ++ ++ armflash_flash_init(); ++ armflash_flash_wp(1); ++ ++ /* ++ * look for CFI based flash parts fitted to this board ++ */ ++ armflash_map.size = size; ++ armflash_map.buswidth = 4; ++ armflash_map.virt = (unsigned long) base; ++ ++ simple_map_init(&armflash_map); ++ ++ /* ++ * Also, the CFI layer automatically works out what size ++ * of chips we have, and does the necessary identification ++ * for us automatically. ++ */ ++ mtd = do_map_probe("cfi_probe", &armflash_map); ++ if (!mtd) ++ return -ENXIO; ++ ++ mtd->owner = THIS_MODULE; ++ ++ ret = parse_mtd_partitions(mtd, probes, &parts, (void *)0); ++ if (ret > 0) { ++ ret = add_mtd_partitions(mtd, parts, ret); ++ if (ret) ++ printk(KERN_ERR "mtd partition registration " ++ "failed: %d\n", ret); ++ } ++ ++ /* ++ * If we got an error, free all resources. ++ */ ++ if (ret < 0) { ++ del_mtd_partitions(mtd); ++ map_destroy(mtd); ++ } ++ ++ return ret; ++} ++ ++static void armflash_cfi_exit(void) ++{ ++ if (mtd) { ++ del_mtd_partitions(mtd); ++ map_destroy(mtd); ++ } ++ if (parts) ++ kfree(parts); ++} ++ ++static int __init armflash_init(void) ++{ ++ int err = -EBUSY; ++ void *base; ++ ++ if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL) ++ goto out; ++ ++ base = ioremap(FLASH_BASE, FLASH_SIZE); ++ err = -ENOMEM; ++ if (base == NULL) ++ goto release; ++ ++ err = armflash_cfi_init(base, FLASH_SIZE); ++ if (err) { ++ iounmap(base); ++release: ++ release_mem_region(FLASH_BASE, FLASH_SIZE); ++ } ++out: ++ return err; ++} ++ ++static void __exit armflash_exit(void) ++{ ++ armflash_cfi_exit(); ++ iounmap((void *)armflash_map.virt); ++ release_mem_region(FLASH_BASE, FLASH_SIZE); ++ armflash_flash_exit(); ++} ++ ++module_init(armflash_init); ++module_exit(armflash_exit); ++ ++MODULE_AUTHOR("ARM Ltd"); ++MODULE_DESCRIPTION("ARM Integrator CFI map driver"); ++MODULE_LICENSE("GPL"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/integrator-flash.c linux/drivers/mtd/maps/integrator-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/integrator-flash.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/integrator-flash.c 2004-11-19 10:25:11.923196144 +0100 +@@ -1,8 +1,9 @@ + /*====================================================================== + +- drivers/mtd/maps/armflash.c: ARM Flash Layout/Partitioning ++ drivers/mtd/maps/integrator-flash.c: ARM Integrator flash map driver + + Copyright (C) 2000 ARM Limited ++ Copyright (C) 2003 Deep Blue Solutions Ltd. + + 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 +@@ -21,7 +22,7 @@ + This is access code for flashes using ARM's flash partitioning + standards. + +- $Id: integrator-flash.c,v 1.7 2001/11/01 20:55:47 rmk Exp $ ++ $Id: integrator-flash.c,v 1.14 2003/10/11 10:00:31 rmk Exp $ + + ======================================================================*/ + +@@ -31,268 +32,181 @@ + #include <linux/kernel.h> + #include <linux/slab.h> + #include <linux/ioport.h> ++#include <linux/device.h> + #include <linux/init.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> + #include <linux/mtd/partitions.h> + ++#include <asm/mach/flash.h> + #include <asm/hardware.h> + #include <asm/io.h> + #include <asm/system.h> + +-extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **); +- +-// board specific stuff - sorry, it should be in arch/arm/mach-*. +-#ifdef CONFIG_ARCH_INTEGRATOR +- +-#define FLASH_BASE INTEGRATOR_FLASH_BASE +-#define FLASH_SIZE INTEGRATOR_FLASH_SIZE +- +-#define FLASH_PART_SIZE 0x400000 +- +-#define SC_CTRLC (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLC_OFFSET) +-#define SC_CTRLS (IO_ADDRESS(INTEGRATOR_SC_BASE) + INTEGRATOR_SC_CTRLS_OFFSET) +-#define EBI_CSR1 (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_CSR1_OFFSET) +-#define EBI_LOCK (IO_ADDRESS(INTEGRATOR_EBI_BASE) + INTEGRATOR_EBI_LOCK_OFFSET) +- +-/* +- * Initialise the flash access systems: +- * - Disable VPP +- * - Assert WP +- * - Set write enable bit in EBI reg +- */ +-static void armflash_flash_init(void) +-{ +- unsigned int tmp; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); +- +- tmp = __raw_readl(EBI_CSR1) | INTEGRATOR_EBI_WRITE_ENABLE; +- __raw_writel(tmp, EBI_CSR1); +- +- if (!(__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE)) { +- __raw_writel(0xa05f, EBI_LOCK); +- __raw_writel(tmp, EBI_CSR1); +- __raw_writel(0, EBI_LOCK); +- } +-} +- +-/* +- * Shutdown the flash access systems: +- * - Disable VPP +- * - Assert WP +- * - Clear write enable bit in EBI reg +- */ +-static void armflash_flash_exit(void) +-{ +- unsigned int tmp; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN | INTEGRATOR_SC_CTRL_nFLWP, SC_CTRLC); +- +- /* +- * Clear the write enable bit in system controller EBI register. +- */ +- tmp = __raw_readl(EBI_CSR1) & ~INTEGRATOR_EBI_WRITE_ENABLE; +- __raw_writel(tmp, EBI_CSR1); +- +- if (__raw_readl(EBI_CSR1) & INTEGRATOR_EBI_WRITE_ENABLE) { +- __raw_writel(0xa05f, EBI_LOCK); +- __raw_writel(tmp, EBI_CSR1); +- __raw_writel(0, EBI_LOCK); +- } +-} +- +-static void armflash_flash_wp(int on) +-{ +- unsigned int reg; +- +- if (on) +- reg = SC_CTRLC; +- else +- reg = SC_CTRLS; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLWP, reg); +-} +- +-static void armflash_set_vpp(struct map_info *map, int on) +-{ +- unsigned int reg; +- +- if (on) +- reg = SC_CTRLS; +- else +- reg = SC_CTRLC; +- +- __raw_writel(INTEGRATOR_SC_CTRL_nFLVPPEN, reg); +-} +-#endif +- + #ifdef CONFIG_ARCH_P720T +- + #define FLASH_BASE (0x04000000) + #define FLASH_SIZE (64*1024*1024) +- +-#define FLASH_PART_SIZE (4*1024*1024) +-#define FLASH_BLOCK_SIZE (128*1024) +- +-static void armflash_flash_init(void) +-{ +-} +- +-static void armflash_flash_exit(void) +-{ +-} +- +-static void armflash_flash_wp(int on) +-{ +-} +- +-static void armflash_set_vpp(struct map_info *map, int on) +-{ +-} + #endif + +-static __u8 armflash_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(ofs + map->map_priv_2); +-} +- +-static __u16 armflash_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(ofs + map->map_priv_2); +-} ++struct armflash_info { ++ struct flash_platform_data *plat; ++ struct resource *res; ++ struct mtd_partition *parts; ++ struct mtd_info *mtd; ++ struct map_info map; ++}; + +-static __u32 armflash_read32(struct map_info *map, unsigned long ofs) ++static void armflash_set_vpp(struct map_info *map, int on) + { +- return readl(ofs + map->map_priv_2); +-} ++ struct armflash_info *info = container_of(map, struct armflash_info, map); + +-static void armflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *) (from + map->map_priv_2), len); ++ if (info->plat && info->plat->set_vpp) ++ info->plat->set_vpp(on); + } + +-static void armflash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, adr + map->map_priv_2); +-} +- +-static void armflash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, adr + map->map_priv_2); +-} ++static const char *probes[] = { "RedBoot", "afs", NULL }; + +-static void armflash_write32(struct map_info *map, __u32 d, unsigned long adr) ++static int armflash_probe(struct device *_dev) + { +- writel(d, adr + map->map_priv_2); +-} ++ struct platform_device *dev = to_platform_device(_dev); ++ struct flash_platform_data *plat = dev->dev.platform_data; ++ struct resource *res = dev->resource; ++ unsigned int size = res->end - res->start + 1; ++ struct armflash_info *info; ++ int err; ++ void *base; + +-static void armflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *) (to + map->map_priv_2), from, len); +-} ++ info = kmalloc(sizeof(struct armflash_info), GFP_KERNEL); ++ if (!info) { ++ err = -ENOMEM; ++ goto out; ++ } + +-static struct map_info armflash_map = +-{ +- name: "AFS", +- read8: armflash_read8, +- read16: armflash_read16, +- read32: armflash_read32, +- copy_from: armflash_copy_from, +- write8: armflash_write8, +- write16: armflash_write16, +- write32: armflash_write32, +- copy_to: armflash_copy_to, +- set_vpp: armflash_set_vpp, +-}; ++ memset(info, 0, sizeof(struct armflash_info)); + +-static struct mtd_info *mtd; +-static struct mtd_partition *parts; ++ info->plat = plat; ++ if (plat && plat->init) { ++ err = plat->init(); ++ if (err) ++ goto no_resource; ++ } + +-static int __init armflash_cfi_init(void *base, u_int size) +-{ +- int ret; ++ info->res = request_mem_region(res->start, size, "armflash"); ++ if (!info->res) { ++ err = -EBUSY; ++ goto no_resource; ++ } + +- armflash_flash_init(); +- armflash_flash_wp(1); ++ base = ioremap(res->start, size); ++ if (!base) { ++ err = -ENOMEM; ++ goto no_mem; ++ } + + /* + * look for CFI based flash parts fitted to this board + */ +- armflash_map.size = size; +- armflash_map.buswidth = 4; +- armflash_map.map_priv_2 = (unsigned long) base; ++ info->map.size = size; ++ info->map.buswidth = plat->width; ++ info->map.phys = res->start; ++ info->map.virt = (unsigned long) base; ++ info->map.name = dev->dev.bus_id; ++ info->map.set_vpp = armflash_set_vpp; ++ ++ simple_map_init(&info->map); + + /* + * Also, the CFI layer automatically works out what size + * of chips we have, and does the necessary identification + * for us automatically. + */ +- mtd = do_map_probe("cfi_probe", &armflash_map); +- if (!mtd) +- return -ENXIO; +- +- mtd->module = THIS_MODULE; +- +- ret = parse_afs_partitions(mtd, &parts); +- if (ret > 0) { +- ret = add_mtd_partitions(mtd, parts, ret); +- if (ret) +- printk(KERN_ERR "mtd partition registration " +- "failed: %d\n", ret); ++ info->mtd = do_map_probe(plat->map_name, &info->map); ++ if (!info->mtd) { ++ err = -ENXIO; ++ goto no_device; + } + ++ info->mtd->owner = THIS_MODULE; ++ ++ err = parse_mtd_partitions(info->mtd, probes, &info->parts, 0); ++ if (err > 0) { ++ err = add_mtd_partitions(info->mtd, info->parts, err); ++ if (err) ++ printk(KERN_ERR ++ "mtd partition registration failed: %d\n", err); ++ } ++ ++ if (err == 0) ++ dev_set_drvdata(&dev->dev, info); ++ + /* + * If we got an error, free all resources. + */ +- if (ret < 0) { +- del_mtd_partitions(mtd); +- map_destroy(mtd); ++ if (err < 0) { ++ if (info->mtd) { ++ del_mtd_partitions(info->mtd); ++ map_destroy(info->mtd); + } ++ if (info->parts) ++ kfree(info->parts); + +- return ret; +-} +- +-static void armflash_cfi_exit(void) +-{ +- if (mtd) { +- del_mtd_partitions(mtd); +- map_destroy(mtd); ++ no_device: ++ iounmap(base); ++ no_mem: ++ release_mem_region(res->start, size); ++ no_resource: ++ if (plat && plat->exit) ++ plat->exit(); ++ kfree(info); + } +- if (parts) +- kfree(parts); ++ out: ++ return err; + } + +-static int __init armflash_init(void) ++static int armflash_remove(struct device *_dev) + { +- int err = -EBUSY; +- void *base; ++ struct platform_device *dev = to_platform_device(_dev); ++ struct armflash_info *info = dev_get_drvdata(&dev->dev); + +- if (request_mem_region(FLASH_BASE, FLASH_SIZE, "flash") == NULL) +- goto out; ++ dev_set_drvdata(&dev->dev, NULL); + +- base = ioremap(FLASH_BASE, FLASH_SIZE); +- err = -ENOMEM; +- if (base == NULL) +- goto release; ++ if (info) { ++ if (info->mtd) { ++ del_mtd_partitions(info->mtd); ++ map_destroy(info->mtd); ++ } ++ if (info->parts) ++ kfree(info->parts); + +- err = armflash_cfi_init(base, FLASH_SIZE); +- if (err) { +- iounmap(base); +-release: +- release_mem_region(FLASH_BASE, FLASH_SIZE); ++ iounmap((void *)info->map.virt); ++ release_resource(info->res); ++ kfree(info->res); ++ ++ if (info->plat && info->plat->exit) ++ info->plat->exit(); ++ ++ kfree(info); + } +-out: +- return err; ++ ++ return 0; ++} ++ ++static struct device_driver armflash_driver = { ++ .name = "armflash", ++ .bus = &platform_bus_type, ++ .probe = armflash_probe, ++ .remove = armflash_remove, ++}; ++ ++static int __init armflash_init(void) ++{ ++ return driver_register(&armflash_driver); + } + + static void __exit armflash_exit(void) + { +- armflash_cfi_exit(); +- iounmap((void *)armflash_map.map_priv_2); +- release_mem_region(FLASH_BASE, FLASH_SIZE); +- armflash_flash_exit(); ++ driver_unregister(&armflash_driver); + } + + module_init(armflash_init); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/iq80310.c linux/drivers/mtd/maps/iq80310.c +--- linux-mips-2.4.27/drivers/mtd/maps/iq80310.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/iq80310.c 2004-11-19 10:25:11.924195992 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: iq80310.c,v 1.9 2002/01/01 22:45:02 rmk Exp $ ++ * $Id: iq80310.c,v 1.17 2003/06/23 11:48:18 dwmw2 Exp $ + * + * Mapping for the Intel XScale IQ80310 evaluation board + * +@@ -14,6 +14,8 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/slab.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -26,127 +28,72 @@ + + static struct mtd_info *mymtd; + +-static __u8 iq80310_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-static __u16 iq80310_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-static __u32 iq80310_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(__u32 *)(map->map_priv_1 + ofs); +-} +- +-static void iq80310_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void iq80310_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-static void iq80310_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-static void iq80310_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-static void iq80310_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} +- + static struct map_info iq80310_map = { +- name: "IQ80310 flash", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: iq80310_read8, +- read16: iq80310_read16, +- read32: iq80310_read32, +- copy_from: iq80310_copy_from, +- write8: iq80310_write8, +- write16: iq80310_write16, +- write32: iq80310_write32, +- copy_to: iq80310_copy_to ++ .name = "IQ80310 flash", ++ .size = WINDOW_SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = WINDOW_ADDR + }; + + static struct mtd_partition iq80310_partitions[4] = { + { +- name: "Firmware", +- size: 0x00080000, +- offset: 0, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Firmware", ++ .size = 0x00080000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + },{ +- name: "Kernel", +- size: 0x000a0000, +- offset: 0x00080000, ++ .name = "Kernel", ++ .size = 0x000a0000, ++ .offset = 0x00080000, + },{ +- name: "Filesystem", +- size: 0x00600000, +- offset: 0x00120000 ++ .name = "Filesystem", ++ .size = 0x00600000, ++ .offset = 0x00120000 + },{ +- name: "RedBoot", +- size: 0x000e0000, +- offset: 0x00720000, +- mask_flags: MTD_WRITEABLE ++ .name = "RedBoot", ++ .size = 0x000e0000, ++ .offset = 0x00720000, ++ .mask_flags = MTD_WRITEABLE + } + }; + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) +- + static struct mtd_info *mymtd; + static struct mtd_partition *parsed_parts; +- +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; + + static int __init init_iq80310(void) + { + struct mtd_partition *parts; + int nb_parts = 0; + int parsed_nr_parts = 0; +- char *part_type = "static"; ++ int ret; + +- iq80310_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); +- if (!iq80310_map.map_priv_1) { ++ iq80310_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ if (!iq80310_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&iq80310_map); ++ + mymtd = do_map_probe("cfi_probe", &iq80310_map); + if (!mymtd) { +- iounmap((void *)iq80310_map.map_priv_1); ++ iounmap((void *)iq80310_map.virt); + return -ENXIO; + } +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- if (parsed_nr_parts == 0) { +- int ret = parse_redboot_partitions(mymtd, &parsed_parts); ++ ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0); + +- if (ret > 0) { +- part_type = "RedBoot"; ++ if (ret > 0) + parsed_nr_parts = ret; +- } +- } +-#endif + + if (parsed_nr_parts > 0) { + parts = parsed_parts; + nb_parts = parsed_nr_parts; + } else { + parts = iq80310_partitions; +- nb_parts = NB_OF(iq80310_partitions); ++ nb_parts = ARRAY_SIZE(iq80310_partitions); + } +- printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, parts, nb_parts); + return 0; + } +@@ -159,8 +106,8 @@ + if (parsed_parts) + kfree(parsed_parts); + } +- if (iq80310_map.map_priv_1) +- iounmap((void *)iq80310_map.map_priv_1); ++ if (iq80310_map.virt) ++ iounmap((void *)iq80310_map.virt); + } + + module_init(init_iq80310); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ixp425.c linux/drivers/mtd/maps/ixp425.c +--- linux-mips-2.4.27/drivers/mtd/maps/ixp425.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/ixp425.c 2004-11-19 10:25:11.925195840 +0100 +@@ -0,0 +1,220 @@ ++/* ++ * $Id: ixp425.c,v 1.1 2003/11/26 21:26:09 dsaxena Exp $ ++ * ++ * drivers/mtd/maps/ixp425.c ++ * ++ * MTD Map file for IXP425 based systems. Please do not make per-board ++ * map driver as the code will be 90% identical. For now just add ++ * if(machine_is_XXX()) checks to the code. I'll clean this stuff to ++ * use platform_data in the the future so we can get rid of that too. ++ * ++ * Original Author: Intel Corporation ++ * Maintainer: Deepak Saxena <dsaxena@mvista.com> ++ * ++ * Copyright (C) 2002 Intel Corporation ++ * Copyright (C) 2003 MontaVista Software, Inc. ++ * ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/init.h> ++#include <linux/kernel.h> ++#include <linux/string.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <linux/ioport.h> ++#include <asm/io.h> ++#include <asm/mach-types.h> ++ ++#include <linux/reboot.h> ++ ++#define WINDOW_ADDR 0x50000000 ++#define BUSWIDTH 2 ++ ++#ifndef __ARMEB__ ++#define BYTE0(h) ((h) & 0xFF) ++#define BYTE1(h) (((h) >> 8) & 0xFF) ++#else ++#define BYTE0(h) (((h) >> 8) & 0xFF) ++#define BYTE1(h) ((h) & 0xFF) ++#endif ++ ++static __u16 ++ixp425_read16(struct map_info *map, unsigned long ofs) ++{ ++ return *(__u16 *) (map->map_priv_1 + ofs); ++} ++ ++/* ++ * The IXP425 expansion bus only allows 16-bit wide acceses ++ * when attached to a 16-bit wide device (such as the 28F128J3A), ++ * so we can't just memcpy_fromio(). ++ */ ++static void ++ixp425_copy_from(struct map_info *map, void *to, ++ unsigned long from, ssize_t len) ++{ ++ int i; ++ u8 *dest = (u8 *) to; ++ u16 *src = (u16 *) (map->map_priv_1 + from); ++ u16 data; ++ ++ for (i = 0; i < (len / 2); i++) { ++ data = src[i]; ++ dest[i * 2] = BYTE0(data); ++ dest[i * 2 + 1] = BYTE1(data); ++ } ++ ++ if (len & 1) ++ dest[len - 1] = BYTE0(src[i]); ++} ++ ++static void ++ixp425_write16(struct map_info *map, __u16 d, unsigned long adr) ++{ ++ *(__u16 *) (map->map_priv_1 + adr) = d; ++} ++ ++static struct map_info ixp425_map = { ++ .name = "IXP425 Flash", ++ .buswidth = BUSWIDTH, ++ .read16 = ixp425_read16, ++ .copy_from = ixp425_copy_from, ++ .write16 = ixp425_write16, ++}; ++ ++/* ++ * Put flash back in read mode so RedBoot can boot properly. ++ */ ++int ixp425_mtd_reboot(struct notifier_block *n, unsigned long code, void *p) ++{ ++ if (code != SYS_RESTART) ++ return NOTIFY_DONE; ++ ++ ixp425_write16(&ixp425_map, 0xff, 0x55 * 0x2); ++ return NOTIFY_DONE; ++} ++ ++static struct notifier_block ixp425_mtd_notifier = { ++ notifier_call:ixp425_mtd_reboot, ++ next:NULL, ++ priority:0 ++}; ++ ++static struct mtd_partition *parsed_parts; ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ ++static struct mtd_partition ixp425_partitions[] = { ++ { ++ .name = "image", ++ .offset = 0x00040000, ++ .size = 0x00400000, ++ }, { ++ .name = "user", ++ .offset = 0x00440000, ++ .size = MTDPART_SIZ_FULL ++ } ++}; ++ ++#define NB_OF(x) (sizeof(x)/sizeof(x[0])) ++ ++static struct mtd_info *ixp425_mtd; ++static struct resource *mtd_resource; ++ ++static void ++ixp425_exit(void) ++{ ++ if (ixp425_mtd) { ++ del_mtd_partitions(ixp425_mtd); ++ map_destroy(ixp425_mtd); ++ } ++ if (ixp425_map.map_priv_1) ++ iounmap((void *) ixp425_map.map_priv_1); ++ if (mtd_resource) ++ release_mem_region(WINDOW_ADDR, ixp425_map.size); ++ ++ if (parsed_parts) ++ kfree(parsed_parts); ++ ++ unregister_reboot_notifier(&ixp425_mtd_notifier); ++ ++ /* Disable flash write */ ++ *IXP425_EXP_CS0 &= ~IXP425_FLASH_WRITABLE; ++ ++ if(machine_is_adi_coyote()) ++ *IXP425_EXP_CS1 &= ~IXP425_FLASH_WRITABLE; ++} ++ ++static int __init ++ixp425_init(void) ++{ ++ int res = -1, npart; ++ ++ /* Enable flash write */ ++ *IXP425_EXP_CS0 |= IXP425_FLASH_WRITABLE; ++ ++ /* ++ * Coyote requires CS1 write to be enabled and has 32MB flash. ++ * This will move to the platform init code in 2.6 ++ */ ++ if(machine_is_adi_coyote()) { ++ *IXP425_EXP_CS1 |= IXP425_FLASH_WRITABLE; ++ ixp425_map.size = 0x02000000; ++ } else ++ ixp425_map.size = 0x01000000; ++ ++ ixp425_map.map_priv_1 = 0; ++ mtd_resource = ++ request_mem_region(WINDOW_ADDR, ixp425_map.size, "IXP425 Flash"); ++ if (!mtd_resource) { ++ printk(KERN_ERR ++ "ixp425 flash: Could not request mem region.\n"); ++ res = -ENOMEM; ++ goto Error; ++ } ++ ++ ixp425_map.map_priv_1 = ++ (unsigned long) ioremap(WINDOW_ADDR, ixp425_map.size); ++ if (!ixp425_map.map_priv_1) { ++ printk("ixp425 Flash: Failed to map IO region. (ioremap)\n"); ++ res = -EIO; ++ goto Error; ++ } ++ ++ ixp425_mtd = do_map_probe("cfi_probe", &ixp425_map); ++ if (!ixp425_mtd) { ++ res = -ENXIO; ++ goto Error; ++ } ++ ixp425_mtd->owner = THIS_MODULE; ++ ++ /* Try to parse RedBoot partitions */ ++ npart = parse_mtd_partitions(ixp425_mtd, probes, &parsed_parts, 0); ++ if (npart > 0) ++ res = add_mtd_partitions(ixp425_mtd, parsed_parts, npart); ++ else { ++ printk("IXP425 Flash: Using static MTD partitions.\n"); ++ res = add_mtd_partitions(ixp425_mtd, ixp425_partitions, ++ NB_OF(ixp425_partitions)); ++ } ++ ++ if (res) ++ goto Error; ++ ++ register_reboot_notifier(&ixp425_mtd_notifier); ++ ++ return res; ++ ++Error: ++ ixp425_exit(); ++ return res; ++} ++ ++module_init(ixp425_init); ++module_exit(ixp425_exit); ++ ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("MTD map driver for ixp425 evaluation board"); ++MODULE_AUTHOR("Deepak Saxena"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/l440gx.c linux/drivers/mtd/maps/l440gx.c +--- linux-mips-2.4.27/drivers/mtd/maps/l440gx.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/l440gx.c 2004-11-19 10:25:11.927195536 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: l440gx.c,v 1.8 2002/01/10 20:27:40 eric Exp $ ++ * $Id: l440gx.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $ + * + * BIOS Flash chip on Intel 440GX board. + * +@@ -9,6 +9,7 @@ + #include <linux/module.h> + #include <linux/pci.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -27,48 +28,6 @@ + + static struct mtd_info *mymtd; + +-__u8 l440gx_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 l440gx_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 l440gx_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void l440gx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void l440gx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void l440gx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void l440gx_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void l440gx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + /* Is this really the vpp port? */ + void l440gx_set_vpp(struct map_info *map, int vpp) +@@ -85,22 +44,15 @@ + } + + struct map_info l440gx_map = { +- name: "L440GX BIOS", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: l440gx_read8, +- read16: l440gx_read16, +- read32: l440gx_read32, +- copy_from: l440gx_copy_from, +- write8: l440gx_write8, +- write16: l440gx_write16, +- write32: l440gx_write32, +- copy_to: l440gx_copy_to, ++ .name = "L440GX BIOS", ++ .size = WINDOW_SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = WINDOW_ADDR, + #if 0 + /* FIXME verify that this is the + * appripriate code for vpp enable/disable + */ +- set_vpp: l440gx_set_vpp ++ .set_vpp = l440gx_set_vpp + #endif + }; + +@@ -113,7 +65,6 @@ + dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, NULL); + +- + pm_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + +@@ -122,15 +73,14 @@ + return -ENODEV; + } + ++ l440gx_map.virt = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); + +- l440gx_map.map_priv_1 = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); +- +- if (!l440gx_map.map_priv_1) { ++ if (!l440gx_map.virt) { + printk(KERN_WARNING "Failed to ioremap L440GX flash region\n"); + return -ENOMEM; + } +- +- printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.map_priv_1); ++ simple_map_init(&l440gx_map); ++ printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt); + + /* Setup the pm iobase resource + * This code should move into some kind of generic bridge +@@ -153,7 +103,7 @@ + /* Allocate the resource region */ + if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) { + printk(KERN_WARNING "Could not allocate pm iobase resource\n"); +- iounmap((void *)l440gx_map.map_priv_1); ++ iounmap((void *)l440gx_map.virt); + return -ENXIO; + } + } +@@ -181,13 +131,13 @@ + mymtd = do_map_probe("map_rom", &l440gx_map); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + add_mtd_device(mymtd); + return 0; + } + +- iounmap((void *)l440gx_map.map_priv_1); ++ iounmap((void *)l440gx_map.virt); + return -ENXIO; + } + +@@ -196,7 +146,7 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + +- iounmap((void *)l440gx_map.map_priv_1); ++ iounmap((void *)l440gx_map.virt); + } + + module_init(init_l440gx); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/lasat.c linux/drivers/mtd/maps/lasat.c +--- linux-mips-2.4.27/drivers/mtd/maps/lasat.c 2003-08-18 04:59:02.000000000 +0200 ++++ linux/drivers/mtd/maps/lasat.c 2004-11-19 10:25:11.928195384 +0100 +@@ -1,11 +1,20 @@ + /* +- * Flash device on lasat 100 and 200 boards ++ * Flash device on Lasat 100 and 200 boards ++ * ++ * (C) 2002 Brian Murphy <brian@murphy.dk> ++ * ++ * 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. ++ * ++ * $Id: lasat.c,v 1.6 2003/09/02 16:36:40 brm Exp $ + * + */ + + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -13,123 +22,80 @@ + #include <linux/config.h> + #include <asm/lasat/lasat.h> + +-static struct mtd_info *mymtd; +- +-static __u8 sp_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 sp_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 sp_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void sp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} ++static struct mtd_info *lasat_mtd; + +-static void sp_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void sp_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void sp_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} ++static struct mtd_partition partition_info[LASAT_MTD_LAST]; ++static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"}; + +-static void sp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++static void lasat_set_vpp(struct map_info *map, int vpp) + { +- memcpy_toio(map->map_priv_1 + to, from, len); ++ if (vpp) ++ *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; ++ else ++ *lasat_misc->flash_wp_reg &= ~(1 << lasat_misc->flash_wp_bit); + } + +-static struct map_info sp_map = { +- name: "SP flash", +- buswidth: 4, +- read8: sp_read8, +- read16: sp_read16, +- read32: sp_read32, +- copy_from: sp_copy_from, +- write8: sp_write8, +- write16: sp_write16, +- write32: sp_write32, +- copy_to: sp_copy_to ++static struct map_info lasat_map = { ++ .name = "LASAT flash", ++ .buswidth = 4, ++ .set_vpp = lasat_set_vpp + }; + +-static struct mtd_partition partition_info[LASAT_MTD_LAST]; +-static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Config", "Filesystem"}; +- +-static int __init init_sp(void) ++static int __init init_lasat(void) + { + int i; +- int nparts = 0; +- /* this does not play well with the old flash code which +- * protects and uprotects the flash when necessary */ ++ /* since we use AMD chips and set_vpp is not implimented ++ * for these (yet) we still have to permanently enable flash write */ + printk(KERN_NOTICE "Unprotecting flash\n"); +- *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; ++ ENABLE_VPP((&lasat_map)); + +- sp_map.map_priv_1 = ioremap_nocache( +- lasat_flash_partition_start(LASAT_MTD_BOOTLOADER), +- lasat_board_info.li_flash_size); +- sp_map.size = lasat_board_info.li_flash_size; ++ lasat_map.phys = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); ++ lasat_map.virt = (unsigned long)ioremap_nocache( ++ lasat_map.phys, lasat_board_info.li_flash_size); ++ lasat_map.size = lasat_board_info.li_flash_size; + +- printk(KERN_NOTICE "sp flash device: %lx at %lx\n", +- sp_map.size, sp_map.map_priv_1); ++ simple_map_init(&lasat_map); + + for (i=0; i < LASAT_MTD_LAST; i++) + partition_info[i].name = lasat_mtd_partnames[i]; + +- mymtd = do_map_probe("cfi_probe", &sp_map); +- if (mymtd) { ++ lasat_mtd = do_map_probe("cfi_probe", &lasat_map); ++ ++ if (!lasat_mtd) ++ lasat_mtd = do_map_probe("jedec_probe", &lasat_map); ++ ++ if (lasat_mtd) { + u32 size, offset = 0; + +- mymtd->module = THIS_MODULE; ++ lasat_mtd->owner = THIS_MODULE; + + for (i=0; i < LASAT_MTD_LAST; i++) { + size = lasat_flash_partition_size(i); +- if (size != 0) { +- nparts++; + partition_info[i].size = size; + partition_info[i].offset = offset; + offset += size; + } +- } + +- add_mtd_partitions( mymtd, partition_info, nparts ); ++ add_mtd_partitions( lasat_mtd, partition_info, LASAT_MTD_LAST ); + return 0; + } + + return -ENXIO; + } + +-static void __exit cleanup_sp(void) ++static void __exit cleanup_lasat(void) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); ++ if (lasat_mtd) { ++ del_mtd_partitions(lasat_mtd); ++ map_destroy(lasat_mtd); + } +- if (sp_map.map_priv_1) { +- sp_map.map_priv_1 = 0; ++ if (lasat_map.virt) { ++ lasat_map.virt = 0; + } + } + +-module_init(init_sp); +-module_exit(cleanup_sp); ++module_init(init_lasat); ++module_exit(cleanup_lasat); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Brian Murphy <brian@murphy.dk>"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/lubbock-flash.c linux/drivers/mtd/maps/lubbock-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/lubbock-flash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/lubbock-flash.c 2004-11-19 10:25:11.930195080 +0100 +@@ -0,0 +1,151 @@ ++/* ++ * $Id: lubbock-flash.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ ++ * ++ * Map driver for the Lubbock developer platform. ++ * ++ * Author: Nicolas Pitre ++ * Copyright: (C) 2001 MontaVista Software Inc. ++ * ++ * 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/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <asm/io.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <asm/hardware.h> ++ ++ ++#define ROM_ADDR 0x00000000 ++#define FLASH_ADDR 0x04000000 ++ ++#define WINDOW_SIZE 64*1024*1024 ++ ++static struct map_info lubbock_maps[2] = { { ++ .size = WINDOW_SIZE, ++ .phys = 0x00000000, ++}, { ++ .size = WINDOW_SIZE, ++ .phys = 0x04000000, ++} }; ++ ++static struct mtd_partition lubbock_partitions[] = { ++ { ++ .name = "Bootloader", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ },{ ++ .name = "Kernel", ++ .size = 0x00100000, ++ .offset = 0x00040000, ++ },{ ++ .name = "Filesystem", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00140000 ++ } ++}; ++ ++static struct mtd_info *mymtds[2]; ++static struct mtd_partition *parsed_parts[2]; ++static int nr_parsed_parts[2]; ++ ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ ++static int __init init_lubbock(void) ++{ ++ int flashboot = (CONF_SWITCHES & 1); ++ int ret = 0, i; ++ ++ lubbock_maps[0].buswidth = lubbock_maps[1].buswidth = ++ (BOOT_DEF & 1) ? 2 : 4; ++ ++ /* Compensate for the nROMBT switch which swaps the flash banks */ ++ printk(KERN_NOTICE "Lubbock configured to boot from %s (bank %d)\n", ++ flashboot?"Flash":"ROM", flashboot); ++ ++ lubbock_maps[flashboot^1].name = "Lubbock Application Flash"; ++ lubbock_maps[flashboot].name = "Lubbock Boot ROM"; ++ ++ for (i = 0; i < 2; i++) { ++ lubbock_maps[i].virt = (unsigned long)__ioremap(lubbock_maps[i].phys, WINDOW_SIZE, 0); ++ if (!lubbock_maps[i].virt) { ++ printk(KERN_WARNING "Failed to ioremap %s\n", lubbock_maps[i].name); ++ if (!ret) ++ ret = -ENOMEM; ++ continue; ++ } ++ simple_map_init(&lubbock_maps[i]); ++ ++ printk(KERN_NOTICE "Probing %s at physical address 0x%08lx (%d-bit buswidth)\n", ++ lubbock_maps[i].name, lubbock_maps[i].phys, ++ lubbock_maps[i].buswidth * 8); ++ ++ mymtds[i] = do_map_probe("cfi_probe", &lubbock_maps[i]); ++ ++ if (!mymtds[i]) { ++ iounmap((void *)lubbock_maps[i].virt); ++ if (!ret) ++ ret = -EIO; ++ continue; ++ } ++ mymtds[i]->owner = THIS_MODULE; ++ ++ int ret = parse_mtd_partitions(mymtds[i], probes, ++ &parsed_parts[i], 0); ++ ++ if (ret > 0) ++ nr_parsed_parts[i] = ret; ++ } ++ ++ if (!mymtds[0] && !mymtds[1]) ++ return ret; ++ ++ for (i = 0; i < 2; i++) { ++ if (!mymtds[i]) { ++ printk(KERN_WARNING "%s is absent. Skipping\n", lubbock_maps[i].name); ++ } else if (nr_parsed_parts[i]) { ++ add_mtd_partitions(mymtds[i], parsed_parts[i], nr_parsed_parts[i]); ++ } else if (!i) { ++ printk("Using static partitions on %s\n", lubbock_maps[i].name); ++ add_mtd_partitions(mymtds[i], lubbock_partitions, ARRAY_SIZE(lubbock_partitions)); ++ } else { ++ printk("Registering %s as whole device\n", lubbock_maps[i].name); ++ add_mtd_device(mymtds[i]); ++ } ++ } ++ return 0; ++} ++ ++static void __exit cleanup_lubbock(void) ++{ ++ int i; ++ for (i = 0; i < 2; i++) { ++ if (!mymtds[i]) ++ continue; ++ ++ if (nr_parsed_parts[i] || !i) ++ del_mtd_partitions(mymtds[i]); ++ else ++ del_mtd_device(mymtds[i]); ++ ++ map_destroy(mymtds[i]); ++ iounmap((void *)lubbock_maps[i].virt); ++ ++ if (parsed_parts[i]) ++ kfree(parsed_parts[i]); ++ } ++} ++ ++module_init(init_lubbock); ++module_exit(cleanup_lubbock); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); ++MODULE_DESCRIPTION("MTD map driver for Intel Lubbock"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/map_funcs.c linux/drivers/mtd/maps/map_funcs.c +--- linux-mips-2.4.27/drivers/mtd/maps/map_funcs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/map_funcs.c 2004-11-19 10:25:11.931194928 +0100 +@@ -0,0 +1,96 @@ ++/* ++ * $Id: map_funcs.c,v 1.3 2003/11/14 19:50:04 thayne Exp $ ++ * ++ * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS ++ * is enabled. ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/config.h> ++#include <linux/types.h> ++#include <linux/string.h> ++#include <asm/io.h> ++ ++#include <linux/mtd/map.h> ++ ++static u8 simple_map_read8(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readb(map->virt + ofs); ++} ++ ++static u16 simple_map_read16(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readw(map->virt + ofs); ++} ++ ++static u32 simple_map_read32(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readl(map->virt + ofs); ++} ++ ++static u64 simple_map_read64(struct map_info *map, unsigned long ofs) ++{ ++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ ++ BUG(); ++ return 0; ++#else ++ return __raw_readll(map->virt + ofs); ++#endif ++} ++ ++static void simple_map_write8(struct map_info *map, u8 datum, unsigned long ofs) ++{ ++ __raw_writeb(datum, map->virt + ofs); ++ mb(); ++} ++ ++static void simple_map_write16(struct map_info *map, u16 datum, unsigned long ofs) ++{ ++ __raw_writew(datum, map->virt + ofs); ++ mb(); ++} ++ ++static void simple_map_write32(struct map_info *map, u32 datum, unsigned long ofs) ++{ ++ __raw_writel(datum, map->virt + ofs); ++ mb(); ++} ++ ++static void simple_map_write64(struct map_info *map, u64 datum, unsigned long ofs) ++{ ++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ ++ BUG(); ++#else ++ __raw_writell(datum, map->virt + ofs); ++ mb(); ++#endif /* CFI_B8 */ ++} ++ ++static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio(to, map->virt + from, len); ++} ++ ++static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio(map->virt + to, from, len); ++} ++ ++void simple_map_init(struct map_info *map) ++{ ++ map->read8 = simple_map_read8; ++ map->read16 = simple_map_read16; ++ map->read32 = simple_map_read32; ++ map->read64 = simple_map_read64; ++ map->write8 = simple_map_write8; ++ map->write16 = simple_map_write16; ++ map->write32 = simple_map_write32; ++ map->write64 = simple_map_write64; ++ map->copy_from = simple_map_copy_from; ++ map->copy_to = simple_map_copy_to; ++} ++ ++EXPORT_SYMBOL(simple_map_init); ++ ++MODULE_LICENSE("GPL"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/mbx860.c linux/drivers/mtd/maps/mbx860.c +--- linux-mips-2.4.27/drivers/mtd/maps/mbx860.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/mbx860.c 2004-11-19 10:25:11.933194624 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: mbx860.c,v 1.1 2001/11/18 19:43:09 dwmw2 Exp $ ++ * $Id: mbx860.c,v 1.5 2003/05/21 12:45:19 dwmw2 Exp $ + * + * Handle mapping of the flash on MBX860 boards + * +@@ -15,6 +15,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -36,91 +37,46 @@ + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ +- { name: "MBX flash BOOT partition", +- offset: 0, +- size: BOOT_PARTITION_SIZE_KiB*1024 }, +- { name: "MBX flash DATA partition", +- offset: BOOT_PARTITION_SIZE_KiB*1024, +- size: (KERNEL_PARTITION_SIZE_KiB)*1024 }, +- { name: "MBX flash APPLICATION partition", +- offset: (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 } ++ { .name = "MBX flash BOOT partition", ++ .offset = 0, ++ .size = BOOT_PARTITION_SIZE_KiB*1024 }, ++ { .name = "MBX flash DATA partition", ++ .offset = BOOT_PARTITION_SIZE_KiB*1024, ++ .size = (KERNEL_PARTITION_SIZE_KiB)*1024 }, ++ { .name = "MBX flash APPLICATION partition", ++ .offset = (BOOT_PARTITION_SIZE_KiB+KERNEL_PARTITION_SIZE_KiB)*1024 } + }; + + + static struct mtd_info *mymtd; + +-__u8 mbx_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-__u16 mbx_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-__u32 mbx_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-void mbx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void mbx_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-void mbx_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-void mbx_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-void mbx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- + struct map_info mbx_map = { +- name: "MBX flash", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: mbx_read8, +- read16: mbx_read16, +- read32: mbx_read32, +- copy_from: mbx_copy_from, +- write8: mbx_write8, +- write16: mbx_write16, +- write32: mbx_write32, +- copy_to: mbx_copy_to ++ .name = "MBX flash", ++ .size = WINDOW_SIZE, ++ .phys = WINDOW_ADDR, ++ .buswidth = 4, + }; + + int __init init_mbx(void) + { +- printk(KERN_NOTICE "Motorola MBX flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); +- mbx_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); ++ printk(KERN_NOTICE "Motorola MBX flash device: 0x%x at 0x%x\n", WINDOW_SIZE*4, WINDOW_ADDR); ++ mbx_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + +- if (!mbx_map.map_priv_1) { ++ if (!mbx_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&mbx_map); ++ + mymtd = do_map_probe("jedec_probe", &mbx_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_device(mymtd); + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + return 0; + } + +- iounmap((void *)mbx_map.map_priv_1); ++ iounmap((void *)mbx_map.virt); + return -ENXIO; + } + +@@ -130,9 +86,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (mbx_map.map_priv_1) { +- iounmap((void *)mbx_map.map_priv_1); +- mbx_map.map_priv_1 = 0; ++ if (mbx_map.virt) { ++ iounmap((void *)mbx_map.virt); ++ mbx_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/mpc1211.c linux/drivers/mtd/maps/mpc1211.c +--- linux-mips-2.4.27/drivers/mtd/maps/mpc1211.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/mpc1211.c 2004-11-19 10:25:11.934194472 +0100 +@@ -0,0 +1,79 @@ ++/* ++ * Flash on MPC-1211 ++ * ++ * (C) 2002 Interface, Saito.K & Jeanne ++ * ++ * GPL'd ++ */ ++ ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <asm/io.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++#include <linux/config.h> ++ ++static struct mtd_info *flash_mtd; ++static struct mtd_partition *parsed_parts; ++ ++struct map_info mpc1211_flash_map = { ++ .name = "MPC-1211 FLASH", ++ .size = 0x80000, ++ .buswidth = 1, ++}; ++ ++static struct mtd_partition mpc1211_partitions[] = { ++ { ++ .name = "IPL & ETH-BOOT", ++ .offset = 0x00000000, ++ .size = 0x10000, ++ }, ++ { ++ .name = "Flash FS", ++ .offset = 0x00010000, ++ .size = MTDPART_SIZ_FULL, ++ } ++}; ++ ++static int __init init_mpc1211_maps(void) ++{ ++ int nr_parts; ++ ++ mpc1211_flash_map.phys = 0; ++ mpc1211_flash_map.virt = P2SEGADDR(0); ++ ++ simple_map_init(&mpc1211_flash_map); ++ ++ printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n"); ++ flash_mtd = do_map_probe("jedec_probe", &mpc1211_flash_map); ++ if (!flash_mtd) { ++ printk(KERN_NOTICE "Flash chips not detected at either possible location.\n"); ++ return -ENXIO; ++ } ++ printk(KERN_NOTICE "MPC-1211: Flash at 0x%08lx\n", mpc1211_flash_map.virt & 0x1fffffff); ++ flash_mtd->module = THIS_MODULE; ++ ++ parsed_parts = mpc1211_partitions; ++ nr_parts = ARRAY_SIZE(mpc1211_partitions); ++ ++ add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); ++ return 0; ++} ++ ++static void __exit cleanup_mpc1211_maps(void) ++{ ++ if (parsed_parts) ++ del_mtd_partitions(flash_mtd); ++ else ++ del_mtd_device(flash_mtd); ++ map_destroy(flash_mtd); ++} ++ ++module_init(init_mpc1211_maps); ++module_exit(cleanup_mpc1211_maps); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Saito.K & Jeanne <ksaito@interface.co.jp>"); ++MODULE_DESCRIPTION("MTD map driver for MPC-1211 boards. Interface"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/netsc520.c linux/drivers/mtd/maps/netsc520.c +--- linux-mips-2.4.27/drivers/mtd/maps/netsc520.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/netsc520.c 2004-11-19 10:25:11.935194320 +0100 +@@ -3,7 +3,7 @@ + * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) + * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH + * +- * $Id: netsc520.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: netsc520.c,v 1.9 2003/05/21 12:45:19 dwmw2 Exp $ + * + * 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 +@@ -27,6 +27,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -50,95 +51,41 @@ + ** recoverable afterwards. + */ + +-static __u8 netsc520_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 netsc520_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 netsc520_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void netsc520_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void netsc520_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void netsc520_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void netsc520_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void netsc520_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- + /* partition_info gives details on the logical partitions that the split the + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ + { +- name: "NetSc520 boot kernel", +- offset: 0, +- size: 0xc0000 ++ .name = "NetSc520 boot kernel", ++ .offset = 0, ++ .size = 0xc0000 + }, + { +- name: "NetSc520 Low BIOS", +- offset: 0xc0000, +- size: 0x40000 ++ .name = "NetSc520 Low BIOS", ++ .offset = 0xc0000, ++ .size = 0x40000 + }, + { +- name: "NetSc520 file system", +- offset: 0x100000, +- size: 0xe80000 ++ .name = "NetSc520 file system", ++ .offset = 0x100000, ++ .size = 0xe80000 + }, + { +- name: "NetSc520 High BIOS", +- offset: 0xf80000, +- size: 0x80000 ++ .name = "NetSc520 High BIOS", ++ .offset = 0xf80000, ++ .size = 0x80000 + }, + }; + #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + +-/* +- * If no idea what is going on here. This is taken from the FlashFX stuff. +- */ +-#define ROMCS 1 +- +- + #define WINDOW_SIZE 0x00100000 + #define WINDOW_ADDR 0x00200000 + + static struct map_info netsc520_map = { +- name: "netsc520 Flash Bank", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: netsc520_read8, +- read16: netsc520_read16, +- read32: netsc520_read32, +- copy_from: netsc520_copy_from, +- write8: netsc520_write8, +- write16: netsc520_write16, +- write32: netsc520_write32, +- copy_to: netsc520_copy_to, +- map_priv_2: WINDOW_ADDR ++ .name = "netsc520 Flash Bank", ++ .size = WINDOW_SIZE, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR, + }; + + #define NUM_FLASH_BANKS (sizeof(netsc520_map)/sizeof(struct map_info)) +@@ -147,13 +94,16 @@ + + static int __init init_netsc520(void) + { +- printk(KERN_NOTICE "NetSc520 flash device: %lx at %lx\n", netsc520_map.size, netsc520_map.map_priv_2); +- netsc520_map.map_priv_1 = (unsigned long)ioremap_nocache(netsc520_map.map_priv_2, netsc520_map.size); ++ printk(KERN_NOTICE "NetSc520 flash device: 0x%lx at 0x%lx\n", netsc520_map.size, netsc520_map.phys); ++ netsc520_map.virt = (unsigned long)ioremap_nocache(netsc520_map.phys, netsc520_map.size); + +- if (!netsc520_map.map_priv_1) { ++ if (!netsc520_map.virt) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } ++ ++ simple_map_init(&netsc520_map); ++ + mymtd = do_map_probe("cfi_probe", &netsc520_map); + if(!mymtd) + mymtd = do_map_probe("map_ram", &netsc520_map); +@@ -161,11 +111,11 @@ + mymtd = do_map_probe("map_rom", &netsc520_map); + + if (!mymtd) { +- iounmap((void *)netsc520_map.map_priv_1); ++ iounmap((void *)netsc520_map.virt); + return -ENXIO; + } + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_partitions( mymtd, partition_info, NUM_PARTITIONS ); + return 0; + } +@@ -176,9 +126,9 @@ + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } +- if (netsc520_map.map_priv_1) { +- iounmap((void *)netsc520_map.map_priv_1); +- netsc520_map.map_priv_1 = 0; ++ if (netsc520_map.virt) { ++ iounmap((void *)netsc520_map.virt); ++ netsc520_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/nettel.c linux/drivers/mtd/maps/nettel.c +--- linux-mips-2.4.27/drivers/mtd/maps/nettel.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/nettel.c 2004-11-19 10:25:11.937194016 +0100 +@@ -6,7 +6,7 @@ + * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) + * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) + * +- * $Id: nettel.c,v 1.1 2002/08/08 06:30:13 gerg Exp $ ++ * $Id: nettel.c,v 1.4 2003/05/20 20:59:30 dwmw2 Exp $ + */ + + /****************************************************************************/ +@@ -59,128 +59,72 @@ + + /****************************************************************************/ + +-static __u8 nettel_read8(struct map_info *map, unsigned long ofs) +-{ +- return(readb(map->map_priv_1 + ofs)); +-} +- +-static __u16 nettel_read16(struct map_info *map, unsigned long ofs) +-{ +- return(readw(map->map_priv_1 + ofs)); +-} +- +-static __u32 nettel_read32(struct map_info *map, unsigned long ofs) +-{ +- return(readl(map->map_priv_1 + ofs)); +-} +- +-static void nettel_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void nettel_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void nettel_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void nettel_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void nettel_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- + /****************************************************************************/ + + #ifdef CONFIG_MTD_CFI_INTELEXT + static struct map_info nettel_intel_map = { +- name: "SnapGear Intel", +- size: 0, +- buswidth: INTEL_BUSWIDTH, +- read8: nettel_read8, +- read16: nettel_read16, +- read32: nettel_read32, +- copy_from: nettel_copy_from, +- write8: nettel_write8, +- write16: nettel_write16, +- write32: nettel_write32, +- copy_to: nettel_copy_to ++ .name = "SnapGear Intel", ++ .size = 0, ++ .buswidth = INTEL_BUSWIDTH, + }; + + static struct mtd_partition nettel_intel_partitions[] = { + { +- name: "SnapGear kernel", +- offset: 0, +- size: 0x000e0000 ++ .name = "SnapGear kernel", ++ .offset = 0, ++ .size = 0x000e0000 + }, + { +- name: "SnapGear filesystem", +- offset: 0x00100000, ++ .name = "SnapGear filesystem", ++ .offset = 0x00100000, + }, + { +- name: "SnapGear config", +- offset: 0x000e0000, +- size: 0x00020000 ++ .name = "SnapGear config", ++ .offset = 0x000e0000, ++ .size = 0x00020000 + }, + { +- name: "SnapGear Intel", +- offset: 0 ++ .name = "SnapGear Intel", ++ .offset = 0 + }, + { +- name: "SnapGear BIOS Config", +- offset: 0x007e0000, +- size: 0x00020000 ++ .name = "SnapGear BIOS Config", ++ .offset = 0x007e0000, ++ .size = 0x00020000 + }, + { +- name: "SnapGear BIOS", +- offset: 0x007e0000, +- size: 0x00020000 ++ .name = "SnapGear BIOS", ++ .offset = 0x007e0000, ++ .size = 0x00020000 + }, + }; + #endif + + static struct map_info nettel_amd_map = { +- name: "SnapGear AMD", +- size: AMD_WINDOW_MAXSIZE, +- buswidth: AMD_BUSWIDTH, +- read8: nettel_read8, +- read16: nettel_read16, +- read32: nettel_read32, +- copy_from: nettel_copy_from, +- write8: nettel_write8, +- write16: nettel_write16, +- write32: nettel_write32, +- copy_to: nettel_copy_to ++ .name = "SnapGear AMD", ++ .size = AMD_WINDOW_MAXSIZE, ++ .buswidth = AMD_BUSWIDTH, + }; + + static struct mtd_partition nettel_amd_partitions[] = { + { +- name: "SnapGear BIOS config", +- offset: 0x000e0000, +- size: 0x00010000 ++ .name = "SnapGear BIOS config", ++ .offset = 0x000e0000, ++ .size = 0x00010000 + }, + { +- name: "SnapGear BIOS", +- offset: 0x000f0000, +- size: 0x00010000 ++ .name = "SnapGear BIOS", ++ .offset = 0x000f0000, ++ .size = 0x00010000 + }, + { +- name: "SnapGear AMD", +- offset: 0 ++ .name = "SnapGear AMD", ++ .offset = 0 + }, + { +- name: "SnapGear high BIOS", +- offset: 0x001f0000, +- size: 0x00010000 ++ .name = "SnapGear high BIOS", ++ .offset = 0x001f0000, ++ .size = 0x00010000 + } + }; + +@@ -328,18 +272,20 @@ + *amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize); + __asm__ ("wbinvd"); + +- nettel_amd_map.map_priv_1 = (unsigned long) ++ nettel_amd_map.phys = amdaddr; ++ nettel_amd_map.virt = (unsigned long) + ioremap_nocache(amdaddr, maxsize); +- if (!nettel_amd_map.map_priv_1) { ++ if (!nettel_amd_map.virt) { + printk("SNAPGEAR: failed to ioremap() BOOTCS\n"); + return(-EIO); + } ++ simple_map_init(&nettel_amd_map); + + if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) { + printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n", + amd_mtd->size>>10); + +- amd_mtd->module = THIS_MODULE; ++ amd_mtd->owner = THIS_MODULE; + + /* The high BIOS partition is only present for 2MB units */ + num_amd_partitions = NUM_AMD_PARTITIONS; +@@ -387,8 +333,8 @@ + + /* Destroy useless AMD MTD mapping */ + amd_mtd = NULL; +- iounmap((void *) nettel_amd_map.map_priv_1); +- nettel_amd_map.map_priv_1 = (unsigned long) NULL; ++ iounmap((void *) nettel_amd_map.virt); ++ nettel_amd_map.virt = (unsigned long) NULL; + #else + /* Only AMD flash supported */ + return(-ENXIO); +@@ -411,16 +357,18 @@ + + /* Probe for the the size of the first Intel flash */ + nettel_intel_map.size = maxsize; +- nettel_intel_map.map_priv_1 = (unsigned long) ++ nettel_intel_map.phys = intel0addr; ++ nettel_intel_map.virt = (unsigned long) + ioremap_nocache(intel0addr, maxsize); +- if (!nettel_intel_map.map_priv_1) { ++ if (!nettel_intel_map.virt) { + printk("SNAPGEAR: failed to ioremap() ROMCS1\n"); + return(-EIO); + } ++ simple_map_init(&nettel_intel_map); + + intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); + if (! intel_mtd) { +- iounmap((void *) nettel_intel_map.map_priv_1); ++ iounmap((void *) nettel_intel_map.virt); + return(-ENXIO); + } + +@@ -441,19 +389,19 @@ + /* Delete the old map and probe again to do both chips */ + map_destroy(intel_mtd); + intel_mtd = NULL; +- iounmap((void *) nettel_intel_map.map_priv_1); ++ iounmap((void *) nettel_intel_map.virt); + + nettel_intel_map.size = maxsize; +- nettel_intel_map.map_priv_1 = (unsigned long) ++ nettel_intel_map.virt = (unsigned long) + ioremap_nocache(intel0addr, maxsize); +- if (!nettel_intel_map.map_priv_1) { ++ if (!nettel_intel_map.virt) { + printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n"); + return(-EIO); + } + + intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map); + if (! intel_mtd) { +- iounmap((void *) nettel_intel_map.map_priv_1); ++ iounmap((void *) nettel_intel_map.virt); + return(-ENXIO); + } + +@@ -468,7 +416,7 @@ + printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n", + (intel_mtd->size >> 10)); + +- intel_mtd->module = THIS_MODULE; ++ intel_mtd->owner = THIS_MODULE; + + #ifndef CONFIG_BLK_DEV_INITRD + ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1); +@@ -523,18 +471,18 @@ + del_mtd_partitions(amd_mtd); + map_destroy(amd_mtd); + } +- if (nettel_amd_map.map_priv_1) { +- iounmap((void *)nettel_amd_map.map_priv_1); +- nettel_amd_map.map_priv_1 = 0; ++ if (nettel_amd_map.virt) { ++ iounmap((void *)nettel_amd_map.virt); ++ nettel_amd_map.virt = 0; + } + #ifdef CONFIG_MTD_CFI_INTELEXT + if (intel_mtd) { + del_mtd_partitions(intel_mtd); + map_destroy(intel_mtd); + } +- if (nettel_intel_map.map_priv_1) { +- iounmap((void *)nettel_intel_map.map_priv_1); +- nettel_intel_map.map_priv_1 = 0; ++ if (nettel_intel_map.virt) { ++ iounmap((void *)nettel_intel_map.virt); ++ nettel_intel_map.virt = 0; + } + #endif + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/ocelot.c linux/drivers/mtd/maps/ocelot.c +--- linux-mips-2.4.27/drivers/mtd/maps/ocelot.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/ocelot.c 2004-11-19 10:25:11.938193864 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: ocelot.c,v 1.6 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: ocelot.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $ + * + * Flash on Momenco Ocelot + */ +@@ -7,6 +7,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -20,47 +21,23 @@ + #define NVRAM_WINDOW_SIZE 0x00007FF0 + #define NVRAM_BUSWIDTH 1 + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +- + static unsigned int cacheflush = 0; + + static struct mtd_info *flash_mtd; + static struct mtd_info *nvram_mtd; + +-__u8 ocelot_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-void ocelot_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- cacheflush = 1; +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void ocelot_copy_from_cache(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- if (cacheflush) { +- dma_cache_inv(map->map_priv_2, map->size); +- cacheflush = 0; +- } +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void ocelot_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) + { +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} ++ struct map_info *map = (struct map_info *)mtd->priv; ++ size_t done = 0; + +-void ocelot_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ + /* If we use memcpy, it does word-wide writes. Even though we told the + GT64120A that it's an 8-bit wide region, word-wide writes don't work. + We end up just writing the first byte of the four to all four bytes. + So we have this loop instead */ ++ *retlen = len; + while(len) { +- __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); ++ __raw_writeb(*(unsigned char *) from, map->virt + to); + from++; + to++; + len--; +@@ -70,24 +47,21 @@ + static struct mtd_partition *parsed_parts; + + struct map_info ocelot_flash_map = { +- name: "Ocelot boot flash", +- size: FLASH_WINDOW_SIZE, +- buswidth: FLASH_BUSWIDTH, +- read8: ocelot_read8, +- copy_from: ocelot_copy_from_cache, +- write8: ocelot_write8, ++ .name = "Ocelot boot flash", ++ .size = FLASH_WINDOW_SIZE, ++ .buswidth = FLASH_BUSWIDTH, ++ .phys = FLASH_WINDOW_ADDR, + }; + + struct map_info ocelot_nvram_map = { +- name: "Ocelot NVRAM", +- size: NVRAM_WINDOW_SIZE, +- buswidth: NVRAM_BUSWIDTH, +- read8: ocelot_read8, +- copy_from: ocelot_copy_from, +- write8: ocelot_write8, +- copy_to: ocelot_copy_to ++ .name = "Ocelot NVRAM", ++ .size = NVRAM_WINDOW_SIZE, ++ .buswidth = NVRAM_BUSWIDTH, ++ .phys = NVRAM_WINDOW_ADDR, + }; + ++static const char *probes[] = { "RedBoot", NULL }; ++ + static int __init init_ocelot_maps(void) + { + void *pld; +@@ -107,12 +81,13 @@ + iounmap(pld); + + /* Now ioremap the NVRAM space */ +- ocelot_nvram_map.map_priv_1 = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); +- if (!ocelot_nvram_map.map_priv_1) { ++ ocelot_nvram_map.virt = (unsigned long)ioremap_nocache(NVRAM_WINDOW_ADDR, NVRAM_WINDOW_SIZE); ++ if (!ocelot_nvram_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Ocelot NVRAM space\n"); + return -EIO; + } +- // ocelot_nvram_map.map_priv_2 = ocelot_nvram_map.map_priv_1; ++ ++ simple_map_init(&ocelot_nvram_map); + + /* And do the RAM probe on it to get an MTD device */ + nvram_mtd = do_map_probe("map_ram", &ocelot_nvram_map); +@@ -120,22 +95,21 @@ + printk("NVRAM probe failed\n"); + goto fail_1; + } +- nvram_mtd->module = THIS_MODULE; ++ nvram_mtd->owner = THIS_MODULE; + nvram_mtd->erasesize = 16; ++ /* Override the write() method */ ++ nvram_mtd->write = ocelot_ram_write; + + /* Now map the flash space */ +- ocelot_flash_map.map_priv_1 = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); +- if (!ocelot_flash_map.map_priv_1) { ++ ocelot_flash_map.virt = (unsigned long)ioremap_nocache(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE); ++ if (!ocelot_flash_map.virt) { + printk(KERN_NOTICE "Failed to ioremap Ocelot flash space\n"); + goto fail_2; + } + /* Now the cached version */ +- ocelot_flash_map.map_priv_2 = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0); ++ ocelot_flash_map.cached = (unsigned long)__ioremap(FLASH_WINDOW_ADDR, FLASH_WINDOW_SIZE, 0); + +- if (!ocelot_flash_map.map_priv_2) { +- /* Doesn't matter if it failed. Just use the uncached version */ +- ocelot_flash_map.map_priv_2 = ocelot_flash_map.map_priv_1; +- } ++ simple_map_init(&ocelot_flash_map); + + /* Only probe for flash if the write jumper is present */ + if (brd_status & 0x40) { +@@ -155,10 +129,10 @@ + + add_mtd_device(nvram_mtd); + +- flash_mtd->module = THIS_MODULE; +- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); ++ flash_mtd->owner = THIS_MODULE; ++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); + +- if (nr_parts) ++ if (nr_parts > 0) + add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); + else + add_mtd_device(flash_mtd); +@@ -166,14 +140,13 @@ + return 0; + + fail3: +- iounmap((void *)ocelot_flash_map.map_priv_1); +- if (ocelot_flash_map.map_priv_2 && +- ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1) +- iounmap((void *)ocelot_flash_map.map_priv_2); ++ iounmap((void *)ocelot_flash_map.virt); ++ if (ocelot_flash_map.cached) ++ iounmap((void *)ocelot_flash_map.cached); + fail_2: + map_destroy(nvram_mtd); + fail_1: +- iounmap((void *)ocelot_nvram_map.map_priv_1); ++ iounmap((void *)ocelot_nvram_map.virt); + + return -ENXIO; + } +@@ -182,16 +155,16 @@ + { + del_mtd_device(nvram_mtd); + map_destroy(nvram_mtd); +- iounmap((void *)ocelot_nvram_map.map_priv_1); ++ iounmap((void *)ocelot_nvram_map.virt); + + if (parsed_parts) + del_mtd_partitions(flash_mtd); + else + del_mtd_device(flash_mtd); + map_destroy(flash_mtd); +- iounmap((void *)ocelot_flash_map.map_priv_1); +- if (ocelot_flash_map.map_priv_2 != ocelot_flash_map.map_priv_1) +- iounmap((void *)ocelot_flash_map.map_priv_2); ++ iounmap((void *)ocelot_flash_map.virt); ++ if (ocelot_flash_map.cached) ++ iounmap((void *)ocelot_flash_map.cached); + } + + module_init(init_ocelot_maps); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/octagon-5066.c linux/drivers/mtd/maps/octagon-5066.c +--- linux-mips-2.4.27/drivers/mtd/maps/octagon-5066.c 2003-08-13 19:19:18.000000000 +0200 ++++ linux/drivers/mtd/maps/octagon-5066.c 2004-11-19 10:25:11.940193560 +0100 +@@ -1,4 +1,4 @@ +-// $Id: octagon-5066.c,v 1.20 2003/01/07 17:21:55 dwmw2 Exp $ ++// $Id: octagon-5066.c,v 1.24 2003/05/21 15:15:07 dwmw2 Exp $ + /* ###################################################################### + + Octagon 5066 MTD Driver. +@@ -31,6 +31,7 @@ + #include <asm/io.h> + + #include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> + + #define WINDOW_START 0xe8000 + #define WINDOW_LENGTH 0x8000 +@@ -151,32 +152,34 @@ + + static struct map_info oct5066_map[2] = { + { +- name: "Octagon 5066 Socket", +- size: 512 * 1024, +- buswidth: 1, +- read8: oct5066_read8, +- read16: oct5066_read16, +- read32: oct5066_read32, +- copy_from: oct5066_copy_from, +- write8: oct5066_write8, +- write16: oct5066_write16, +- write32: oct5066_write32, +- copy_to: oct5066_copy_to, +- map_priv_1: 1<<6 ++ .name = "Octagon 5066 Socket", ++ .phys = NO_XIP, ++ .size = 512 * 1024, ++ .buswidth = 1, ++ .read8 = oct5066_read8, ++ .read16 = oct5066_read16, ++ .read32 = oct5066_read32, ++ .copy_from = oct5066_copy_from, ++ .write8 = oct5066_write8, ++ .write16 = oct5066_write16, ++ .write32 = oct5066_write32, ++ .copy_to = oct5066_copy_to, ++ .map_priv_1 = 1<<6 + }, + { +- name: "Octagon 5066 Internal Flash", +- size: 2 * 1024 * 1024, +- buswidth: 1, +- read8: oct5066_read8, +- read16: oct5066_read16, +- read32: oct5066_read32, +- copy_from: oct5066_copy_from, +- write8: oct5066_write8, +- write16: oct5066_write16, +- write32: oct5066_write32, +- copy_to: oct5066_copy_to, +- map_priv_1: 2<<6 ++ .name = "Octagon 5066 Internal Flash", ++ .phys = NO_XIP, ++ .size = 2 * 1024 * 1024, ++ .buswidth = 1, ++ .read8 = oct5066_read8, ++ .read16 = oct5066_read16, ++ .read32 = oct5066_read32, ++ .copy_from = oct5066_copy_from, ++ .write8 = oct5066_write8, ++ .write16 = oct5066_write16, ++ .write32 = oct5066_write32, ++ .copy_to = oct5066_copy_to, ++ .map_priv_1 = 2<<6 + } + }; + +@@ -244,6 +247,7 @@ + } + if (OctProbe() != 0) { + printk(KERN_NOTICE "5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n"); ++ iounmap((void *)iomapadr); + ret = -EAGAIN; + goto out_unmap; + } +@@ -261,7 +265,7 @@ + if (!oct5066_mtd[i]) + oct5066_mtd[i] = do_map_probe("map_rom", &oct5066_map[i]); + if (oct5066_mtd[i]) { +- oct5066_mtd[i]->module = THIS_MODULE; ++ oct5066_mtd[i]->owner = THIS_MODULE; + add_mtd_device(oct5066_mtd[i]); + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/omap-toto-flash.c linux/drivers/mtd/maps/omap-toto-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/omap-toto-flash.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/omap-toto-flash.c 2004-11-19 10:25:11.941193408 +0100 +@@ -0,0 +1,137 @@ ++/* ++ * NOR Flash memory access on TI Toto board ++ * ++ * jzhang@ti.com (C) 2003 Texas Instruments. ++ * ++ * (C) 2002 MontVista Software, Inc. ++ * ++ * $Id: omap-toto-flash.c,v 1.1 2003/10/21 13:55:21 dwmw2 Exp $ ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++ ++#include <linux/errno.h> ++#include <linux/init.h> ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++ ++#include <asm/hardware.h> ++#include <asm/io.h> ++ ++ ++#ifndef CONFIG_ARCH_OMAP ++#error This is for OMAP architecture only ++#endif ++ ++//these lines need be moved to a hardware header file ++#define OMAP_TOTO_FLASH_BASE 0xd8000000 ++#define OMAP_TOTO_FLASH_SIZE 0x80000 ++ ++static struct map_info omap_toto_map_flash = { ++ .name = "OMAP Toto flash", ++ .buswidth = 2, ++ .virt = OMAP_TOTO_FLASH_BASE, ++}; ++ ++ ++static struct mtd_partition toto_flash_partitions[] = { ++ { ++ .name = "BootLoader", ++ .size = 0x00040000, /* hopefully u-boot will stay 128k + 128*/ ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "ReservedSpace", ++ .size = 0x00030000, ++ .offset = MTDPART_OFS_APPEND, ++ //mask_flags: MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "EnvArea", /* bottom 64KiB for env vars */ ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ } ++}; ++ ++static struct mtd_partition *parsed_parts; ++ ++static struct mtd_info *flash_mtd; ++ ++static int __init init_flash (void) ++{ ++ ++ struct mtd_partition *parts; ++ int nb_parts = 0; ++ int parsed_nr_parts = 0; ++ const char *part_type; ++ ++ /* ++ * Static partition definition selection ++ */ ++ part_type = "static"; ++ ++ parts = toto_flash_partitions; ++ nb_parts = ARRAY_SIZE(toto_flash_partitions); ++ omap_toto_map_flash.size = OMAP_TOTO_FLASH_SIZE; ++ omap_toto_map_flash.phys = virt_to_phys(OMAP_TOTO_FLASH_BASE); ++ ++ simple_map_init(&omap_toto_map_flash); ++ /* ++ * Now let's probe for the actual flash. Do it here since ++ * specific machine settings might have been set above. ++ */ ++ printk(KERN_NOTICE "OMAP toto flash: probing %d-bit flash bus\n", ++ omap_toto_map_flash.buswidth*8); ++ flash_mtd = do_map_probe("jedec_probe", &omap_toto_map_flash); ++ if (!flash_mtd) ++ return -ENXIO; ++ ++ if (parsed_nr_parts > 0) { ++ parts = parsed_parts; ++ nb_parts = parsed_nr_parts; ++ } ++ ++ if (nb_parts == 0) { ++ printk(KERN_NOTICE "OMAP toto flash: no partition info available," ++ "registering whole flash at once\n"); ++ if (add_mtd_device(flash_mtd)){ ++ return -ENXIO; ++ } ++ } else { ++ printk(KERN_NOTICE "Using %s partition definition\n", ++ part_type); ++ return add_mtd_partitions(flash_mtd, parts, nb_parts); ++ } ++ return 0; ++} ++ ++int __init omap_toto_mtd_init(void) ++{ ++ int status; ++ ++ if (status = init_flash()) { ++ printk(KERN_ERR "OMAP Toto Flash: unable to init map for toto flash\n"); ++ } ++ return status; ++} ++ ++static void __exit omap_toto_mtd_cleanup(void) ++{ ++ if (flash_mtd) { ++ del_mtd_partitions(flash_mtd); ++ map_destroy(flash_mtd); ++ if (parsed_parts) ++ kfree(parsed_parts); ++ } ++} ++ ++module_init(omap_toto_mtd_init); ++module_exit(omap_toto_mtd_cleanup); ++ ++MODULE_AUTHOR("Jian Zhang"); ++MODULE_DESCRIPTION("OMAP Toto board map driver"); ++MODULE_LICENSE("GPL"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pb1xxx-flash.c linux/drivers/mtd/maps/pb1xxx-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/pb1xxx-flash.c 2003-05-19 08:27:22.000000000 +0200 ++++ linux/drivers/mtd/maps/pb1xxx-flash.c 2004-11-19 10:25:11.943193104 +0100 +@@ -3,12 +3,13 @@ + * + * (C) 2001 Pete Popov <ppopov@mvista.com> + * +- * $Id: pb1xxx-flash.c,v 1.4 2002/09/13 13:51:54 dwmw2 Exp $ ++ * $Id: pb1xxx-flash.c,v 1.10 2003/08/28 06:50:24 ppopov Exp $ + */ + + #include <linux/config.h> + #include <linux/module.h> + #include <linux/types.h> ++#include <linux/init.h> + #include <linux/kernel.h> + + #include <linux/mtd/mtd.h> +@@ -25,210 +26,110 @@ + #endif + + #ifdef CONFIG_MIPS_PB1000 ++ + #define WINDOW_ADDR 0x1F800000 + #define WINDOW_SIZE 0x800000 +-#endif +- +-__u8 physmap_read8(struct map_info *map, unsigned long ofs) +-{ +- __u8 ret; +- ret = __raw_readb(map->map_priv_1 + ofs); +- DBG("read8 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u16 physmap_read16(struct map_info *map, unsigned long ofs) +-{ +- __u16 ret; +- ret = __raw_readw(map->map_priv_1 + ofs); +- DBG("read16 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-__u32 physmap_read32(struct map_info *map, unsigned long ofs) +-{ +- __u32 ret; +- ret = __raw_readl(map->map_priv_1 + ofs); +- DBG("read32 from %x, %x\n", (unsigned)(map->map_priv_1 + ofs), ret); +- return ret; +-} +- +-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- DBG("physmap_copy from %x to %x\n", (unsigned)from, (unsigned)to); +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- DBG("write8 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- DBG("write16 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- DBG("write32 at %x, %x\n", (unsigned)(map->map_priv_1 + adr), d); +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- DBG("physmap_copy_to %x from %x\n", (unsigned)to, (unsigned)from); +- memcpy_toio(map->map_priv_1 + to, from, len); +-} +- +- +- +-static struct map_info pb1xxx_map = { +- name: "Pb1xxx flash", +- read8: physmap_read8, +- read16: physmap_read16, +- read32: physmap_read32, +- copy_from: physmap_copy_from, +- write8: physmap_write8, +- write16: physmap_write16, +- write32: physmap_write32, +- copy_to: physmap_copy_to, +-}; + +- +-#ifdef CONFIG_MIPS_PB1000 +- +-static unsigned long flash_size = 0x00800000; +-static unsigned char flash_buswidth = 4; + static struct mtd_partition pb1xxx_partitions[] = { + { +- name: "yamon env", +- size: 0x00020000, +- offset: 0, +- mask_flags: MTD_WRITEABLE +- },{ +- name: "User FS", +- size: 0x003e0000, +- offset: 0x20000, +- },{ +- name: "boot code", +- size: 0x100000, +- offset: 0x400000, +- mask_flags: MTD_WRITEABLE +- },{ +- name: "raw/kernel", +- size: 0x300000, +- offset: 0x500000 +- } ++ .name = "yamon env", ++ .size = 0x00020000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE}, ++ { ++ .name = "User FS", ++ .size = 0x003e0000, ++ .offset = 0x20000,}, ++ { ++ .name = "boot code", ++ .size = 0x100000, ++ .offset = 0x400000, ++ .mask_flags = MTD_WRITEABLE}, ++ { ++ .name = "raw/kernel", ++ .size = 0x300000, ++ .offset = 0x500000} + }; + + #elif defined(CONFIG_MIPS_PB1500) || defined(CONFIG_MIPS_PB1100) + +-static unsigned char flash_buswidth = 4; + #if defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER) +-/* both 32MiB banks will be used. Combine the first 32MiB bank and the +- * first 28MiB of the second bank together into a single jffs/jffs2 ++/* both 32MB banks will be used. Combine the first 32MB bank and the ++ * first 28MB of the second bank together into a single jffs/jffs2 + * partition. + */ +-static unsigned long flash_size = 0x04000000; + #define WINDOW_ADDR 0x1C000000 + #define WINDOW_SIZE 0x4000000 + static struct mtd_partition pb1xxx_partitions[] = { + { +- name: "User FS", +- size: 0x3c00000, +- offset: 0x0000000 +- },{ +- name: "yamon", +- size: 0x0100000, +- offset: 0x3c00000, +- mask_flags: MTD_WRITEABLE +- },{ +- name: "raw kernel", +- size: 0x02c0000, +- offset: 0x3d00000 ++ .name = "User FS", ++ .size = 0x3c00000, ++ .offset = 0x0000000 ++ },{ ++ .name = "yamon", ++ .size = 0x0100000, ++ .offset = 0x3c00000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "raw kernel", ++ .size = 0x02c0000, ++ .offset = 0x3d00000 + } + }; + #elif defined(CONFIG_MTD_PB1500_BOOT) && !defined(CONFIG_MTD_PB1500_USER) +-static unsigned long flash_size = 0x02000000; + #define WINDOW_ADDR 0x1E000000 + #define WINDOW_SIZE 0x2000000 + static struct mtd_partition pb1xxx_partitions[] = { + { +- name: "User FS", +- size: 0x1c00000, +- offset: 0x0000000 +- },{ +- name: "yamon", +- size: 0x0100000, +- offset: 0x1c00000, +- mask_flags: MTD_WRITEABLE +- },{ +- name: "raw kernel", +- size: 0x02c0000, +- offset: 0x1d00000 ++ .name = "User FS", ++ .size = 0x1c00000, ++ .offset = 0x0000000 ++ },{ ++ .name = "yamon", ++ .size = 0x0100000, ++ .offset = 0x1c00000, ++ .mask_flags = MTD_WRITEABLE ++ },{ ++ .name = "raw kernel", ++ .size = 0x02c0000, ++ .offset = 0x1d00000 + } + }; + #elif !defined(CONFIG_MTD_PB1500_BOOT) && defined(CONFIG_MTD_PB1500_USER) +-static unsigned long flash_size = 0x02000000; + #define WINDOW_ADDR 0x1C000000 + #define WINDOW_SIZE 0x2000000 + static struct mtd_partition pb1xxx_partitions[] = { + { +- name: "User FS", +- size: 0x1e00000, +- offset: 0x0000000 +- },{ +- name: "raw kernel", +- size: 0x0200000, +- offset: 0x1e00000, ++ .name = "User FS", ++ .size = 0x1e00000, ++ .offset = 0x0000000 ++ },{ ++ .name = "raw kernel", ++ .size = 0x0200000, ++ .offset = 0x1e00000, + } + }; + #else + #error MTD_PB1500 define combo error /* should never happen */ + #endif +-#elif defined(CONFIG_MTD_BOSPORUS) +-static unsigned char flash_buswidth = 2; +-static unsigned long flash_size = 0x02000000; +-#define WINDOW_ADDR 0x1F000000 +-#define WINDOW_SIZE 0x2000000 +-static struct mtd_partition pb1xxx_partitions[] = { +- { +- name: "User FS", +- size: 0x00400000, +- offset: 0x00000000, +- },{ +- name: "Yamon-2", +- size: 0x00100000, +- offset: 0x00400000, +- },{ +- name: "Root FS", +- size: 0x00700000, +- offset: 0x00500000, +- },{ +- name: "Yamon-1", +- size: 0x00100000, +- offset: 0x00C00000, +- },{ +- name: "Kernel", +- size: 0x00300000, +- offset: 0x00D00000, +- } +-}; + #else + #error Unsupported board + #endif + ++#define NAME "Pb1x00 Linux Flash" ++#define PADDR WINDOW_ADDR ++#define BUSWIDTH 4 ++#define SIZE WINDOW_SIZE ++#define PARTITIONS 4 ++ ++static struct map_info pb1xxx_mtd_map = { ++ .name = NAME, ++ .size = SIZE, ++ .buswidth = BUSWIDTH, ++ .phys = PADDR, ++}; + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) +- +-static struct mtd_partition *parsed_parts; +-static struct mtd_info *mymtd; ++static struct mtd_info *pb1xxx_mtd; + + int __init pb1xxx_mtd_init(void) + { +@@ -236,40 +137,37 @@ + int nb_parts = 0; + char *part_type; + +- /* Default flash buswidth */ +- pb1xxx_map.buswidth = flash_buswidth; +- + /* + * Static partition definition selection + */ + part_type = "static"; + parts = pb1xxx_partitions; +- nb_parts = NB_OF(pb1xxx_partitions); +- pb1xxx_map.size = flash_size; ++ nb_parts = ARRAY_SIZE(pb1xxx_partitions); + + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ + printk(KERN_NOTICE "Pb1xxx flash: probing %d-bit flash bus\n", +- pb1xxx_map.buswidth*8); +- pb1xxx_map.map_priv_1 = +- (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); +- mymtd = do_map_probe("cfi_probe", &pb1xxx_map); +- if (!mymtd) return -ENXIO; +- mymtd->module = THIS_MODULE; ++ BUSWIDTH*8); ++ pb1xxx_mtd_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ ++ simple_map_init(&pb1xxx_mtd_map); ++ ++ pb1xxx_mtd = do_map_probe("cfi_probe", &pb1xxx_mtd_map); ++ if (!pb1xxx_mtd) return -ENXIO; ++ pb1xxx_mtd->owner = THIS_MODULE; + +- add_mtd_partitions(mymtd, parts, nb_parts); ++ add_mtd_partitions(pb1xxx_mtd, parts, nb_parts); + return 0; + } + + static void __exit pb1xxx_mtd_cleanup(void) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); +- if (parsed_parts) +- kfree(parsed_parts); ++ if (pb1xxx_mtd) { ++ del_mtd_partitions(pb1xxx_mtd); ++ map_destroy(pb1xxx_mtd); ++ iounmap((void *) pb1xxx_mtd_map.virt); + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pci.c linux/drivers/mtd/maps/pci.c +--- linux-mips-2.4.27/drivers/mtd/maps/pci.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/pci.c 2004-11-19 10:25:11.944192952 +0100 +@@ -7,7 +7,7 @@ + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * +- * $Id: pci.c,v 1.2 2003/01/24 13:11:43 dwmw2 Exp $ ++ * $Id: pci.c,v 1.5 2003/05/20 20:59:31 dwmw2 Exp $ + * + * Generic PCI memory map driver. We support the following boards: + * - Intel IQ80310 ATU. +@@ -98,10 +98,10 @@ + } + + static struct mtd_pci_info intel_iq80310_info = { +- init: intel_iq80310_init, +- exit: intel_iq80310_exit, +- translate: intel_iq80310_translate, +- map_name: "cfi_probe", ++ .init = intel_iq80310_init, ++ .exit = intel_iq80310_exit, ++ .translate = intel_iq80310_translate, ++ .map_name = "cfi_probe", + }; + + /* +@@ -181,10 +181,10 @@ + } + + static struct mtd_pci_info intel_dc21285_info = { +- init: intel_dc21285_init, +- exit: intel_dc21285_exit, +- translate: intel_dc21285_translate, +- map_name: "jedec_probe", ++ .init = intel_dc21285_init, ++ .exit = intel_dc21285_exit, ++ .translate = intel_dc21285_translate, ++ .map_name = "jedec_probe", + }; + + /* +@@ -193,22 +193,20 @@ + + static struct pci_device_id mtd_pci_ids[] __devinitdata = { + { +- vendor: PCI_VENDOR_ID_INTEL, +- device: 0x530d, +- subvendor: PCI_ANY_ID, +- subdevice: PCI_ANY_ID, +- class: PCI_CLASS_MEMORY_OTHER << 8, +- class_mask: 0xffff00, +- driver_data: (unsigned long)&intel_iq80310_info, ++ .vendor = PCI_VENDOR_ID_INTEL, ++ .device = 0x530d, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID, ++ .class = PCI_CLASS_MEMORY_OTHER << 8, ++ .class_mask = 0xffff00, ++ .driver_data = (unsigned long)&intel_iq80310_info, + }, + { +- vendor: PCI_VENDOR_ID_DEC, +- device: PCI_DEVICE_ID_DEC_21285, +- subvendor: 0, /* DC21285 defaults to 0 on reset */ +- subdevice: 0, /* DC21285 defaults to 0 on reset */ +- class: 0, +- class_mask: 0, +- driver_data: (unsigned long)&intel_dc21285_info, ++ .vendor = PCI_VENDOR_ID_DEC, ++ .device = PCI_DEVICE_ID_DEC_21285, ++ .subvendor = 0, /* DC21285 defaults to 0 on reset */ ++ .subdevice = 0, /* DC21285 defaults to 0 on reset */ ++ .driver_data = (unsigned long)&intel_dc21285_info, + }, + { 0, } + }; +@@ -275,14 +273,15 @@ + } + + static struct map_info mtd_pci_map = { +- read8: mtd_pci_read8, +- read16: mtd_pci_read16, +- read32: mtd_pci_read32, +- copy_from: mtd_pci_copyfrom, +- write8: mtd_pci_write8, +- write16: mtd_pci_write16, +- write32: mtd_pci_write32, +- copy_to: mtd_pci_copyto, ++ .phys = NO_XIP, ++ .read8 = mtd_pci_read8, ++ .read16 = mtd_pci_read16, ++ .read32 = mtd_pci_read32, ++ .copy_from = mtd_pci_copyfrom, ++ .write8 = mtd_pci_write8, ++ .write16 = mtd_pci_write16, ++ .write32 = mtd_pci_write32, ++ .copy_to = mtd_pci_copyto, + }; + + static int __devinit +@@ -322,7 +321,7 @@ + if (!mtd) + goto release; + +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + add_mtd_device(mtd); + + pci_set_drvdata(dev, mtd); +@@ -359,10 +358,10 @@ + } + + static struct pci_driver mtd_pci_driver = { +- name: "MTD PCI", +- probe: mtd_pci_probe, +- remove: __devexit_p(mtd_pci_remove), +- id_table: mtd_pci_ids, ++ .name = "MTD PCI", ++ .probe = mtd_pci_probe, ++ .remove = __devexit_p(mtd_pci_remove), ++ .id_table = mtd_pci_ids, + }; + + static int __init mtd_pci_maps_init(void) +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pcmciamtd.c linux/drivers/mtd/maps/pcmciamtd.c +--- linux-mips-2.4.27/drivers/mtd/maps/pcmciamtd.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/maps/pcmciamtd.c 2004-11-19 10:25:11.946192648 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: pcmciamtd.c,v 1.39 2003/01/06 17:51:38 spse Exp $ ++ * $Id: pcmciamtd.c,v 1.48 2003/06/24 07:14:38 spse Exp $ + * + * pcmciamtd.c - MTD driver for PCMCIA flash memory cards + * +@@ -14,6 +14,7 @@ + #include <linux/module.h> + #include <linux/slab.h> + #include <linux/timer.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <asm/system.h> + +@@ -24,6 +25,7 @@ + #include <pcmcia/ds.h> + + #include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> + + #ifdef CONFIG_MTD_DEBUG + static int debug = CONFIG_MTD_DEBUG_VERBOSE; +@@ -47,7 +49,7 @@ + + + #define DRIVER_DESC "PCMCIA Flash memory card driver" +-#define DRIVER_VERSION "$Revision: 1.39 $" ++#define DRIVER_VERSION "$Revision: 1.48 $" + + /* Size of the PCMCIA address space: 26 bits = 64 MB */ + #define MAX_PCMCIA_ADDR 0x4000000 +@@ -96,7 +98,7 @@ + MODULE_PARM(mem_speed, "i"); + MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns"); + MODULE_PARM(force_size, "i"); +-MODULE_PARM_DESC(force_size, "Force size of card in MB (1-64)"); ++MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)"); + MODULE_PARM(setvpp, "i"); + MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)"); + MODULE_PARM(vpp, "i"); +@@ -106,11 +108,13 @@ + + + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,69) + static inline void cs_error(client_handle_t handle, int func, int ret) + { + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); + } ++#endif + + + /* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */ +@@ -529,6 +533,7 @@ + + card_settings(dev, link, &new_name); + ++ dev->pcmcia_map.phys = NO_XIP; + dev->pcmcia_map.read8 = pcmcia_read8_remap; + dev->pcmcia_map.read16 = pcmcia_read16_remap; + dev->pcmcia_map.copy_from = pcmcia_copy_from_remap; +@@ -539,7 +544,7 @@ + dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp; + + /* Request a memory window for PCMCIA. Some architeures can map windows upto the maximum +- that PCMCIA can support (64Mb) - this is ideal and we aim for a window the size of the ++ that PCMCIA can support (64MiB) - this is ideal and we aim for a window the size of the + whole card - otherwise we try smaller windows until we succeed */ + + req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; +@@ -552,7 +557,7 @@ + + do { + int ret; +- DEBUG(2, "requesting window with size = %dKB memspeed = %d", ++ DEBUG(2, "requesting window with size = %dKiB memspeed = %d", + req.Size >> 10, req.AccessSpeed); + link->win = (window_handle_t)link->handle; + ret = CardServices(RequestWindow, &link->win, &req); +@@ -560,7 +565,7 @@ + if(ret) { + req.Size >>= 1; + } else { +- DEBUG(2, "Got window of size %dKB", req.Size >> 10); ++ DEBUG(2, "Got window of size %dKiB", req.Size >> 10); + dev->win_size = req.Size; + break; + } +@@ -573,7 +578,7 @@ + pcmciamtd_release((u_long)link); + return; + } +- DEBUG(1, "Allocated a window of %dKB", dev->win_size >> 10); ++ DEBUG(1, "Allocated a window of %dKiB", dev->win_size >> 10); + + /* Get write protect status */ + CS_CHECK(GetStatus, link->handle, &status); +@@ -642,21 +647,21 @@ + } + + dev->mtd_info = mtd; +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + + if(new_name) { + int size = 0; + char unit = ' '; + /* Since we are using a default name, make it better by adding in the + size */ +- if(mtd->size < 1048576) { /* <1MB in size, show size in K */ ++ if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */ + size = mtd->size >> 10; + unit = 'K'; + } else { + size = mtd->size >> 20; + unit = 'M'; + } +- snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%cB %s", size, unit, "PCMCIA Memory card"); ++ snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%ciB %s", size, unit, "PCMCIA Memory card"); + } + + /* If the memory found is fits completely into the mapped PCMCIA window, +@@ -828,16 +833,20 @@ + } + + ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68) ++static struct pcmcia_driver pcmciamtd_driver = { ++ .drv = { ++ .name = "pcmciamtd" ++ }, ++ .attach = pcmciamtd_attach, ++ .detach = pcmciamtd_detach, ++ .owner = THIS_MODULE ++}; ++#endif ++ ++ + static int __init init_pcmciamtd(void) + { +- servinfo_t serv; +- +- info(DRIVER_DESC " " DRIVER_VERSION); +- CardServices(GetCardServicesInfo, &serv); +- if (serv.Revision != CS_RELEASE_CODE) { +- err("Card Services release does not match!"); +- return -1; +- } + + if(buswidth && buswidth != 1 && buswidth != 2) { + info("bad buswidth (%d), using default", buswidth); +@@ -851,15 +860,24 @@ + info("bad mem_type (%d), using default", mem_type); + mem_type = 0; + } ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68) ++ return pcmcia_register_driver(&pcmciamtd_driver); ++#else + register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach); + return 0; ++#endif + } + + + static void __exit exit_pcmciamtd(void) + { + DEBUG(1, DRIVER_DESC " unloading"); ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,68) ++ pcmcia_unregister_driver(&pcmciamtd_driver); ++#else + unregister_pccard_driver(&dev_info); ++#endif + + while(dev_list) { + dev_link_t *link = dev_list; +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/physmap.c linux/drivers/mtd/maps/physmap.c +--- linux-mips-2.4.27/drivers/mtd/maps/physmap.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/physmap.c 2004-11-19 10:25:11.947192496 +0100 +@@ -1,179 +1,114 @@ + /* +- * $Id: physmap.c,v 1.21 2002/09/05 05:12:54 acurtis Exp $ ++ * $Id: physmap.c,v 1.30 2003/11/11 19:05:18 jsun Exp $ + * + * Normal mappings of chips in physical memory ++ * ++ * Copyright (C) 2003 MontaVista Software Inc. ++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net ++ * ++ * 031022 - [jsun] add run-time configure and partition setup + */ + + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/slab.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> + #include <linux/config.h> +- +-#ifdef CONFIG_MTD_PARTITIONS + #include <linux/mtd/partitions.h> +-#endif +- +-#define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START +-#define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN +-#define BUSWIDTH CONFIG_MTD_PHYSMAP_BUSWIDTH + + static struct mtd_info *mymtd; + +-__u8 physmap_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 physmap_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 physmap_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void physmap_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} ++struct map_info physmap_map = {.name = "phys_mapped_flash"}; + +-void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} ++#ifdef CONFIG_MTD_PARTITIONS ++static struct mtd_partition *mtd_parts; ++static int mtd_parts_nb; + +-void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} ++static int num_physmap_partitions; ++static struct mtd_partition *physmap_partitions; + +-void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} ++char *part_probes[] __initdata = {"cmdlinepart", "RedBoot", NULL}; + +-void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++void physmap_set_partitions(struct mtd_partition *parts, int num_parts) + { +- memcpy_toio(map->map_priv_1 + to, from, len); ++ physmap_partitions=parts; ++ num_physmap_partitions=num_parts; + } +- +-struct map_info physmap_map = { +- name: "Physically mapped flash", +- size: WINDOW_SIZE, +- buswidth: BUSWIDTH, +- read8: physmap_read8, +- read16: physmap_read16, +- read32: physmap_read32, +- copy_from: physmap_copy_from, +- write8: physmap_write8, +- write16: physmap_write16, +- write32: physmap_write32, +- copy_to: physmap_copy_to +-}; +- +-#ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +-static struct mtd_partition *mtd_parts = 0; +-static int mtd_parts_nb = 0; +-#else +-static struct mtd_partition physmap_partitions[] = { +-/* Put your own partition definitions here */ +-#if 0 +- { +- name: "bootROM", +- size: 0x80000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "zImage", +- size: 0x100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- } +-#endif +-}; +- +-#define NUM_PARTITIONS (sizeof(physmap_partitions)/sizeof(struct mtd_partition)) +- +-#endif +-#endif ++#endif /* CONFIG_MTD_PARTITIONS */ + + int __init init_physmap(void) + { + static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 }; + const char **type; + +- printk(KERN_NOTICE "physmap flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); +- physmap_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); ++ printk(KERN_NOTICE "physmap flash device: %lx at %lx\n", physmap_map.size, physmap_map.phys); ++ physmap_map.virt = (unsigned long)ioremap(physmap_map.phys, physmap_map.size); + +- if (!physmap_map.map_priv_1) { ++ if (!physmap_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + ++ simple_map_init(&physmap_map); ++ + mymtd = 0; + type = rom_probe_types; + for(; !mymtd && *type; type++) { + mymtd = do_map_probe(*type, &physmap_map); + } + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + +- add_mtd_device(mymtd); + #ifdef CONFIG_MTD_PARTITIONS +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, +- "phys"); ++ mtd_parts_nb = parse_mtd_partitions(mymtd, part_probes, ++ &mtd_parts, 0); ++ + if (mtd_parts_nb > 0) + { +- printk(KERN_NOTICE +- "Using command line partition definition\n"); + add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb); ++ return 0; + } +-#else +- if (NUM_PARTITIONS != 0) ++ ++ if (num_physmap_partitions != 0) + { + printk(KERN_NOTICE + "Using physmap partition definition\n"); +- add_mtd_partitions (mymtd, physmap_partitions, NUM_PARTITIONS); ++ add_mtd_partitions (mymtd, physmap_partitions, num_physmap_partitions); ++ return 0; + } + + #endif +-#endif ++ add_mtd_device(mymtd); ++ + return 0; + } + +- iounmap((void *)physmap_map.map_priv_1); ++ iounmap((void *)physmap_map.virt); + return -ENXIO; + } + + static void __exit cleanup_physmap(void) + { +- if (mymtd) { ++#ifdef CONFIG_MTD_PARTITIONS ++ if (mtd_parts_nb) { ++ del_mtd_partitions(mymtd); ++ kfree(mtd_parts); ++ } else if (num_physmap_partitions) { ++ del_mtd_partitions(mymtd); ++ } else { + del_mtd_device(mymtd); +- map_destroy(mymtd); +- } +- if (physmap_map.map_priv_1) { +- iounmap((void *)physmap_map.map_priv_1); +- physmap_map.map_priv_1 = 0; + } ++#else ++ del_mtd_device(mymtd); ++#endif ++ map_destroy(mymtd); ++ ++ iounmap((void *)physmap_map.virt); ++ physmap_map.virt = 0; + } + + module_init(init_physmap); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/pnc2000.c linux/drivers/mtd/maps/pnc2000.c +--- linux-mips-2.4.27/drivers/mtd/maps/pnc2000.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/pnc2000.c 2004-11-19 10:25:11.949192192 +0100 +@@ -5,12 +5,13 @@ + * + * This code is GPL + * +- * $Id: pnc2000.c,v 1.10 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: pnc2000.c,v 1.14 2003/05/21 12:45:19 dwmw2 Exp $ + */ + + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -24,58 +25,13 @@ + * MAP DRIVER STUFF + */ + +-__u8 pnc_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(WINDOW_ADDR + ofs); +-} +- +-__u16 pnc_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(WINDOW_ADDR + ofs); +-} +- +-__u32 pnc_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(volatile unsigned int *)(WINDOW_ADDR + ofs); +-} +- +-void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(WINDOW_ADDR + from), len); +-} +- +-void pnc_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(WINDOW_ADDR + adr) = d; +-} +- +-void pnc_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(WINDOW_ADDR + adr) = d; +-} +- +-void pnc_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(WINDOW_ADDR + adr) = d; +-} +- +-void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(WINDOW_ADDR + to), from, len); +-} + + struct map_info pnc_map = { +- name: "PNC-2000", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: pnc_read8, +- read16: pnc_read16, +- read32: pnc_read32, +- copy_from: pnc_copy_from, +- write8: pnc_write8, +- write16: pnc_write16, +- write32: pnc_write32, +- copy_to: pnc_copy_to ++ .name = "PNC-2000", ++ .size = WINDOW_SIZE, ++ .buswidth = 4, ++ .phys = 0xFFFFFFFF, ++ .virt = WINDOW_ADDR, + }; + + +@@ -84,19 +40,19 @@ + */ + static struct mtd_partition pnc_partitions[3] = { + { +- name: "PNC-2000 boot firmware", +- size: 0x20000, +- offset: 0 ++ .name = "PNC-2000 boot firmware", ++ .size = 0x20000, ++ .offset = 0 + }, + { +- name: "PNC-2000 kernel", +- size: 0x1a0000, +- offset: 0x20000 ++ .name = "PNC-2000 kernel", ++ .size = 0x1a0000, ++ .offset = 0x20000 + }, + { +- name: "PNC-2000 filesystem", +- size: 0x240000, +- offset: 0x1c0000 ++ .name = "PNC-2000 filesystem", ++ .size = 0x240000, ++ .offset = 0x1c0000 + } + }; + +@@ -110,9 +66,11 @@ + { + printk(KERN_NOTICE "Photron PNC-2000 flash mapping: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); + ++ simple_map_init(&pnc_map); ++ + mymtd = do_map_probe("cfi_probe", &pnc_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + return add_mtd_partitions(mymtd, pnc_partitions, 3); + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/redwood.c linux/drivers/mtd/maps/redwood.c +--- linux-mips-2.4.27/drivers/mtd/maps/redwood.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/redwood.c 2004-11-19 10:25:11.962190216 +0100 +@@ -1,38 +1,23 @@ + /* +- * $Id: ++ * $Id: redwood.c,v 1.7 2003/11/21 23:09:52 trini Exp $ + * +- * redwood.c - mapper for IBM Redwood-4/5 board. ++ * drivers/mtd/maps/redwood.c + * +- * Copyright 2001 MontaVista Softare Inc. ++ * FLASH map for the IBM Redwood 4/5/6 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 SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED +- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +- * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN +- * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, +- * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +- * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF +- * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON +- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +- * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +- * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +- * +- * You should have received a copy of the GNU General Public License along +- * with this program; if not, write to the Free Software Foundation, Inc., +- * 675 Mass Ave, Cambridge, MA 02139, USA. +- * +- * History: 12/17/2001 - Armin +- * migrated to use do_map_probe ++ * Author: MontaVista Software, Inc. <source@mvista.com> + * ++ * 2001-2003 (c) MontaVista, Software, Inc. This file is licensed under ++ * the terms of the GNU General Public License version 2. This program ++ * is licensed "as is" without any warranty of any kind, whether express ++ * or implied. + */ + ++#include <linux/config.h> + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -40,96 +25,102 @@ + + #include <asm/io.h> + ++#if !defined (CONFIG_REDWOOD_6) ++ + #define WINDOW_ADDR 0xffc00000 + #define WINDOW_SIZE 0x00400000 + +-__u8 redwood_flash_read8(struct map_info *map, unsigned long ofs) +-{ +- return *(__u8 *)(map->map_priv_1 + ofs); +-} +- +-__u16 redwood_flash_read16(struct map_info *map, unsigned long ofs) +-{ +- return *(__u16 *)(map->map_priv_1 + ofs); +-} +- +-__u32 redwood_flash_read32(struct map_info *map, unsigned long ofs) +-{ +- return *(volatile unsigned int *)(map->map_priv_1 + ofs); +-} +- +-void redwood_flash_copy_from(struct map_info *map, void *to, +- unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void redwood_flash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *(__u8 *)(map->map_priv_1 + adr) = d; +-} +- +-void redwood_flash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *(__u16 *)(map->map_priv_1 + adr) = d; +-} +- +-void redwood_flash_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *(__u32 *)(map->map_priv_1 + adr) = d; +-} +- +-void redwood_flash_copy_to(struct map_info *map, unsigned long to, +- const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} ++#define RW_PART0_OF 0 ++#define RW_PART0_SZ 0x10000 ++#define RW_PART1_OF RW_PART0_SZ ++#define RW_PART1_SZ 0x200000 - 0x10000 ++#define RW_PART2_OF 0x200000 ++#define RW_PART2_SZ 0x10000 ++#define RW_PART3_OF 0x210000 ++#define RW_PART3_SZ 0x200000 - (0x10000 + 0x20000) ++#define RW_PART4_OF 0x3e0000 ++#define RW_PART4_SZ 0x20000 + +-struct map_info redwood_flash_map = { +- name: "IBM Redwood", +- size: WINDOW_SIZE, +- buswidth: 2, +- read8: redwood_flash_read8, +- read16: redwood_flash_read16, +- read32: redwood_flash_read32, +- copy_from: redwood_flash_copy_from, +- write8: redwood_flash_write8, +- write16: redwood_flash_write16, +- write32: redwood_flash_write32, +- copy_to: redwood_flash_copy_to ++static struct mtd_partition redwood_flash_partitions[] = { ++ { ++ .name = "Redwood OpenBIOS Vital Product Data", ++ .offset = RW_PART0_OF, ++ .size = RW_PART0_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ }, ++ { ++ .name = "Redwood kernel", ++ .offset = RW_PART1_OF, ++ .size = RW_PART1_SZ ++ }, ++ { ++ .name = "Redwood OpenBIOS non-volatile storage", ++ .offset = RW_PART2_OF, ++ .size = RW_PART2_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ }, ++ { ++ .name = "Redwood filesystem", ++ .offset = RW_PART3_OF, ++ .size = RW_PART3_SZ ++ }, ++ { ++ .name = "Redwood OpenBIOS", ++ .offset = RW_PART4_OF, ++ .size = RW_PART4_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ ++ } + }; + ++#else /* CONFIG_REDWOOD_6 */ ++/* FIXME: the window is bigger - armin */ ++#define WINDOW_ADDR 0xff800000 ++#define WINDOW_SIZE 0x00800000 ++ ++#define RW_PART0_OF 0 ++#define RW_PART0_SZ 0x400000 /* 4 MiB data */ ++#define RW_PART1_OF RW_PART0_OF + RW_PART0_SZ ++#define RW_PART1_SZ 0x10000 /* 64K VPD */ ++#define RW_PART2_OF RW_PART1_OF + RW_PART1_SZ ++#define RW_PART2_SZ 0x400000 - (0x10000 + 0x20000) ++#define RW_PART3_OF RW_PART2_OF + RW_PART2_SZ ++#define RW_PART3_SZ 0x20000 + + static struct mtd_partition redwood_flash_partitions[] = { + { +- name: "Redwood OpenBIOS Vital Product Data", +- offset: 0, +- size: 0x10000, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Redwood filesystem", ++ .offset = RW_PART0_OF, ++ .size = RW_PART0_SZ + }, + { +- name: "Redwood kernel", +- offset: 0x10000, +- size: 0x200000 - 0x10000 ++ .name = "Redwood OpenBIOS Vital Product Data", ++ .offset = RW_PART1_OF, ++ .size = RW_PART1_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + }, + { +- name: "Redwood OpenBIOS non-volatile storage", +- offset: 0x200000, +- size: 0x10000, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Redwood kernel", ++ .offset = RW_PART2_OF, ++ .size = RW_PART2_SZ + }, + { +- name: "Redwood filesystem", +- offset: 0x210000, +- size: 0x200000 - (0x10000 + 0x20000) +- }, +- { +- name: "Redwood OpenBIOS", +- offset: 0x3e0000, +- size: 0x20000, +- mask_flags: MTD_WRITEABLE /* force read-only */ ++ .name = "Redwood OpenBIOS", ++ .offset = RW_PART3_OF, ++ .size = RW_PART3_SZ, ++ .mask_flags = MTD_WRITEABLE /* force read-only */ + } + }; ++ ++#endif /* CONFIG_REDWOOD_6 */ ++ ++struct map_info redwood_flash_map = { ++ .name = "IBM Redwood", ++ .size = WINDOW_SIZE, ++ .buswidth = 2, ++ .phys = WINDOW_ADDR, ++}; ++ ++ + #define NUM_REDWOOD_FLASH_PARTITIONS \ + (sizeof(redwood_flash_partitions)/sizeof(redwood_flash_partitions[0])) + +@@ -140,18 +131,19 @@ + printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n", + WINDOW_SIZE, WINDOW_ADDR); + +- redwood_flash_map.map_priv_1 = ++ redwood_flash_map.virt = + (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + +- if (!redwood_flash_map.map_priv_1) { ++ if (!redwood_flash_map.virt) { + printk("init_redwood_flash: failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&redwood_flash_map); + + redwood_mtd = do_map_probe("cfi_probe",&redwood_flash_map); + + if (redwood_mtd) { +- redwood_mtd->module = THIS_MODULE; ++ redwood_mtd->owner = THIS_MODULE; + return add_mtd_partitions(redwood_mtd, + redwood_flash_partitions, + NUM_REDWOOD_FLASH_PARTITIONS); +@@ -164,10 +156,15 @@ + { + if (redwood_mtd) { + del_mtd_partitions(redwood_mtd); +- iounmap((void *)redwood_flash_map.map_priv_1); ++ /* moved iounmap after map_destroy - armin */ + map_destroy(redwood_mtd); ++ iounmap((void *)redwood_flash_map.virt); + } + } + + module_init(init_redwood_flash); + module_exit(cleanup_redwood_flash); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("MontaVista Software <source@mvista.com>"); ++MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/rpxlite.c linux/drivers/mtd/maps/rpxlite.c +--- linux-mips-2.4.27/drivers/mtd/maps/rpxlite.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/rpxlite.c 2004-11-19 10:25:11.963190064 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp $ ++ * $Id: rpxlite.c,v 1.19 2003/05/21 12:45:19 dwmw2 Exp $ + * + * Handle mapping of the flash on the RPX Lite and CLLF boards + */ +@@ -7,6 +7,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -17,80 +18,31 @@ + + static struct mtd_info *mymtd; + +-__u8 rpxlite_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-__u16 rpxlite_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-__u32 rpxlite_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- +-struct map_info rpxlite_map = { +- name: "RPX", +- size: WINDOW_SIZE, +- buswidth: 4, +- read8: rpxlite_read8, +- read16: rpxlite_read16, +- read32: rpxlite_read32, +- copy_from: rpxlite_copy_from, +- write8: rpxlite_write8, +- write16: rpxlite_write16, +- write32: rpxlite_write32, +- copy_to: rpxlite_copy_to ++static struct map_info rpxlite_map = { ++ .name = "RPX", ++ .size = WINDOW_SIZE, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR, + }; + + int __init init_rpxlite(void) + { + printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); +- rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); ++ rpxlite_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + +- if (!rpxlite_map.map_priv_1) { ++ if (!rpxlite_map.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } ++ simple_map_init(&rpxlite_map); + mymtd = do_map_probe("cfi_probe", &rpxlite_map); + if (mymtd) { +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + add_mtd_device(mymtd); + return 0; + } + +- iounmap((void *)rpxlite_map.map_priv_1); ++ iounmap((void *)rpxlite_map.virt); + return -ENXIO; + } + +@@ -100,9 +52,9 @@ + del_mtd_device(mymtd); + map_destroy(mymtd); + } +- if (rpxlite_map.map_priv_1) { +- iounmap((void *)rpxlite_map.map_priv_1); +- rpxlite_map.map_priv_1 = 0; ++ if (rpxlite_map.virt) { ++ iounmap((void *)rpxlite_map.virt); ++ rpxlite_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sa1100-flash.c linux/drivers/mtd/maps/sa1100-flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/sa1100-flash.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/sa1100-flash.c 2004-11-19 10:25:11.966189608 +0100 +@@ -3,7 +3,7 @@ + * + * (C) 2000 Nicolas Pitre <nico@cam.org> + * +- * $Id: sa1100-flash.c,v 1.29 2002/09/06 14:36:19 abz Exp $ ++ * $Id: sa1100-flash.c,v 1.38 2004/01/22 19:11:08 rmk Exp $ + */ + + #include <linux/config.h> +@@ -11,278 +11,212 @@ + #include <linux/types.h> + #include <linux/ioport.h> + #include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/errno.h> ++#include <linux/slab.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> + #include <linux/mtd/partitions.h> ++#include <linux/mtd/concat.h> + + #include <asm/hardware.h> ++#include <asm/mach-types.h> + #include <asm/io.h> ++#include <asm/sizes.h> + ++#include <asm/arch/h3600.h> + + #ifndef CONFIG_ARCH_SA1100 + #error This is for SA1100 architecture only + #endif + ++/* ++ * This isnt complete yet, so... ++ */ ++#define CONFIG_MTD_SA1100_STATICMAP 1 + +-#define WINDOW_ADDR 0xe8000000 +- +-static __u8 sa1100_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 sa1100_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 sa1100_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void sa1100_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void sa1100_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void sa1100_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void sa1100_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *)(map->map_priv_1 + to), from, len); +-} +- +-static struct map_info sa1100_map = { +- name: "SA1100 flash", +- read8: sa1100_read8, +- read16: sa1100_read16, +- read32: sa1100_read32, +- copy_from: sa1100_copy_from, +- write8: sa1100_write8, +- write16: sa1100_write16, +- write32: sa1100_write32, +- copy_to: sa1100_copy_to, +- +- map_priv_1: WINDOW_ADDR, +- map_priv_2: -1, +-}; +- +- ++#ifdef CONFIG_MTD_SA1100_STATICMAP + /* + * Here are partition information for all known SA1100-based devices. + * See include/linux/mtd/partitions.h for definition of the mtd_partition + * structure. + * +- * The *_max_flash_size is the maximum possible mapped flash size which +- * is not necessarily the actual flash size. It must be no more than +- * the value specified in the "struct map_desc *_io_desc" mapping +- * definition for the corresponding machine. ++ * Please note: ++ * 1. We no longer support static flash mappings via the machine io_desc ++ * structure. ++ * 2. The flash size given should be the largest flash size that can ++ * be accommodated. ++ * ++ * The MTD layer will detect flash chip aliasing and reduce the size of ++ * the map accordingly. + * + * Please keep these in alphabetical order, and formatted as per existing + * entries. Thanks. + */ + + #ifdef CONFIG_SA1100_ADSBITSY +-#define ADSBITSY_FLASH_SIZE 0x02000000 + static struct mtd_partition adsbitsy_partitions[] = { + { +- name: "bootROM", +- size: 0x80000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "zImage", +- size: 0x100000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "bootROM", ++ .size = 0x80000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "zImage", ++ .size = 0x100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "ramdisk.gz", ++ .size = 0x300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "User FS", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_ASSABET + /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ +-#define ASSABET4_FLASH_SIZE 0x00400000 + static struct mtd_partition assabet4_partitions[] = { + { +- name: "bootloader", +- size: 0x00020000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "bootloader params", +- size: 0x00020000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "jffs", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "bootloader", ++ .size = 0x00020000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "bootloader params", ++ .size = 0x00020000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "jffs", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + + /* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */ +-#define ASSABET5_FLASH_SIZE 0x02000000 + static struct mtd_partition assabet5_partitions[] = { + { +- name: "bootloader", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "bootloader params", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "jffs", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "bootloader", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "bootloader params", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "jffs", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + +-#define ASSABET_FLASH_SIZE ASSABET5_FLASH_SIZE + #define assabet_partitions assabet5_partitions + #endif + + #ifdef CONFIG_SA1100_BADGE4 +- + /* +- * 1 x Intel 28F320C3BA100 Advanced+ Boot Block Flash (32 Mi bit) ++ * 1 x Intel 28F320C3 Advanced+ Boot Block Flash (32 Mi bit) + * Eight 4 KiW Parameter Bottom Blocks (64 KiB) + * Sixty-three 32 KiW Main Blocks (4032 Ki b) ++ * ++ * <or> ++ * ++ * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit) ++ * Eight 4 KiW Parameter Bottom Blocks (64 KiB) ++ * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b) + */ +-#define BADGE4_FLASH_SIZE 0x00400000 + static struct mtd_partition badge4_partitions[] = { + { +- name: "BLOB boot loader", +- offset: 0, +- size: 0x0000A000 +- }, { +- name: "params", +- offset: MTDPART_OFS_APPEND, +- size: 0x00006000 +- }, { +- name: "kernel", +- offset: MTDPART_OFS_APPEND, +- size: 0x00100000 +- }, { +- name: "root", +- offset: MTDPART_OFS_APPEND, +- size: MTDPART_SIZ_FULL ++ .name = "BLOB boot loader", ++ .offset = 0, ++ .size = 0x0000A000 ++ }, { ++ .name = "params", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 0x00006000 ++ }, { ++ .name = "root", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL + } + }; +- + #endif + + + #ifdef CONFIG_SA1100_CERF + #ifdef CONFIG_SA1100_CERF_FLASH_32MB +-#define CERF_FLASH_SIZE 0x02000000 +-static struct mtd_partition cerf_partitions[] = { +- { +- name: "firmware", +- size: 0x00040000, +- offset: 0, +- }, { +- name: "params", +- size: 0x00040000, +- offset: 0x00040000, +- }, { +- name: "kernel", +- size: 0x00100000, +- offset: 0x00080000, +- }, { +- name: "rootdisk", +- size: 0x01E80000, +- offset: 0x00180000, +- } +-}; ++# define CERF_FLASH_SIZE 0x02000000 + #elif defined CONFIG_SA1100_CERF_FLASH_16MB +-#define CERF_FLASH_SIZE 0x01000000 ++# define CERF_FLASH_SIZE 0x01000000 ++#elif defined CONFIG_SA1100_CERF_FLASH_8MB ++# define CERF_FLASH_SIZE 0x00800000 ++#else ++# error "Undefined flash size for CERF in sa1100-flash.c" ++#endif ++ + static struct mtd_partition cerf_partitions[] = { + { +- name: "firmware", +- size: 0x00020000, +- offset: 0, +- }, { +- name: "params", +- size: 0x00020000, +- offset: 0x00020000, +- }, { +- name: "kernel", +- size: 0x00100000, +- offset: 0x00040000, +- }, { +- name: "rootdisk", +- size: 0x00EC0000, +- offset: 0x00140000, ++ .name = "Bootloader", ++ .size = 0x00020000, ++ .offset = 0x00000000, ++ }, { ++ .name = "Params", ++ .size = 0x00040000, ++ .offset = 0x00020000, ++ }, { ++ .name = "Kernel", ++ .size = 0x00100000, ++ .offset = 0x00060000, ++ }, { ++ .name = "Filesystem", ++ .size = CERF_FLASH_SIZE-0x00160000, ++ .offset = 0x00160000, + } + }; +-#elif defined CONFIG_SA1100_CERF_FLASH_8MB +-# error "Unwritten type definition" +-#else +-# error "Undefined memory orientation for CERF in sa1100-flash.c" +-#endif + #endif + + #ifdef CONFIG_SA1100_CONSUS +-#define CONSUS_FLASH_SIZE 0x02000000 + static struct mtd_partition consus_partitions[] = { + { +- name: "Consus boot firmware", +- offset: 0, +- size: 0x00040000, +- mask_flags: MTD_WRITABLE, /* force read-only */ +- }, { +- name: "Consus kernel", +- offset: 0x00040000, +- size: 0x00100000, +- mask_flags: 0, ++ .name = "Consus boot firmware", ++ .offset = 0, ++ .size = 0x00040000, ++ .mask_flags = MTD_WRITABLE, /* force read-only */ ++ }, { ++ .name = "Consus kernel", ++ .offset = 0x00040000, ++ .size = 0x00100000, ++ .mask_flags = 0, + }, { +- name: "Consus disk", +- offset: 0x00140000, ++ .name = "Consus disk", ++ .offset = 0x00140000, + /* The rest (up to 16M) for jffs. We could put 0 and + make it find the size automatically, but right now + i have 32 megs. jffs will use all 32 megs if given + the chance, and this leads to horrible problems + when you try to re-flash the image because blob + won't erase the whole partition. */ +- size: 0x01000000 - 0x00140000, +- mask_flags: 0, ++ .size = 0x01000000 - 0x00140000, ++ .mask_flags = 0, + }, { + /* this disk is a secondary disk, which can be used as + needed, for simplicity, make it the size of the other + consus partition, although realistically it could be + the remainder of the disk (depending on the file + system used) */ +- name: "Consus disk2", +- offset: 0x01000000, +- size: 0x01000000 - 0x00140000, +- mask_flags: 0, ++ .name = "Consus disk2", ++ .offset = 0x01000000, ++ .size = 0x01000000 - 0x00140000, ++ .mask_flags = 0, + } + }; + #endif +@@ -292,96 +226,95 @@ + #define FLEXANET_FLASH_SIZE 0x02000000 + static struct mtd_partition flexanet_partitions[] = { + { +- name: "bootloader", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "bootloader params", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "kernel", +- size: 0x000C0000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "altkernel", +- size: 0x000C0000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "root", +- size: 0x00400000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "free1", +- size: 0x00300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "free2", +- size: 0x00300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, +- }, { +- name: "free3", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, ++ .name = "bootloader", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "bootloader params", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "kernel", ++ .size = 0x000C0000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "altkernel", ++ .size = 0x000C0000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "root", ++ .size = 0x00400000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "free1", ++ .size = 0x00300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "free2", ++ .size = 0x00300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, ++ }, { ++ .name = "free3", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, + } + }; + #endif + + #ifdef CONFIG_SA1100_FREEBIRD +-#define FREEBIRD_FLASH_SIZE 0x02000000 + static struct mtd_partition freebird_partitions[] = { +-#if CONFIG_SA1100_FREEBIRD_NEW ++#ifdef CONFIG_SA1100_FREEBIRD_NEW + { +- name: "firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "kernel", +- size: 0x00080000, +- offset: 0x00040000, +- }, { +- name: "params", +- size: 0x00040000, +- offset: 0x000C0000, +- }, { +- name: "initrd", +- size: 0x00100000, +- offset: 0x00100000, +- }, { +- name: "root cramfs", +- size: 0x00300000, +- offset: 0x00200000, +- }, { +- name: "usr cramfs", +- size: 0x00C00000, +- offset: 0x00500000, +- }, { +- name: "local", +- size: MTDPART_SIZ_FULL, +- offset: 0x01100000, ++ .name = "firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "kernel", ++ .size = 0x00080000, ++ .offset = 0x00040000, ++ }, { ++ .name = "params", ++ .size = 0x00040000, ++ .offset = 0x000C0000, ++ }, { ++ .name = "initrd", ++ .size = 0x00100000, ++ .offset = 0x00100000, ++ }, { ++ .name = "root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00200000, ++ }, { ++ .name = "usr cramfs", ++ .size = 0x00C00000, ++ .offset = 0x00500000, ++ }, { ++ .name = "local", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x01100000, + } + #else + { +- size: 0x00040000, +- offset: 0, ++ .size = 0x00040000, ++ .offset = 0, + }, { +- size: 0x000c0000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x000c0000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: 0x00400000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x00400000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + #endif + }; +@@ -389,178 +322,215 @@ + + #ifdef CONFIG_SA1100_FRODO + /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ +-#define FRODO_FLASH_SIZE 0x02000000 + static struct mtd_partition frodo_partitions[] = + { + { +- name: "Boot Loader", +- size: 0x00040000, +- offset: 0x00000000 +- }, { +- name: "Parameter Block", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND +- }, { +- name: "Linux Kernel", +- size: 0x00100000, +- offset: MTDPART_OFS_APPEND +- }, { +- name: "Ramdisk", +- size: 0x00680000, +- offset: MTDPART_OFS_APPEND +- }, { +- name: "Flash File System", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND ++ .name = "bootloader", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE ++ }, { ++ .name = "bootloader params", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE ++ }, { ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE ++ }, { ++ .name = "ramdisk", ++ .size = 0x00400000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE ++ }, { ++ .name = "file system", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND + } + }; + #endif + + #ifdef CONFIG_SA1100_GRAPHICSCLIENT +-#define GRAPHICSCLIENT_FLASH_SIZE 0x02000000 + static struct mtd_partition graphicsclient_partitions[] = { + { +- name: "zImage", +- size: 0x100000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "zImage", ++ .size = 0x100000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "ramdisk.gz", ++ .size = 0x300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "User FS", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_GRAPHICSMASTER +-#define GRAPHICSMASTER_FLASH_SIZE 0x01000000 + static struct mtd_partition graphicsmaster_partitions[] = { + { +- name: "zImage", +- size: 0x100000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "zImage", ++ .size = 0x100000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "ramdisk.gz", +- size: 0x300000, +- offset: MTDPART_OFS_APPEND, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "ramdisk.gz", ++ .size = 0x300000, ++ .offset = MTDPART_OFS_APPEND, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "User FS", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "User FS", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + +-#ifdef CONFIG_SA1100_H3600 +-#define H3600_FLASH_SIZE 0x02000000 +-static struct mtd_partition h3600_partitions[] = { ++#ifdef CONFIG_SA1100_H3XXX ++static struct mtd_partition h3xxx_partitions[] = { + { +- name: "H3600 boot firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "H3600 kernel", +- size: 0x00080000, +- offset: 0x00040000, ++ .name = "H3XXX boot firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "H3600 params", +- size: 0x00040000, +- offset: 0x000C0000, ++#ifdef CONFIG_MTD_2PARTS_IPAQ ++ .name = "H3XXX root jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00040000, ++#else ++ .name = "H3XXX kernel", ++ .size = 0x00080000, ++ .offset = 0x00040000, ++ }, { ++ .name = "H3XXX params", ++ .size = 0x00040000, ++ .offset = 0x000C0000, + }, { + #ifdef CONFIG_JFFS2_FS +- name: "H3600 root jffs2", +- size: MTDPART_SIZ_FULL, +- offset: 0x00100000, ++ .name = "H3XXX root jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00100000, + #else +- name: "H3600 initrd", +- size: 0x00100000, +- offset: 0x00100000, ++ .name = "H3XXX initrd", ++ .size = 0x00100000, ++ .offset = 0x00100000, + }, { +- name: "H3600 root cramfs", +- size: 0x00300000, +- offset: 0x00200000, ++ .name = "H3XXX root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00200000, + }, { +- name: "H3600 usr cramfs", +- size: 0x00800000, +- offset: 0x00500000, ++ .name = "H3XXX usr cramfs", ++ .size = 0x00800000, ++ .offset = 0x00500000, + }, { +- name: "H3600 usr local", +- size: MTDPART_SIZ_FULL, +- offset: 0x00d00000, ++ .name = "H3XXX usr local", ++ .size = MTDPART_SIZ_FULL, ++ .offset = 0x00d00000, ++#endif + #endif + } + }; + +-static void h3600_set_vpp(struct map_info *map, int vpp) ++static void h3xxx_set_vpp(struct map_info *map, int vpp) + { + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); + } ++#else ++#define h3xxx_set_vpp NULL ++#endif ++ ++#ifdef CONFIG_SA1100_HACKKIT ++static struct mtd_partition hackkit_partitions[] = { ++ { ++ .name = "BLOB", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "config", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "initrd", ++ .size = 0x00180000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "rootfs", ++ .size = 0x700000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "data", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, ++ } ++}; + #endif + + #ifdef CONFIG_SA1100_HUW_WEBPANEL +-#define HUW_WEBPANEL_FLASH_SIZE 0x01000000 + static struct mtd_partition huw_webpanel_partitions[] = { + { +- name: "Loader", +- size: 0x00040000, +- offset: 0, +- }, { +- name: "Sector 1", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, ++ .name = "Loader", ++ .size = 0x00040000, ++ .offset = 0, ++ }, { ++ .name = "Sector 1", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_JORNADA720 +-#define JORNADA720_FLASH_SIZE 0x02000000 + static struct mtd_partition jornada720_partitions[] = { + { +- name: "JORNADA720 boot firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "JORNADA720 boot firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "JORNADA720 kernel", +- size: 0x000c0000, +- offset: 0x00040000, ++ .name = "JORNADA720 kernel", ++ .size = 0x000c0000, ++ .offset = 0x00040000, + }, { +- name: "JORNADA720 params", +- size: 0x00040000, +- offset: 0x00100000, ++ .name = "JORNADA720 params", ++ .size = 0x00040000, ++ .offset = 0x00100000, + }, { +- name: "JORNADA720 initrd", +- size: 0x00100000, +- offset: 0x00140000, ++ .name = "JORNADA720 initrd", ++ .size = 0x00100000, ++ .offset = 0x00140000, + }, { +- name: "JORNADA720 root cramfs", +- size: 0x00300000, +- offset: 0x00240000, ++ .name = "JORNADA720 root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00240000, + }, { +- name: "JORNADA720 usr cramfs", +- size: 0x00800000, +- offset: 0x00540000, ++ .name = "JORNADA720 usr cramfs", ++ .size = 0x00800000, ++ .offset = 0x00540000, + }, { +- name: "JORNADA720 usr local", +- size: 0 /* will expand to the end of the flash */ +- offset: 0x00d00000, ++ .name = "JORNADA720 usr local", ++ .size = 0, /* will expand to the end of the flash */ ++ .offset = 0x00d00000, + } + }; + +-static void jornada720_set_vpp(int vpp) ++static void jornada720_set_vpp(struct map_info *map, int vpp) + { + if (vpp) + PPSR |= 0x80; +@@ -568,454 +538,811 @@ + PPSR &= ~0x80; + PPDR |= 0x80; + } +- ++#else ++#define jornada720_set_vpp NULL + #endif + + #ifdef CONFIG_SA1100_PANGOLIN +-#define PANGOLIN_FLASH_SIZE 0x04000000 + static struct mtd_partition pangolin_partitions[] = { + { +- name: "boot firmware", +- size: 0x00080000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "kernel", +- size: 0x00100000, +- offset: 0x00080000, +- }, { +- name: "initrd", +- size: 0x00280000, +- offset: 0x00180000, +- }, { +- name: "initrd-test", +- size: 0x03C00000, +- offset: 0x00400000, ++ .name = "boot firmware", ++ .size = 0x00080000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = 0x00080000, ++ }, { ++ .name = "initrd", ++ .size = 0x00280000, ++ .offset = 0x00180000, ++ }, { ++ .name = "initrd-test", ++ .size = 0x03C00000, ++ .offset = 0x00400000, + } + }; + #endif + + #ifdef CONFIG_SA1100_PT_SYSTEM3 + /* erase size is 0x40000 == 256k partitions have to have this boundary */ +-#define SYSTEM3_FLASH_SIZE 0x01000000 + static struct mtd_partition system3_partitions[] = { + { +- name: "BLOB", +- size: 0x00040000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "config", +- size: 0x00040000, +- offset: MTDPART_OFS_APPEND, +- }, { +- name: "kernel", +- size: 0x00100000, +- offset: MTDPART_OFS_APPEND, +- }, { +- name: "root", +- size: MTDPART_SIZ_FULL, +- offset: MTDPART_OFS_APPEND, ++ .name = "BLOB", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "config", ++ .size = 0x00040000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "root", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_SHANNON +-#define SHANNON_FLASH_SIZE 0x00400000 + static struct mtd_partition shannon_partitions[] = { + { +- name: "BLOB boot loader", +- offset: 0, +- size: 0x20000 ++ .name = "BLOB boot loader", ++ .offset = 0, ++ .size = 0x20000 + }, + { +- name: "kernel", +- offset: MTDPART_OFS_APPEND, +- size: 0xe0000 ++ .name = "kernel", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 0xe0000 + }, + { +- name: "initrd", +- offset: MTDPART_OFS_APPEND, +- size: MTDPART_SIZ_FULL ++ .name = "initrd", ++ .offset = MTDPART_OFS_APPEND, ++ .size = MTDPART_SIZ_FULL + } + }; + + #endif + + #ifdef CONFIG_SA1100_SHERMAN +-#define SHERMAN_FLASH_SIZE 0x02000000 + static struct mtd_partition sherman_partitions[] = { + { +- size: 0x50000, +- offset: 0, ++ .size = 0x50000, ++ .offset = 0, + }, { +- size: 0x70000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x70000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: 0x600000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0x600000, ++ .offset = MTDPART_OFS_APPEND, + }, { +- size: 0xA0000, +- offset: MTDPART_OFS_APPEND, ++ .size = 0xA0000, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_SIMPAD +-#define SIMPAD_FLASH_SIZE 0x02000000 + static struct mtd_partition simpad_partitions[] = { + { +- name: "SIMpad boot firmware", +- size: 0x00080000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "SIMpad kernel", +- size: 0x00100000, +- offset: 0x00080000, +- }, { +-#ifdef CONFIG_JFFS2_FS +- name: "SIMpad root jffs2", +- size: MTDPART_SIZ_FULL, +- offset: 0x00180000, ++ .name = "SIMpad boot firmware", ++ .size = 0x00080000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "SIMpad kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++#ifdef CONFIG_ROOT_CRAMFS ++ .name = "SIMpad root cramfs", ++ .size =0x00D80000, ++ .offset = MTDPART_OFS_APPEND ++ ++ }, { ++ .name = "SIMpad local jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND + #else +- name: "SIMpad initrd", +- size: 0x00300000, +- offset: 0x00180000, +- }, { +- name: "SIMpad root cramfs", +- size: 0x00300000, +- offset: 0x00480000, +- }, { +- name: "SIMpad usr cramfs", +- size: 0x005c0000, +- offset: 0x00780000, +- }, { +- name: "SIMpad usr local", +- size: MTDPART_SIZ_FULL, +- offset: 0x00d40000, ++ .name = "SIMpad root jffs2", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND + #endif + } + }; + #endif /* CONFIG_SA1100_SIMPAD */ + + #ifdef CONFIG_SA1100_STORK +-#define STORK_FLASH_SIZE 0x02000000 + static struct mtd_partition stork_partitions[] = { + { +- name: "STORK boot firmware", +- size: 0x00040000, +- offset: 0, +- mask_flags: MTD_WRITEABLE, /* force read-only */ +- }, { +- name: "STORK params", +- size: 0x00040000, +- offset: 0x00040000, +- }, { +- name: "STORK kernel", +- size: 0x00100000, +- offset: 0x00080000, ++ .name = "STORK boot firmware", ++ .size = 0x00040000, ++ .offset = 0, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, { ++ .name = "STORK params", ++ .size = 0x00040000, ++ .offset = 0x00040000, ++ }, { ++ .name = "STORK kernel", ++ .size = 0x00100000, ++ .offset = 0x00080000, + }, { + #ifdef CONFIG_JFFS2_FS +- name: "STORK root jffs2", +- offset: 0x00180000, +- size: MTDPART_SIZ_FULL, ++ .name = "STORK root jffs2", ++ .offset = 0x00180000, ++ .size = MTDPART_SIZ_FULL, + #else +- name: "STORK initrd", +- size: 0x00100000, +- offset: 0x00180000, +- }, { +- name: "STORK root cramfs", +- size: 0x00300000, +- offset: 0x00280000, +- }, { +- name: "STORK usr cramfs", +- size: 0x00800000, +- offset: 0x00580000, +- }, { +- name: "STORK usr local", +- offset: 0x00d80000, +- size: MTDPART_SIZ_FULL, ++ .name = "STORK initrd", ++ .size = 0x00100000, ++ .offset = 0x00180000, ++ }, { ++ .name = "STORK root cramfs", ++ .size = 0x00300000, ++ .offset = 0x00280000, ++ }, { ++ .name = "STORK usr cramfs", ++ .size = 0x00800000, ++ .offset = 0x00580000, ++ }, { ++ .name = "STORK usr local", ++ .offset = 0x00d80000, ++ .size = MTDPART_SIZ_FULL, ++#endif ++ } ++}; + #endif ++ ++#ifdef CONFIG_SA1100_TRIZEPS ++static struct mtd_partition trizeps_partitions[] = { ++ { ++ .name = "Bootloader", ++ .size = 0x00100000, ++ .offset = 0, ++ }, { ++ .name = "Kernel", ++ .size = 0x00100000, ++ .offset = MTDPART_OFS_APPEND, ++ }, { ++ .name = "root", ++ .size = MTDPART_SIZ_FULL, ++ .offset = MTDPART_OFS_APPEND, + } + }; + #endif + + #ifdef CONFIG_SA1100_YOPY +-#define YOPY_FLASH_SIZE 0x08000000 + static struct mtd_partition yopy_partitions[] = { + { +- name: "boot firmware", +- size: 0x00040000, +- offset: 0x00000000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "boot firmware", ++ .size = 0x00040000, ++ .offset = 0x00000000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { +- name: "kernel", +- size: 0x00080000, +- offset: 0x00080000, ++ .name = "kernel", ++ .size = 0x00080000, ++ .offset = 0x00080000, + }, { +- name: "initrd", +- size: 0x00300000, +- offset: 0x00100000, ++ .name = "initrd", ++ .size = 0x00300000, ++ .offset = 0x00100000, + }, { +- name: "root", +- size: 0x01000000, +- offset: 0x00400000, ++ .name = "root", ++ .size = 0x01000000, ++ .offset = 0x00400000, + } + }; + #endif + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +-extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); +- +-static struct mtd_partition *parsed_parts; +-static struct mtd_info *mymtd; +- +-int __init sa1100_mtd_init(void) ++static int __init sa1100_static_partitions(struct mtd_partition **parts) + { +- struct mtd_partition *parts; +- int nb_parts = 0, ret; +- int parsed_nr_parts = 0; +- const char *part_type; +- unsigned long base = -1UL; +- +- /* Default flash buswidth */ +- sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4; +- +- /* +- * Static partition definition selection +- */ +- part_type = "static"; ++ int nb_parts = 0; + + #ifdef CONFIG_SA1100_ADSBITSY + if (machine_is_adsbitsy()) { +- parts = adsbitsy_partitions; ++ *parts = adsbitsy_partitions; + nb_parts = ARRAY_SIZE(adsbitsy_partitions); +- sa1100_map.size = ADSBITSY_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2 : 4; + } + #endif + #ifdef CONFIG_SA1100_ASSABET + if (machine_is_assabet()) { +- parts = assabet_partitions; ++ *parts = assabet_partitions; + nb_parts = ARRAY_SIZE(assabet_partitions); +- sa1100_map.size = ASSABET_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4()) { +- parts = badge4_partitions; ++ *parts = badge4_partitions; + nb_parts = ARRAY_SIZE(badge4_partitions); +- sa1100_map.size = BADGE4_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_CERF + if (machine_is_cerf()) { +- parts = cerf_partitions; ++ *parts = cerf_partitions; + nb_parts = ARRAY_SIZE(cerf_partitions); +- sa1100_map.size = CERF_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_CONSUS + if (machine_is_consus()) { +- parts = consus_partitions; ++ *parts = consus_partitions; + nb_parts = ARRAY_SIZE(consus_partitions); +- sa1100_map.size = CONSUS_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_FLEXANET + if (machine_is_flexanet()) { +- parts = flexanet_partitions; ++ *parts = flexanet_partitions; + nb_parts = ARRAY_SIZE(flexanet_partitions); +- sa1100_map.size = FLEXANET_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_FREEBIRD + if (machine_is_freebird()) { +- parts = freebird_partitions; ++ *parts = freebird_partitions; + nb_parts = ARRAY_SIZE(freebird_partitions); +- sa1100_map.size = FREEBIRD_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_FRODO + if (machine_is_frodo()) { +- parts = frodo_partitions; ++ *parts = frodo_partitions; + nb_parts = ARRAY_SIZE(frodo_partitions); +- sa1100_map.size = FRODO_FLASH_SIZE; +- base = 0x00000000; + } + #endif + #ifdef CONFIG_SA1100_GRAPHICSCLIENT + if (machine_is_graphicsclient()) { +- parts = graphicsclient_partitions; ++ *parts = graphicsclient_partitions; + nb_parts = ARRAY_SIZE(graphicsclient_partitions); +- sa1100_map.size = GRAPHICSCLIENT_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + } + #endif + #ifdef CONFIG_SA1100_GRAPHICSMASTER + if (machine_is_graphicsmaster()) { +- parts = graphicsmaster_partitions; ++ *parts = graphicsmaster_partitions; + nb_parts = ARRAY_SIZE(graphicsmaster_partitions); +- sa1100_map.size = GRAPHICSMASTER_FLASH_SIZE; +- sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + } + #endif +-#ifdef CONFIG_SA1100_H3600 +- if (machine_is_h3600()) { +- parts = h3600_partitions; +- nb_parts = ARRAY_SIZE(h3600_partitions); +- sa1100_map.size = H3600_FLASH_SIZE; +- sa1100_map.set_vpp = h3600_set_vpp; ++#ifdef CONFIG_SA1100_H3XXX ++ if (machine_is_h3xxx()) { ++ *parts = h3xxx_partitions; ++ nb_parts = ARRAY_SIZE(h3xxx_partitions); ++ } ++#endif ++#ifdef CONFIG_SA1100_HACKKIT ++ if (machine_is_hackkit()) { ++ *parts = hackkit_partitions; ++ nb_parts = ARRAY_SIZE(hackkit_partitions); + } + #endif + #ifdef CONFIG_SA1100_HUW_WEBPANEL + if (machine_is_huw_webpanel()) { +- parts = huw_webpanel_partitions; ++ *parts = huw_webpanel_partitions; + nb_parts = ARRAY_SIZE(huw_webpanel_partitions); +- sa1100_map.size = HUW_WEBPANEL_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_JORNADA720 + if (machine_is_jornada720()) { +- parts = jornada720_partitions; ++ *parts = jornada720_partitions; + nb_parts = ARRAY_SIZE(jornada720_partitions); +- sa1100_map.size = JORNADA720_FLASH_SIZE; +- sa1100_map.set_vpp = jornada720_set_vpp; + } + #endif + #ifdef CONFIG_SA1100_PANGOLIN + if (machine_is_pangolin()) { +- parts = pangolin_partitions; ++ *parts = pangolin_partitions; + nb_parts = ARRAY_SIZE(pangolin_partitions); +- sa1100_map.size = PANGOLIN_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_PT_SYSTEM3 + if (machine_is_pt_system3()) { +- parts = system3_partitions; ++ *parts = system3_partitions; + nb_parts = ARRAY_SIZE(system3_partitions); +- sa1100_map.size = SYSTEM3_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_SHANNON + if (machine_is_shannon()) { +- parts = shannon_partitions; ++ *parts = shannon_partitions; + nb_parts = ARRAY_SIZE(shannon_partitions); +- sa1100_map.size = SHANNON_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_SHERMAN + if (machine_is_sherman()) { +- parts = sherman_partitions; ++ *parts = sherman_partitions; + nb_parts = ARRAY_SIZE(sherman_partitions); +- sa1100_map.size = SHERMAN_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_SIMPAD + if (machine_is_simpad()) { +- parts = simpad_partitions; ++ *parts = simpad_partitions; + nb_parts = ARRAY_SIZE(simpad_partitions); +- sa1100_map.size = SIMPAD_FLASH_SIZE; + } + #endif + #ifdef CONFIG_SA1100_STORK + if (machine_is_stork()) { +- parts = stork_partitions; ++ *parts = stork_partitions; + nb_parts = ARRAY_SIZE(stork_partitions); +- sa1100_map.size = STORK_FLASH_SIZE; ++ } ++#endif ++#ifdef CONFIG_SA1100_TRIZEPS ++ if (machine_is_trizeps()) { ++ *parts = trizeps_partitions; ++ nb_parts = ARRAY_SIZE(trizeps_partitions); + } + #endif + #ifdef CONFIG_SA1100_YOPY + if (machine_is_yopy()) { +- parts = yopy_partitions; ++ *parts = yopy_partitions; + nb_parts = ARRAY_SIZE(yopy_partitions); +- sa1100_map.size = YOPY_FLASH_SIZE; + } + #endif + ++ return nb_parts; ++} ++#endif ++ ++struct sa_info { ++ unsigned long base; ++ unsigned long size; ++ int width; ++ void *vbase; ++ void (*set_vpp)(struct map_info *, int); ++ struct map_info *map; ++ struct mtd_info *mtd; ++ struct resource *res; ++}; ++ ++#define NR_SUBMTD 4 ++ ++static struct sa_info info[NR_SUBMTD]; ++ ++static int __init sa1100_setup_mtd(struct sa_info *sa, int nr, struct mtd_info **rmtd) ++{ ++ struct mtd_info *subdev[nr]; ++ struct map_info *maps; ++ int i, found = 0, ret = 0; ++ + /* +- * For simple flash devices, use ioremap to map the flash. ++ * Allocate the map_info structs in one go. + */ +- if (base != (unsigned long)-1) { +- if (!request_mem_region(base, sa1100_map.size, "flash")) +- return -EBUSY; +- sa1100_map.map_priv_2 = base; +- sa1100_map.map_priv_1 = (unsigned long) +- ioremap(base, sa1100_map.size); ++ maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); ++ if (!maps) ++ return -ENOMEM; ++ ++ memset(maps, 0, sizeof(struct map_info) * nr); ++ ++ /* ++ * Claim and then map the memory regions. ++ */ ++ for (i = 0; i < nr; i++) { ++ if (sa[i].base == (unsigned long)-1) ++ break; ++ ++ sa[i].res = request_mem_region(sa[i].base, sa[i].size, "sa1100 flash"); ++ if (!sa[i].res) { ++ ret = -EBUSY; ++ break; ++ } ++ ++ sa[i].map = maps + i; ++ ++ sa[i].vbase = ioremap(sa[i].base, sa[i].size); ++ if (!sa[i].vbase) { + ret = -ENOMEM; +- if (!sa1100_map.map_priv_1) +- goto out_err; ++ break; + } + ++ sa[i].map->virt = (unsigned long)sa[i].vbase; ++ sa[i].map->phys = sa[i].base; ++ sa[i].map->set_vpp = sa[i].set_vpp; ++ sa[i].map->buswidth = sa[i].width; ++ sa[i].map->size = sa[i].size; ++ ++ simple_map_init(sa[i].map); ++ + /* + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. + */ +- printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); +- mymtd = do_map_probe("cfi_probe", &sa1100_map); ++ sa[i].mtd = do_map_probe("cfi_probe", sa[i].map); ++ if (sa[i].mtd == NULL) { + ret = -ENXIO; +- if (!mymtd) +- goto out_err; +- mymtd->module = THIS_MODULE; ++ break; ++ } ++ sa[i].mtd->owner = THIS_MODULE; ++ subdev[i] = sa[i].mtd; ++ ++ printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %dMiB, " ++ "%d-bit\n", sa[i].base, sa[i].mtd->size >> 20, ++ sa[i].width * 8); ++ found += 1; ++ } + + /* +- * Dynamic partition selection stuff (might override the static ones) ++ * ENXIO is special. It means we didn't find a chip when ++ * we probed. We need to tear down the mapping, free the ++ * resource and mark it as such. + */ +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- if (parsed_nr_parts == 0) { +- int ret = parse_redboot_partitions(mymtd, &parsed_parts); ++ if (ret == -ENXIO) { ++ iounmap(sa[i].vbase); ++ sa[i].vbase = NULL; ++ release_resource(sa[i].res); ++ sa[i].res = NULL; ++ } + +- if (ret > 0) { +- part_type = "RedBoot"; +- parsed_nr_parts = ret; ++ /* ++ * If we found one device, don't bother with concat support. ++ * If we found multiple devices, use concat if we have it ++ * available, otherwise fail. ++ */ ++ if (ret == 0 || ret == -ENXIO) { ++ if (found == 1) { ++ *rmtd = subdev[0]; ++ ret = 0; ++ } else if (found > 1) { ++ /* ++ * We detected multiple devices. Concatenate ++ * them together. ++ */ ++#ifdef CONFIG_MTD_CONCAT ++ *rmtd = mtd_concat_create(subdev, found, ++ "sa1100 flash"); ++ if (*rmtd == NULL) ++ ret = -ENXIO; ++#else ++ printk(KERN_ERR "SA1100 flash: multiple devices " ++ "found but MTD concat support disabled.\n"); ++ ret = -ENXIO; ++#endif + } + } ++ ++ /* ++ * If we failed, clean up. ++ */ ++ if (ret) { ++ do { ++ if (sa[i].mtd) ++ map_destroy(sa[i].mtd); ++ if (sa[i].vbase) ++ iounmap(sa[i].vbase); ++ if (sa[i].res) ++ release_resource(sa[i].res); ++ } while (i--); ++ ++ kfree(maps); ++ } ++ ++ return ret; ++} ++ ++static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd) ++{ ++ int i; ++ ++ del_mtd_partitions(mtd); ++ ++#ifdef CONFIG_MTD_CONCAT ++ if (mtd != sa[0].mtd) ++ mtd_concat_destroy(mtd); + #endif +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- if (parsed_nr_parts == 0) { +- int ret = parse_cmdline_partitions(mymtd, &parsed_parts, "sa1100"); +- if (ret > 0) { +- part_type = "Command Line"; +- parsed_nr_parts = ret; ++ ++ for (i = NR_SUBMTD; i >= 0; i--) { ++ if (sa[i].mtd) ++ map_destroy(sa[i].mtd); ++ if (sa[i].vbase) ++ iounmap(sa[i].vbase); ++ if (sa[i].res) ++ release_resource(sa[i].res); + } ++ kfree(sa[0].map); ++} ++ ++/* ++ * A Thought: can we automatically detect the flash? ++ * - Check to see if the region is busy (yes -> failure) ++ * - Is the MSC setup for flash (no -> failure) ++ * - Probe for flash ++ */ ++ ++static struct map_info sa1100_probe_map __initdata = { ++ .name = "SA1100-flash", ++}; ++ ++static void __init sa1100_probe_one_cs(unsigned int msc, unsigned long phys) ++{ ++ struct mtd_info *mtd; ++ ++ printk(KERN_INFO "* Probing 0x%08lx: MSC = 0x%04x %d bit ", ++ phys, msc & 0xffff, msc & MSC_RBW ? 16 : 32); ++ ++ if (check_mem_region(phys, 0x08000000)) { ++ printk("busy\n"); ++ return; + } +-#endif + +- if (parsed_nr_parts > 0) { +- parts = parsed_parts; +- nb_parts = parsed_nr_parts; ++ if ((msc & 3) == 1) { ++ printk("wrong type\n"); ++ return; + } + +- if (nb_parts == 0) { +- printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n"); +- add_mtd_device(mymtd); +- } else { +- printk(KERN_NOTICE "Using %s partition definition\n", part_type); +- add_mtd_partitions(mymtd, parts, nb_parts); ++ sa1100_probe_map.buswidth = msc & MSC_RBW ? 2 : 4; ++ sa1100_probe_map.size = SZ_1M; ++ sa1100_probe_map.phys = phys; ++ sa1100_probe_map.virt = (unsigned long)ioremap(phys, SZ_1M); ++ if (sa1100_probe_map.virt == 0) ++ goto fail; ++ simple_map_init(&sa1100_probe_map); ++ ++ /* Shame cfi_probe blurts out kernel messages... */ ++ mtd = do_map_probe("cfi_probe", &sa1100_probe_map); ++ if (mtd) ++ map_destroy(mtd); ++ iounmap((void *)sa1100_probe_map.virt); ++ ++ if (!mtd) ++ goto fail; ++ ++ printk("pass\n"); ++ return; ++ ++ fail: ++ printk("failed\n"); ++} ++ ++static void __init sa1100_probe_flash(void) ++{ ++ printk(KERN_INFO "-- SA11xx Flash probe. Please report results.\n"); ++ sa1100_probe_one_cs(MSC0, SA1100_CS0_PHYS); ++ sa1100_probe_one_cs(MSC0 >> 16, SA1100_CS1_PHYS); ++ sa1100_probe_one_cs(MSC1, SA1100_CS2_PHYS); ++ sa1100_probe_one_cs(MSC1 >> 16, SA1100_CS3_PHYS); ++ sa1100_probe_one_cs(MSC2, SA1100_CS4_PHYS); ++ sa1100_probe_one_cs(MSC2 >> 16, SA1100_CS5_PHYS); ++ printk(KERN_INFO "-- SA11xx Flash probe complete.\n"); ++} ++ ++static int __init sa1100_locate_flash(void) ++{ ++ int i, nr = -ENODEV; ++ ++ sa1100_probe_flash(); ++ ++ if (machine_is_adsbitsy()) { ++ info[0].base = SA1100_CS1_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_assabet()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ info[1].base = SA1100_CS1_PHYS; /* neponset */ ++ info[1].size = SZ_32M; ++ nr = 2; ++ } ++ if (machine_is_badge4()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_64M; ++ nr = 1; ++ } ++ if (machine_is_cerf()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_consus()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_flexanet()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_freebird()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_frodo()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_graphicsclient()) { ++ info[0].base = SA1100_CS1_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; + } +- return 0; ++ if (machine_is_graphicsmaster()) { ++ info[0].base = SA1100_CS1_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_h3xxx()) { ++ info[0].set_vpp = h3xxx_set_vpp; ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_huw_webpanel()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_itsy()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_jornada720()) { ++ info[0].set_vpp = jornada720_set_vpp; ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_nanoengine()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[1].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_pangolin()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_64M; ++ nr = 1; ++ } ++ if (machine_is_pfs168()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_pleb()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_4M; ++ info[1].base = SA1100_CS1_PHYS; ++ info[1].size = SZ_4M; ++ nr = 2; ++ } ++ if (machine_is_pt_system3()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_shannon()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_4M; ++ nr = 1; ++ } ++ if (machine_is_sherman()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_simpad()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ info[1].base = SA1100_CS1_PHYS; ++ info[1].size = SZ_16M; ++ nr = 2; ++ } ++ if (machine_is_stork()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_32M; ++ nr = 1; ++ } ++ if (machine_is_trizeps()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_16M; ++ nr = 1; ++ } ++ if (machine_is_victor()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_2M; ++ nr = 1; ++ } ++ if (machine_is_yopy()) { ++ info[0].base = SA1100_CS0_PHYS; ++ info[0].size = SZ_64M; ++ info[1].base = SA1100_CS1_PHYS; ++ info[1].size = SZ_64M; ++ nr = 2; ++ } ++ ++ if (nr < 0) ++ return nr; + +- out_err: +- if (sa1100_map.map_priv_2 != -1) { +- iounmap((void *)sa1100_map.map_priv_1); +- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size); ++ /* ++ * Retrieve the buswidth from the MSC registers. ++ * We currently only implement CS0 and CS1 here. ++ */ ++ for (i = 0; i < nr; i++) { ++ switch (info[i].base) { ++ default: ++ printk(KERN_WARNING "SA1100 flash: unknown base address " ++ "0x%08lx, assuming CS0\n", info[i].base); ++ case SA1100_CS0_PHYS: ++ info[i].width = (MSC0 & MSC_RBW) ? 2 : 4; ++ break; ++ ++ case SA1100_CS1_PHYS: ++ info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; ++ break; + } +- return ret; ++ } ++ ++ return nr; + } + +-static void __exit sa1100_mtd_cleanup(void) ++static struct mtd_partition *parsed_parts; ++const char *part_probes[] = { "cmdlinepart", "RedBoot", NULL }; ++ ++static void __init sa1100_locate_partitions(struct mtd_info *mtd) + { +- if (mymtd) { +- del_mtd_partitions(mymtd); +- map_destroy(mymtd); +- if (parsed_parts) +- kfree(parsed_parts); ++ const char *part_type = NULL; ++ int nr_parts = 0; ++ ++ do { ++ /* ++ * Partition selection stuff. ++ */ ++#ifdef CONFIG_MTD_PARTITIONS ++ nr_parts = parse_mtd_partitions(mtd, part_probes, &parsed_parts, 0); ++ if (nr_parts > 0) { ++ part_type = "dynamic"; ++ break; + } +- if (sa1100_map.map_priv_2 != -1) { +- iounmap((void *)sa1100_map.map_priv_1); +- release_mem_region(sa1100_map.map_priv_2, sa1100_map.size); ++#endif ++#ifdef CONFIG_MTD_SA1100_STATICMAP ++ nr_parts = sa1100_static_partitions(&parsed_parts); ++ if (nr_parts > 0) { ++ part_type = "static"; ++ break; + } ++#endif ++ } while (0); ++ ++ if (nr_parts == 0) { ++ printk(KERN_NOTICE "SA1100 flash: no partition info " ++ "available, registering whole flash\n"); ++ add_mtd_device(mtd); ++ } else { ++ printk(KERN_NOTICE "SA1100 flash: using %s partition " ++ "definition\n", part_type); ++ add_mtd_partitions(mtd, parsed_parts, nr_parts); ++ } ++ ++ /* Always succeeds. */ ++} ++ ++static void __exit sa1100_destroy_partitions(void) ++{ ++ if (parsed_parts) ++ kfree(parsed_parts); ++} ++ ++static struct mtd_info *mymtd; ++ ++static int __init sa1100_mtd_init(void) ++{ ++ int ret; ++ int nr; ++ ++ nr = sa1100_locate_flash(); ++ if (nr < 0) ++ return nr; ++ ++ ret = sa1100_setup_mtd(info, nr, &mymtd); ++ if (ret == 0) ++ sa1100_locate_partitions(mymtd); ++ ++ return ret; ++} ++ ++static void __exit sa1100_mtd_cleanup(void) ++{ ++ sa1100_destroy_mtd(info, mymtd); ++ sa1100_destroy_partitions(); + } + + module_init(sa1100_mtd_init); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sbc8240.c linux/drivers/mtd/maps/sbc8240.c +--- linux-mips-2.4.27/drivers/mtd/maps/sbc8240.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/maps/sbc8240.c 2004-11-19 10:25:11.967189456 +0100 +@@ -0,0 +1,417 @@ ++/* ++ * Handle mapping of the flash memory access routines on the SBC8240 board. ++ * ++ * Carolyn Smith, Tektronix, Inc. ++ * ++ * This code is GPLed ++ * ++ * $Id: sbc8240.c,v 1.2 2003/09/30 19:37:00 thayne Exp $ ++ * ++ */ ++ ++/* ++ * The SBC8240 has 2 flash banks. ++ * Bank 0 is a 512 KiB AMD AM29F040B; 8 x 64 KiB sectors. ++ * It contains the U-Boot code (7 sectors) and the environment (1 sector). ++ * Bank 1 is 4 x 1 MiB AMD AM29LV800BT; 15 x 64 KiB sectors, 1 x 32 KiB sector, ++ * 2 x 8 KiB sectors, 1 x 16 KiB sectors. ++ * Both parts are JEDEC compatible. ++ */ ++ ++#include <linux/config.h> ++#include <linux/module.h> ++#include <linux/types.h> ++#include <linux/kernel.h> ++#include <asm/io.h> ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/cfi.h> ++ ++#ifdef CONFIG_MTD_PARTITIONS ++#include <linux/mtd/partitions.h> ++#endif ++ ++#define DEBUG ++ ++#ifdef DEBUG ++# define debugk(fmt,args...) printk(fmt ,##args) ++#else ++# define debugk(fmt,args...) ++#endif ++ ++ ++#define WINDOW_ADDR0 0xFFF00000 /* 512 KiB */ ++#define WINDOW_SIZE0 0x00080000 ++#define BUSWIDTH0 1 ++ ++#define WINDOW_ADDR1 0xFF000000 /* 4 MiB */ ++#define WINDOW_SIZE1 0x00400000 ++#define BUSWIDTH1 8 ++ ++#define MSG_PREFIX "sbc8240:" /* prefix for our printk()'s */ ++#define MTDID "sbc8240-%d" /* for mtdparts= partitioning */ ++ ++ ++static __u8 sbc8240_read8 (struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readb(map->map_priv_1 + ofs); ++} ++ ++static __u16 sbc8240_read16 (struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readw(map->map_priv_1 + ofs); ++} ++ ++static __u32 sbc8240_read32 (struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readl(map->map_priv_1 + ofs); ++} ++ ++unsigned long long result64; ++ ++static __u64 sbc8240_read64 (struct map_info *map, unsigned long ofs) ++{ ++ unsigned long flags, msr, saved_msr; ++ volatile long saved_fr[2]; ++ volatile unsigned long long result; ++ volatile unsigned long *p; ++ ++ save_flags(flags); ++ cli(); ++ ++ /* turn off floating point unavailable exceptions */ ++ ++ __asm__ __volatile__ ( ++ "mfmsr %0" ++ : "=r" (msr) :); ++ ++ saved_msr = msr; ++ msr |= MSR_FP; ++ msr &= ~(MSR_FE0 | MSR_FE1); ++ ++ __asm__ __volatile__ ( ++ "mtmsr %0\n" ++ "isync\n" ++ : : "r" (msr)); ++ ++ /* read the data via a floating point register */ ++ ++ ofs = map->map_priv_1 + ofs; ++ p = (unsigned long *) &result64; ++ ++ __asm__ __volatile__ ( ++ "lfd 1,0(%1)\n" ++ "stfd 1,0(%0)\n" ++ : : "r" (p), "r" (ofs) ++ ); ++ ++ /* restore state */ ++ ++ __asm__ __volatile__ ( ++ "mtmsr %0\n" ++ "isync\n" ++ : : "r" (saved_msr)); ++ ++ restore_flags(flags); ++ ++ p = (unsigned long *) &result64; ++ debugk("sbc8240_read64 ofs 0x%x result 0x%08x%08x\n", ofs, *p, *(p+1)); ++ ++ return result64; ++} ++ ++static void sbc8240_copy_from (struct map_info *map, ++ void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio (to, (void *) (map->map_priv_1 + from), len); ++} ++ ++static void sbc8240_write8 (struct map_info *map, __u8 d, unsigned long adr) ++{ ++ __raw_writeb(d, map->map_priv_1 + adr); ++ mb(); ++} ++ ++static void sbc8240_write16 (struct map_info *map, __u16 d, ++ unsigned long adr) ++{ ++ __raw_writew(d, map->map_priv_1 + adr); ++ mb(); ++} ++ ++static void sbc8240_write32 (struct map_info *map, __u32 d, ++ unsigned long adr) ++{ ++ __raw_writel(d, map->map_priv_1 + adr); ++ mb(); ++} ++ ++static void sbc8240_write64 (struct map_info *map, __u64 data, ++ unsigned long adr) ++{ ++ unsigned long long tmp; ++ unsigned long flags, msr, saved_msr, *p; ++ volatile long saved_fr[2]; ++ ++ save_flags(flags); ++ cli(); ++ ++ /* turn off floating point unavailable exceptions */ ++ ++ __asm__ __volatile__ ( ++ "mfmsr %0" ++ : "=r" (msr) :); ++ ++ saved_msr = msr; ++ msr |= MSR_FP; ++ msr &= ~(MSR_FE0 | MSR_FE1); ++ ++ __asm__ __volatile__ ( ++ "mtmsr %0\n" ++ "isync\n" ++ : : "r" (msr)); ++ ++ ++ /* write the data via a floating point register */ ++ ++ tmp = data; ++ p = (unsigned long *) &tmp; ++ adr = map->map_priv_1 + adr; ++ debugk("sbc8240_write64 adr 0x%x data 0x%08x%08x\n", adr, *p, *(p+1)); ++ ++ __asm__ __volatile__ ( ++ "stfd 1,0(%2)\n" ++ "lfd 1,0(%0)\n" ++ "stfd 1,0(%1)\n" ++ "lfd 1,0(%2)\n" ++ : : "r" (p), "r" (adr), "b" (saved_fr) ++ ); ++ ++ /* restore state */ ++ ++ __asm__ __volatile__ ( ++ "mtmsr %0\n" ++ "isync\n" ++ : : "r" (saved_msr)); ++ ++ restore_flags(flags); ++} ++ ++static void sbc8240_copy_to (struct map_info *map, ++ unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio ((void *) (map->map_priv_1 + to), from, len); ++} ++ ++static struct map_info sbc8240_map[2] = { ++ { ++ .name = "sbc8240 Flash Bank #0", ++ .size = WINDOW_SIZE0, ++ .buswidth = BUSWIDTH0, ++ .read8 = sbc8240_read8, ++ .read16 = sbc8240_read16, ++ .read32 = sbc8240_read32, ++ .read64 = sbc8240_read64, ++ .copy_from = sbc8240_copy_from, ++ .write8 = sbc8240_write8, ++ .write16 = sbc8240_write16, ++ .write32 = sbc8240_write32, ++ .write64 = sbc8240_write64, ++ .copy_to = sbc8240_copy_to ++ }, ++ { ++ .name = "sbc8240 Flash Bank #1", ++ .size = WINDOW_SIZE1, ++ .buswidth = BUSWIDTH1, ++ .read8 = sbc8240_read8, ++ .read16 = sbc8240_read16, ++ .read32 = sbc8240_read32, ++ .read64 = sbc8240_read64, ++ .copy_from = sbc8240_copy_from, ++ .write8 = sbc8240_write8, ++ .write16 = sbc8240_write16, ++ .write32 = sbc8240_write32, ++ .write64 = sbc8240_write64, ++ .copy_to = sbc8240_copy_to ++ } ++}; ++ ++#define NUM_FLASH_BANKS (sizeof(sbc8240_map) / sizeof(struct map_info)) ++ ++/* ++ * The following defines the partition layout of SBC8240 boards. ++ * ++ * See include/linux/mtd/partitions.h for definition of the ++ * mtd_partition structure. ++ * ++ * The *_max_flash_size is the maximum possible mapped flash size ++ * which is not necessarily the actual flash size. It must correspond ++ * to the value specified in the mapping definition defined by the ++ * "struct map_desc *_io_desc" for the corresponding machine. ++ */ ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ ++static struct mtd_partition sbc8240_uboot_partitions [] = { ++ /* Bank 0 */ ++ { ++ .name = "U-boot", /* U-Boot Firmware */ ++ .offset = 0, ++ .size = 0x00070000, /* 7 x 64 KiB sectors */ ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ ++ }, ++ { ++ .name = "environment", /* U-Boot environment */ ++ .offset = 0x00070000, ++ .size = 0x00010000, /* 1 x 64 KiB sector */ ++ }, ++}; ++ ++static struct mtd_partition sbc8240_fs_partitions [] = { ++ { ++ .name = "jffs", /* JFFS filesystem */ ++ .offset = 0, ++ .size = 0x003C0000, /* 4 * 15 * 64KiB */ ++ }, ++ { ++ .name = "tmp32", ++ .offset = 0x003C0000, ++ .size = 0x00020000, /* 4 * 32KiB */ ++ }, ++ { ++ .name = "tmp8a", ++ .offset = 0x003E0000, ++ .size = 0x00008000, /* 4 * 8KiB */ ++ }, ++ { ++ .name = "tmp8b", ++ .offset = 0x003E8000, ++ .size = 0x00008000, /* 4 * 8KiB */ ++ }, ++ { ++ .name = "tmp16", ++ .offset = 0x003F0000, ++ .size = 0x00010000, /* 4 * 16KiB */ ++ } ++}; ++ ++#define NB_OF(x) (sizeof (x) / sizeof (x[0])) ++ ++/* trivial struct to describe partition information */ ++struct mtd_part_def ++{ ++ int nums; ++ unsigned char *type; ++ struct mtd_partition* mtd_part; ++}; ++ ++static struct mtd_info *sbc8240_mtd[NUM_FLASH_BANKS]; ++static struct mtd_part_def sbc8240_part_banks[NUM_FLASH_BANKS]; ++ ++ ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++ ++int __init init_sbc8240_mtd (void) ++{ ++ static struct _cjs { ++ u_long addr; ++ u_long size; ++ } pt[NUM_FLASH_BANKS] = { ++ { ++ .addr = WINDOW_ADDR0, ++ .size = WINDOW_SIZE0 ++ }, ++ { ++ .addr = WINDOW_ADDR1, ++ .size = WINDOW_SIZE1 ++ }, ++ }; ++ ++ int devicesfound = 0; ++ int i; ++ ++ for (i = 0; i < NUM_FLASH_BANKS; i++) { ++ printk (KERN_NOTICE MSG_PREFIX ++ "Probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr); ++ ++ sbc8240_map[i].map_priv_1 = ++ (unsigned long) ioremap (pt[i].addr, pt[i].size); ++ if (!sbc8240_map[i].map_priv_1) { ++ printk (MSG_PREFIX "failed to ioremap\n"); ++ return -EIO; ++ } ++ ++ sbc8240_mtd[i] = do_map_probe("jedec_probe", &sbc8240_map[i]); ++ ++ if (sbc8240_mtd[i]) { ++ sbc8240_mtd[i]->module = THIS_MODULE; ++ devicesfound++; ++ } ++ } ++ ++ if (!devicesfound) { ++ printk(KERN_NOTICE MSG_PREFIX ++ "No suppported flash chips found!\n"); ++ return -ENXIO; ++ } ++ ++#ifdef CONFIG_MTD_PARTITIONS ++ sbc8240_part_banks[0].mtd_part = sbc8240_uboot_partitions; ++ sbc8240_part_banks[0].type = "static image"; ++ sbc8240_part_banks[0].nums = NB_OF(sbc8240_uboot_partitions); ++ sbc8240_part_banks[1].mtd_part = sbc8240_fs_partitions; ++ sbc8240_part_banks[1].type = "static file system"; ++ sbc8240_part_banks[1].nums = NB_OF(sbc8240_fs_partitions); ++ ++ for (i = 0; i < NUM_FLASH_BANKS; i++) { ++ ++ if (!sbc8240_mtd[i]) continue; ++ if (sbc8240_part_banks[i].nums == 0) { ++ printk (KERN_NOTICE MSG_PREFIX ++ "No partition info available, registering whole device\n"); ++ add_mtd_device(sbc8240_mtd[i]); ++ } else { ++ printk (KERN_NOTICE MSG_PREFIX ++ "Using %s partition definition\n", sbc8240_part_banks[i].mtd_part->name); ++ add_mtd_partitions (sbc8240_mtd[i], ++ sbc8240_part_banks[i].mtd_part, ++ sbc8240_part_banks[i].nums); ++ } ++ } ++#else ++ printk(KERN_NOTICE MSG_PREFIX ++ "Registering %d flash banks at once\n", devicesfound); ++ ++ for (i = 0; i < devicesfound; i++) { ++ add_mtd_device(sbc8240_mtd[i]); ++ } ++#endif /* CONFIG_MTD_PARTITIONS */ ++ ++ return devicesfound == 0 ? -ENXIO : 0; ++} ++ ++static void __exit cleanup_sbc8240_mtd (void) ++{ ++ int i; ++ ++ for (i = 0; i < NUM_FLASH_BANKS; i++) { ++ if (sbc8240_mtd[i]) { ++ del_mtd_device (sbc8240_mtd[i]); ++ map_destroy (sbc8240_mtd[i]); ++ } ++ if (sbc8240_map[i].map_priv_1) { ++ iounmap ((void *) sbc8240_map[i].map_priv_1); ++ sbc8240_map[i].map_priv_1 = 0; ++ } ++ } ++} ++ ++module_init (init_sbc8240_mtd); ++module_exit (cleanup_sbc8240_mtd); ++ ++MODULE_LICENSE ("GPL"); ++MODULE_AUTHOR ("Carolyn Smith <carolyn.smith@tektronix.com>"); ++MODULE_DESCRIPTION ("MTD map driver for SBC8240 boards"); ++ +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sbc_gxx.c linux/drivers/mtd/maps/sbc_gxx.c +--- linux-mips-2.4.27/drivers/mtd/maps/sbc_gxx.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/sbc_gxx.c 2004-11-19 10:25:11.969189152 +0100 +@@ -17,7 +17,7 @@ + along with this program; if not, write to the Free Software + Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + +- $Id: sbc_gxx.c,v 1.21 2003/01/24 13:40:14 dwmw2 Exp $ ++ $Id: sbc_gxx.c,v 1.26 2003/05/26 08:50:36 dwmw2 Exp $ + + The SBC-MediaGX / SBC-GXx has up to 16 MiB of + Intel StrataFlash (28F320/28F640) in x8 mode. +@@ -91,14 +91,14 @@ + * single flash device into. If the size if zero we use up to the end of the + * device. */ + static struct mtd_partition partition_info[]={ +- { name: "SBC-GXx flash boot partition", +- offset: 0, +- size: BOOT_PARTITION_SIZE_KiB*1024 }, +- { name: "SBC-GXx flash data partition", +- offset: BOOT_PARTITION_SIZE_KiB*1024, +- size: (DATA_PARTITION_SIZE_KiB)*1024 }, +- { name: "SBC-GXx flash application partition", +- offset: (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 } ++ { .name = "SBC-GXx flash boot partition", ++ .offset = 0, ++ .size = BOOT_PARTITION_SIZE_KiB*1024 }, ++ { .name = "SBC-GXx flash data partition", ++ .offset = BOOT_PARTITION_SIZE_KiB*1024, ++ .size = (DATA_PARTITION_SIZE_KiB)*1024 }, ++ { .name = "SBC-GXx flash application partition", ++ .offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 } + }; + + #define NUM_PARTITIONS 3 +@@ -203,19 +203,20 @@ + } + + static struct map_info sbc_gxx_map = { +- name: "SBC-GXx flash", +- size: MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount ++ .name = "SBC-GXx flash", ++ .phys = NO_XIP, ++ .size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount + of flash so the cfi probe routines find all + the chips */ +- buswidth: 1, +- read8: sbc_gxx_read8, +- read16: sbc_gxx_read16, +- read32: sbc_gxx_read32, +- copy_from: sbc_gxx_copy_from, +- write8: sbc_gxx_write8, +- write16: sbc_gxx_write16, +- write32: sbc_gxx_write32, +- copy_to: sbc_gxx_copy_to ++ .buswidth = 1, ++ .read8 = sbc_gxx_read8, ++ .read16 = sbc_gxx_read16, ++ .read32 = sbc_gxx_read32, ++ .copy_from = sbc_gxx_copy_from, ++ .write8 = sbc_gxx_write8, ++ .write16 = sbc_gxx_write16, ++ .write32 = sbc_gxx_write32, ++ .copy_to = sbc_gxx_copy_to + }; + + /* MTD device for all of the flash. */ +@@ -234,12 +235,6 @@ + + int __init init_sbc_gxx(void) + { +- if (check_region(PAGE_IO,PAGE_IO_SIZE) != 0) { +- printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", +- sbc_gxx_map.name, +- PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); +- return -EAGAIN; +- } + iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); + if (!iomapadr) { + printk( KERN_ERR"%s: failed to ioremap memory region\n", +@@ -247,7 +242,14 @@ + return -EIO; + } + +- request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash" ); ++ if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) { ++ printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n", ++ sbc_gxx_map.name, ++ PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 ); ++ iounmap((void *)iomapadr); ++ return -EAGAIN; ++ } ++ + + printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n", + sbc_gxx_map.name, +@@ -261,7 +263,7 @@ + return -ENXIO; + } + +- all_mtd->module=THIS_MODULE; ++ all_mtd->owner = THIS_MODULE; + + /* Create MTD devices for each partition. */ + add_mtd_partitions(all_mtd, partition_info, NUM_PARTITIONS ); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sc520cdp.c linux/drivers/mtd/maps/sc520cdp.c +--- linux-mips-2.4.27/drivers/mtd/maps/sc520cdp.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/sc520cdp.c 2004-11-19 10:25:11.970189000 +0100 +@@ -16,7 +16,7 @@ + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA + * +- * $Id: sc520cdp.c,v 1.11 2002/03/08 16:34:35 rkaiser Exp $ ++ * $Id: sc520cdp.c,v 1.15 2003/05/21 12:45:20 dwmw2 Exp $ + * + * + * The SC520CDP is an evaluation board for the Elan SC520 processor available +@@ -29,6 +29,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -84,88 +85,25 @@ + #define WINDOW_SIZE_1 0x00800000 + #define WINDOW_SIZE_2 0x00080000 + +-static __u8 sc520cdp_read8(struct map_info *map, unsigned long ofs) +-{ +- return readb(map->map_priv_1 + ofs); +-} +- +-static __u16 sc520cdp_read16(struct map_info *map, unsigned long ofs) +-{ +- return readw(map->map_priv_1 + ofs); +-} +- +-static __u32 sc520cdp_read32(struct map_info *map, unsigned long ofs) +-{ +- return readl(map->map_priv_1 + ofs); +-} +- +-static void sc520cdp_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-static void sc520cdp_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- writeb(d, map->map_priv_1 + adr); +-} +- +-static void sc520cdp_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- writew(d, map->map_priv_1 + adr); +-} +- +-static void sc520cdp_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- writel(d, map->map_priv_1 + adr); +-} +- +-static void sc520cdp_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} + + static struct map_info sc520cdp_map[] = { + { +- name: "SC520CDP Flash Bank #0", +- size: WINDOW_SIZE_0, +- buswidth: 4, +- read8: sc520cdp_read8, +- read16: sc520cdp_read16, +- read32: sc520cdp_read32, +- copy_from: sc520cdp_copy_from, +- write8: sc520cdp_write8, +- write16: sc520cdp_write16, +- write32: sc520cdp_write32, +- copy_to: sc520cdp_copy_to, +- map_priv_2: WINDOW_ADDR_0 ++ .name = "SC520CDP Flash Bank #0", ++ .size = WINDOW_SIZE_0, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR_0 + }, + { +- name: "SC520CDP Flash Bank #1", +- size: WINDOW_SIZE_1, +- buswidth: 4, +- read8: sc520cdp_read8, +- read16: sc520cdp_read16, +- read32: sc520cdp_read32, +- copy_from: sc520cdp_copy_from, +- write8: sc520cdp_write8, +- write16: sc520cdp_write16, +- write32: sc520cdp_write32, +- copy_to: sc520cdp_copy_to, +- map_priv_2: WINDOW_ADDR_1 ++ .name = "SC520CDP Flash Bank #1", ++ .size = WINDOW_SIZE_1, ++ .buswidth = 4, ++ .phys = WINDOW_ADDR_1 + }, + { +- name: "SC520CDP DIL Flash", +- size: WINDOW_SIZE_2, +- buswidth: 1, +- read8: sc520cdp_read8, +- read16: sc520cdp_read16, +- read32: sc520cdp_read32, +- copy_from: sc520cdp_copy_from, +- write8: sc520cdp_write8, +- write16: sc520cdp_write16, +- write32: sc520cdp_write32, +- copy_to: sc520cdp_copy_to, +- map_priv_2: WINDOW_ADDR_2 ++ .name = "SC520CDP DIL Flash", ++ .size = WINDOW_SIZE_2, ++ .buswidth = 1, ++ .phys = WINDOW_ADDR_2 + }, + }; + +@@ -255,9 +193,9 @@ + /* map in SC520's MMCR area */ + mmcr = (unsigned long *)ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT); + if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */ +- /* force map_priv_2 fields to BIOS defaults: */ ++ /* force physical address fields to BIOS defaults: */ + for(i = 0; i < NUM_FLASH_BANKS; i++) +- sc520cdp_map[i].map_priv_2 = par_table[i].default_address; ++ sc520cdp_map[i].phys = par_table[i].default_address; + return; + } + +@@ -282,7 +220,7 @@ + sc520cdp_map[i].name); + printk(KERN_NOTICE "Trying default address 0x%lx\n", + par_table[i].default_address); +- sc520cdp_map[i].map_priv_2 = par_table[i].default_address; ++ sc520cdp_map[i].phys = par_table[i].default_address; + } + } + iounmap((void *)mmcr); +@@ -300,13 +238,18 @@ + #endif + + for (i = 0; i < NUM_FLASH_BANKS; i++) { +- printk(KERN_NOTICE "SC520 CDP flash device: %lx at %lx\n", sc520cdp_map[i].size, sc520cdp_map[i].map_priv_2); +- sc520cdp_map[i].map_priv_1 = (unsigned long)ioremap_nocache(sc520cdp_map[i].map_priv_2, sc520cdp_map[i].size); ++ printk(KERN_NOTICE "SC520 CDP flash device: 0x%lx at 0x%lx\n", ++ sc520cdp_map[i].size, sc520cdp_map[i].phys); + +- if (!sc520cdp_map[i].map_priv_1) { ++ sc520cdp_map[i].virt = (unsigned long)ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size); ++ ++ if (!sc520cdp_map[i].virt) { + printk("Failed to ioremap_nocache\n"); + return -EIO; + } ++ ++ simple_map_init(&sc520cdp_map[i]); ++ + mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]); + if(!mymtd[i]) + mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]); +@@ -314,11 +257,11 @@ + mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]); + + if (mymtd[i]) { +- mymtd[i]->module = THIS_MODULE; ++ mymtd[i]->owner = THIS_MODULE; + ++devices_found; + } + else { +- iounmap((void *)sc520cdp_map[i].map_priv_1); ++ iounmap((void *)sc520cdp_map[i].virt); + } + } + if(devices_found >= 2) { +@@ -346,9 +289,9 @@ + for (i = 0; i < NUM_FLASH_BANKS; i++) { + if (mymtd[i]) + map_destroy(mymtd[i]); +- if (sc520cdp_map[i].map_priv_1) { +- iounmap((void *)sc520cdp_map[i].map_priv_1); +- sc520cdp_map[i].map_priv_1 = 0; ++ if (sc520cdp_map[i].virt) { ++ iounmap((void *)sc520cdp_map[i].virt); ++ sc520cdp_map[i].virt = 0; + } + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/scb2_flash.c linux/drivers/mtd/maps/scb2_flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/scb2_flash.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/scb2_flash.c 2004-11-19 10:25:11.972188696 +0100 +@@ -1,6 +1,6 @@ + /* + * MTD map driver for BIOS Flash on Intel SCB2 boards +- * $Id: scb2_flash.c,v 1.2 2003/01/24 13:09:56 dwmw2 Exp $ ++ * $Id: scb2_flash.c,v 1.6 2003/05/21 12:45:20 dwmw2 Exp $ + * Copyright (C) 2002 Sun Microsystems, Inc. + * Tim Hockin <thockin@sun.com> + * +@@ -14,7 +14,7 @@ + * try to request it here, but if it fails, we carry on anyway. + * + * This is how the chip is attached, so said the schematic: +- * * a 4 MiB (32 Mb) 16 bit chip ++ * * a 4 MiB (32 Mib) 16 bit chip + * * a 1 MiB memory region + * * A20 and A21 pulled up + * * D8-D15 ignored +@@ -48,6 +48,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -60,65 +61,13 @@ + #define SCB2_ADDR 0xfff00000 + #define SCB2_WINDOW 0x00100000 + +-static __u8 scb2_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 scb2_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static __u32 scb2_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-static void scb2_copy_from(struct map_info *map, void *to, +- unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void scb2_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scb2_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scb2_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scb2_copy_to(struct map_info *map, unsigned long to, +- const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + static void *scb2_ioaddr; + static struct mtd_info *scb2_mtd; + struct map_info scb2_map = { +- name: "SCB2 BIOS Flash", +- size: 0, +- buswidth: 1, +- read8: scb2_read8, +- read16: scb2_read16, +- read32: scb2_read32, +- copy_from: scb2_copy_from, +- write8: scb2_write8, +- write16: scb2_write16, +- write32: scb2_write32, +- copy_to: scb2_copy_to, ++ .name = "SCB2 BIOS Flash", ++ .size = 0, ++ .buswidth = 1, + }; + static int region_fail; + +@@ -137,6 +86,8 @@ + return -1; + } + ++ /* I wasn't here. I didn't see. dwmw2. */ ++ + /* the chip is sometimes bigger than the map - what a waste */ + mtd->size = map->size; + +@@ -211,9 +162,12 @@ + return -ENOMEM; + } + +- scb2_map.map_priv_1 = (unsigned long)scb2_ioaddr; ++ scb2_map.phys = SCB2_ADDR; ++ scb2_map.virt = (unsigned long)scb2_ioaddr; + scb2_map.size = SCB2_WINDOW; + ++ simple_map_init(&scb2_map); ++ + /* try to find a chip */ + scb2_mtd = do_map_probe("cfi_probe", &scb2_map); + +@@ -225,7 +179,7 @@ + return -ENODEV; + } + +- scb2_mtd->module = THIS_MODULE; ++ scb2_mtd->owner = THIS_MODULE; + if (scb2_fixup_mtd(scb2_mtd) < 0) { + del_mtd_device(scb2_mtd); + map_destroy(scb2_mtd); +@@ -235,7 +189,7 @@ + return -ENODEV; + } + +- printk(KERN_NOTICE MODNAME ": chip size %x at offset %x\n", ++ printk(KERN_NOTICE MODNAME ": chip size 0x%x at offset 0x%x\n", + scb2_mtd->size, SCB2_WINDOW - scb2_mtd->size); + + add_mtd_device(scb2_mtd); +@@ -266,19 +220,19 @@ + + static struct pci_device_id scb2_flash_pci_ids[] __devinitdata = { + { +- vendor: PCI_VENDOR_ID_SERVERWORKS, +- device: PCI_DEVICE_ID_SERVERWORKS_CSB5, +- subvendor: PCI_ANY_ID, +- subdevice: PCI_ANY_ID ++ .vendor = PCI_VENDOR_ID_SERVERWORKS, ++ .device = PCI_DEVICE_ID_SERVERWORKS_CSB5, ++ .subvendor = PCI_ANY_ID, ++ .subdevice = PCI_ANY_ID + }, + { 0, } + }; + + static struct pci_driver scb2_flash_driver = { +- name: "Intel SCB2 BIOS Flash", +- id_table: scb2_flash_pci_ids, +- probe: scb2_flash_probe, +- remove: __devexit_p(scb2_flash_remove), ++ .name = "Intel SCB2 BIOS Flash", ++ .id_table = scb2_flash_pci_ids, ++ .probe = scb2_flash_probe, ++ .remove = __devexit_p(scb2_flash_remove), + }; + + static int __init +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/scx200_docflash.c linux/drivers/mtd/maps/scx200_docflash.c +--- linux-mips-2.4.27/drivers/mtd/maps/scx200_docflash.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/scx200_docflash.c 2004-11-19 10:25:11.973188544 +0100 +@@ -2,7 +2,7 @@ + + Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> + +- $Id: scx200_docflash.c,v 1.1 2003/01/24 13:20:40 dwmw2 Exp $ ++ $Id: scx200_docflash.c,v 1.5 2003/05/21 12:45:20 dwmw2 Exp $ + + National Semiconductor SCx200 flash mapped with DOCCS + */ +@@ -11,6 +11,7 @@ + #include <linux/config.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -75,46 +76,9 @@ + #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + #endif + +-static __u8 scx200_docflash_read8(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readb(map->map_priv_1 + ofs); +-} +- +-static __u16 scx200_docflash_read16(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readw(map->map_priv_1 + ofs); +-} +- +-static void scx200_docflash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-static void scx200_docflash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scx200_docflash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +- mb(); +-} +- +-static void scx200_docflash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + static struct map_info scx200_docflash_map = { + .name = "NatSemi SCx200 DOCCS Flash", +- .read8 = scx200_docflash_read8, +- .read16 = scx200_docflash_read16, +- .copy_from = scx200_docflash_copy_from, +- .write8 = scx200_docflash_write8, +- .write16 = scx200_docflash_write16, +- .copy_to = scx200_docflash_copy_to + }; + + int __init init_scx200_docflash(void) +@@ -213,8 +177,11 @@ + else + scx200_docflash_map.buswidth = 2; + +- scx200_docflash_map.map_priv_1 = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size); +- if (!scx200_docflash_map.map_priv_1) { ++ simple_map_init(&scx200_docflash_map); ++ ++ scx200_docflash_map.phys = docmem.start; ++ scx200_docflash_map.virt = (unsigned long)ioremap(docmem.start, scx200_docflash_map.size); ++ if (!scx200_docflash_map.virt) { + printk(KERN_ERR NAME ": failed to ioremap the flash\n"); + release_resource(&docmem); + return -EIO; +@@ -223,7 +190,7 @@ + mymtd = do_map_probe(flashtype, &scx200_docflash_map); + if (!mymtd) { + printk(KERN_ERR NAME ": unable to detect flash\n"); +- iounmap((void *)scx200_docflash_map.map_priv_1); ++ iounmap((void *)scx200_docflash_map.virt); + release_resource(&docmem); + return -ENXIO; + } +@@ -231,7 +198,7 @@ + if (size < mymtd->size) + printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n"); + +- mymtd->module = THIS_MODULE; ++ mymtd->owner = THIS_MODULE; + + #if PARTITION + partition_info[3].offset = mymtd->size-partition_info[3].size; +@@ -253,8 +220,8 @@ + #endif + map_destroy(mymtd); + } +- if (scx200_docflash_map.map_priv_1) { +- iounmap((void *)scx200_docflash_map.map_priv_1); ++ if (scx200_docflash_map.virt) { ++ iounmap((void *)scx200_docflash_map.virt); + release_resource(&docmem); + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/solutionengine.c linux/drivers/mtd/maps/solutionengine.c +--- linux-mips-2.4.27/drivers/mtd/maps/solutionengine.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/solutionengine.c 2004-11-19 10:25:11.975188240 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: solutionengine.c,v 1.4 2001/11/07 01:20:59 jsiegel Exp $ ++ * $Id: solutionengine.c,v 1.10 2003/05/21 12:45:20 dwmw2 Exp $ + * + * Flash and EPROM on Hitachi Solution Engine and similar boards. + * +@@ -11,6 +11,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/map.h> +@@ -18,60 +19,39 @@ + #include <linux/config.h> + + +-extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); +- +-__u32 soleng_read32(struct map_info *map, unsigned long ofs) +-{ +- return __raw_readl(map->map_priv_1 + ofs); +-} +- +-void soleng_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +- mb(); +-} +- +-void soleng_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +- + static struct mtd_info *flash_mtd; + static struct mtd_info *eprom_mtd; + + static struct mtd_partition *parsed_parts; + + struct map_info soleng_eprom_map = { +- name: "Solution Engine EPROM", +- size: 0x400000, +- buswidth: 4, +- copy_from: soleng_copy_from, ++ .name = "Solution Engine EPROM", ++ .size = 0x400000, ++ .buswidth = 4, + }; + + struct map_info soleng_flash_map = { +- name: "Solution Engine FLASH", +- size: 0x400000, +- buswidth: 4, +- read32: soleng_read32, +- copy_from: soleng_copy_from, +- write32: soleng_write32, ++ .name = "Solution Engine FLASH", ++ .size = 0x400000, ++ .buswidth = 4, + }; + ++static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; ++ + #ifdef CONFIG_MTD_SUPERH_RESERVE + static struct mtd_partition superh_se_partitions[] = { + /* Reserved for boot code, read-only */ + { +- name: "flash_boot", +- offset: 0x00000000, +- size: CONFIG_MTD_SUPERH_RESERVE, +- mask_flags: MTD_WRITEABLE, ++ .name = "flash_boot", ++ .offset = 0x00000000, ++ .size = CONFIG_MTD_SUPERH_RESERVE, ++ .mask_flags = MTD_WRITEABLE, + }, + /* All else is writable (e.g. JFFS) */ + { +- name: "Flash FS", +- offset: MTDPART_OFS_NXTBLK, +- size: MTDPART_SIZ_FULL, ++ .name = "Flash FS", ++ .offset = MTDPART_OFS_NXTBLK, ++ .size = MTDPART_SIZ_FULL, + } + }; + #endif /* CONFIG_MTD_SUPERH_RESERVE */ +@@ -81,16 +61,22 @@ + int nr_parts = 0; + + /* First probe at offset 0 */ +- soleng_flash_map.map_priv_1 = P2SEGADDR(0); +- soleng_eprom_map.map_priv_1 = P1SEGADDR(0x01000000); ++ soleng_flash_map.phys = 0; ++ soleng_flash_map.virt = P2SEGADDR(0); ++ soleng_eprom_map.phys = 0x01000000; ++ soleng_eprom_map.virt = P1SEGADDR(0x01000000); ++ simple_map_init(&soleng_eprom_map); ++ simple_map_init(&soleng_flash_map); + + printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n"); + flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map); + if (!flash_mtd) { + /* Not there. Try swapping */ + printk(KERN_NOTICE "Probing for flash chips at 0x01000000:\n"); +- soleng_flash_map.map_priv_1 = P2SEGADDR(0x01000000); +- soleng_eprom_map.map_priv_1 = P1SEGADDR(0); ++ soleng_flash_map.phys = 0x01000000; ++ soleng_flash_map.virt = P2SEGADDR(0x01000000); ++ soleng_eprom_map.phys = 0; ++ soleng_eprom_map.virt = P1SEGADDR(0); + flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map); + if (!flash_mtd) { + /* Eep. */ +@@ -99,25 +85,20 @@ + } + } + printk(KERN_NOTICE "Solution Engine: Flash at 0x%08lx, EPROM at 0x%08lx\n", +- soleng_flash_map.map_priv_1 & 0x1fffffff, +- soleng_eprom_map.map_priv_1 & 0x1fffffff); +- flash_mtd->module = THIS_MODULE; ++ soleng_flash_map.phys & 0x1fffffff, ++ soleng_eprom_map.phys & 0x1fffffff); ++ flash_mtd->owner = THIS_MODULE; + + eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map); + if (eprom_mtd) { +- eprom_mtd->module = THIS_MODULE; ++ eprom_mtd->owner = THIS_MODULE; + add_mtd_device(eprom_mtd); + } + +-#ifdef CONFIG_MTD_REDBOOT_PARTS +- nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); +- if (nr_parts > 0) +- printk(KERN_NOTICE "Found RedBoot partition table.\n"); +- else if (nr_parts < 0) +- printk(KERN_NOTICE "Error looking for RedBoot partitions.\n"); +-#endif /* CONFIG_MTD_REDBOOT_PARTS */ ++ nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); ++ + #if CONFIG_MTD_SUPERH_RESERVE +- if (nr_parts == 0) { ++ if (nr_parts <= 0) { + printk(KERN_NOTICE "Using configured partition at 0x%08x.\n", + CONFIG_MTD_SUPERH_RESERVE); + parsed_parts = superh_se_partitions; +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/sun_uflash.c linux/drivers/mtd/maps/sun_uflash.c +--- linux-mips-2.4.27/drivers/mtd/maps/sun_uflash.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/sun_uflash.c 2004-11-19 10:25:11.976188088 +0100 +@@ -1,4 +1,4 @@ +-/* $Id: sun_uflash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $ ++/* $Id: sun_uflash.c,v 1.7 2003/05/20 20:59:32 dwmw2 Exp $ + * + * sun_uflash - Driver implementation for user-programmable flash + * present on many Sun Microsystems SME boardsets. +@@ -48,60 +48,11 @@ + struct list_head list; + }; + +-__u8 uflash_read8(struct map_info *map, unsigned long ofs) +-{ +- return(__raw_readb(map->map_priv_1 + ofs)); +-} +- +-__u16 uflash_read16(struct map_info *map, unsigned long ofs) +-{ +- return(__raw_readw(map->map_priv_1 + ofs)); +-} +- +-__u32 uflash_read32(struct map_info *map, unsigned long ofs) +-{ +- return(__raw_readl(map->map_priv_1 + ofs)); +-} +- +-void uflash_copy_from(struct map_info *map, void *to, unsigned long from, +- ssize_t len) +-{ +- memcpy_fromio(to, map->map_priv_1 + from, len); +-} +- +-void uflash_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- __raw_writeb(d, map->map_priv_1 + adr); +-} +- +-void uflash_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- __raw_writew(d, map->map_priv_1 + adr); +-} +- +-void uflash_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- __raw_writel(d, map->map_priv_1 + adr); +-} +- +-void uflash_copy_to(struct map_info *map, unsigned long to, const void *from, +- ssize_t len) +-{ +- memcpy_toio(map->map_priv_1 + to, from, len); +-} + + struct map_info uflash_map_templ = { +- name: "SUNW,???-????", +- size: UFLASH_WINDOW_SIZE, +- buswidth: UFLASH_BUSWIDTH, +- read8: uflash_read8, +- read16: uflash_read16, +- read32: uflash_read32, +- copy_from: uflash_copy_from, +- write8: uflash_write8, +- write16: uflash_write16, +- write32: uflash_write32, +- copy_to: uflash_copy_to ++ .name = "SUNW,???-????", ++ .size = UFLASH_WINDOW_SIZE, ++ .buswidth = UFLASH_BUSWIDTH, + }; + + int uflash_devinit(struct linux_ebus_device* edev) +@@ -145,20 +96,22 @@ + if(0 != pdev->name && 0 < strlen(pdev->name)) { + pdev->map.name = pdev->name; + } +- +- pdev->map.map_priv_1 = ++ pdev->phys = edev->resource[0].start; ++ pdev->virt = + (unsigned long)ioremap_nocache(edev->resource[0].start, pdev->map.size); +- if(0 == pdev->map.map_priv_1) { ++ if(0 == pdev->map.virt) { + printk("%s: failed to map device\n", __FUNCTION__); + kfree(pdev->name); + kfree(pdev); + return(-1); + } + ++ simple_map_init(&pdev->map); ++ + /* MTD registration */ + pdev->mtd = do_map_probe("cfi_probe", &pdev->map); + if(0 == pdev->mtd) { +- iounmap((void *)pdev->map.map_priv_1); ++ iounmap((void *)pdev->map.virt); + kfree(pdev->name); + kfree(pdev); + return(-ENXIO); +@@ -166,7 +119,7 @@ + + list_add(&pdev->list, &device_list); + +- pdev->mtd->module = THIS_MODULE; ++ pdev->mtd->owner = THIS_MODULE; + + add_mtd_device(pdev->mtd); + return(0); +@@ -211,9 +164,9 @@ + del_mtd_device(udev->mtd); + map_destroy(udev->mtd); + } +- if(0 != udev->map.map_priv_1) { +- iounmap((void*)udev->map.map_priv_1); +- udev->map.map_priv_1 = 0; ++ if(0 != udev->map.virt) { ++ iounmap((void*)udev->map.virt); ++ udev->map.virt = 0; + } + if(0 != udev->name) { + kfree(udev->name); +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/tqm8xxl.c linux/drivers/mtd/maps/tqm8xxl.c +--- linux-mips-2.4.27/drivers/mtd/maps/tqm8xxl.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/tqm8xxl.c 2004-11-19 10:25:11.978187784 +0100 +@@ -2,7 +2,7 @@ + * Handle mapping of the flash memory access routines + * on TQM8xxL based devices. + * +- * $Id: tqm8xxl.c,v 1.4 2002/06/20 13:41:20 mag Exp $ ++ * $Id: tqm8xxl.c,v 1.9 2003/06/23 11:48:18 dwmw2 Exp $ + * + * based on rpxlite.c + * +@@ -26,6 +26,7 @@ + #include <linux/module.h> + #include <linux/types.h> + #include <linux/kernel.h> ++#include <linux/init.h> + #include <asm/io.h> + + #include <linux/mtd/mtd.h> +@@ -51,46 +52,6 @@ + static unsigned long num_banks; + static unsigned long start_scan_addr; + +-__u8 tqm8xxl_read8(struct map_info *map, unsigned long ofs) +-{ +- return *((__u8 *)(map->map_priv_1 + ofs)); +-} +- +-__u16 tqm8xxl_read16(struct map_info *map, unsigned long ofs) +-{ +- return *((__u16 *)(map->map_priv_1 + ofs)); +-} +- +-__u32 tqm8xxl_read32(struct map_info *map, unsigned long ofs) +-{ +- return *((__u32 *)(map->map_priv_1 + ofs)); +-} +- +-void tqm8xxl_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void tqm8xxl_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *((__u8 *)(map->map_priv_1 + adr)) = d; +-} +- +-void tqm8xxl_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *((__u16 *)( map->map_priv_1 + adr)) = d; +-} +- +-void tqm8xxl_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *((__u32 *)(map->map_priv_1 + adr)) = d; +-} +- +-void tqm8xxl_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy_toio((void *)(map->map_priv_1 + to), from, len); +-} +- + /* + * Here are partition information for all known TQM8xxL series devices. + * See include/linux/mtd/partitions.h for definition of the mtd_partition +@@ -107,50 +68,48 @@ + static unsigned long tqm8xxl_max_flash_size = 0x00800000; + + /* partition definition for first flash bank +- * also ref. to "drivers\char\flash_config.c" ++ * (cf. "drivers/char/flash_config.c") + */ + static struct mtd_partition tqm8xxl_partitions[] = { + { +- name: "ppcboot", +- offset: 0x00000000, +- size: 0x00020000, /* 128KB */ +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "ppcboot", ++ .offset = 0x00000000, ++ .size = 0x00020000, /* 128KB */ ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "kernel", /* default kernel image */ +- offset: 0x00020000, +- size: 0x000e0000, +- mask_flags: MTD_WRITEABLE, /* force read-only */ ++ .name = "kernel", /* default kernel image */ ++ .offset = 0x00020000, ++ .size = 0x000e0000, ++ .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, + { +- name: "user", +- offset: 0x00100000, +- size: 0x00100000, ++ .name = "user", ++ .offset = 0x00100000, ++ .size = 0x00100000, + }, + { +- name: "initrd", +- offset: 0x00200000, +- size: 0x00200000, ++ .name = "initrd", ++ .offset = 0x00200000, ++ .size = 0x00200000, + } + }; +-/* partition definition for second flahs bank */ ++/* partition definition for second flash bank */ + static struct mtd_partition tqm8xxl_fs_partitions[] = { + { +- name: "cramfs", +- offset: 0x00000000, +- size: 0x00200000, ++ .name = "cramfs", ++ .offset = 0x00000000, ++ .size = 0x00200000, + }, + { +- name: "jffs", +- offset: 0x00200000, +- size: 0x00200000, +- //size: MTDPART_SIZ_FULL, ++ .name = "jffs", ++ .offset = 0x00200000, ++ .size = 0x00200000, ++ .//size = MTDPART_SIZ_FULL, + } + }; + #endif + +-#define NB_OF(x) (sizeof(x)/sizeof(x[0])) +- + int __init init_tqm_mtd(void) + { + int idx = 0, ret = 0; +@@ -160,67 +119,73 @@ + + flash_addr = bd->bi_flashstart; + flash_size = bd->bi_flashsize; +- //request maximum flash size address spzce ++ ++ //request maximum flash size address space + start_scan_addr = (unsigned long)ioremap(flash_addr, flash_size); + if (!start_scan_addr) { +- //printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, FLASH_ADDR); +- printk("%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); ++ printk(KERN_WARNING "%s:Failed to ioremap address:0x%x\n", __FUNCTION__, flash_addr); + return -EIO; + } +- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) +- { ++ ++ for (idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { + if(mtd_size >= flash_size) + break; + +- printk("%s: chip probing count %d\n", __FUNCTION__, idx); ++ printk(KERN_INFO "%s: chip probing count %d\n", __FUNCTION__, idx); + + map_banks[idx] = (struct map_info *)kmalloc(sizeof(struct map_info), GFP_KERNEL); +- if(map_banks[idx] == NULL) +- { +- //return -ENOMEM; ++ if(map_banks[idx] == NULL) { + ret = -ENOMEM; ++ /* FIXME: What if some MTD devices were probed already? */ + goto error_mem; + } ++ + memset((void *)map_banks[idx], 0, sizeof(struct map_info)); + map_banks[idx]->name = (char *)kmalloc(16, GFP_KERNEL); +- if(map_banks[idx]->name == NULL) +- { +- //return -ENOMEM; ++ ++ if (!map_banks[idx]->name) { + ret = -ENOMEM; ++ /* FIXME: What if some MTD devices were probed already? */ + goto error_mem; + } +- memset((void *)map_banks[idx]->name, 0, 16); +- + sprintf(map_banks[idx]->name, "TQM8xxL%d", idx); ++ + map_banks[idx]->size = flash_size; + map_banks[idx]->buswidth = 4; +- map_banks[idx]->read8 = tqm8xxl_read8; +- map_banks[idx]->read16 = tqm8xxl_read16; +- map_banks[idx]->read32 = tqm8xxl_read32; +- map_banks[idx]->copy_from = tqm8xxl_copy_from; +- map_banks[idx]->write8 = tqm8xxl_write8; +- map_banks[idx]->write16 = tqm8xxl_write16; +- map_banks[idx]->write32 = tqm8xxl_write32; +- map_banks[idx]->copy_to = tqm8xxl_copy_to; ++ ++ simple_map_init(map_banks[idx]); ++ ++ map_banks[idx]->virt = start_scan_addr; ++ map_banks[idx]->phys = flash_addr; ++ /* FIXME: This looks utterly bogus, but I'm trying to ++ preserve the behaviour of the original (shown here)... ++ + map_banks[idx]->map_priv_1 = + start_scan_addr + ((idx > 0) ? + (mtd_banks[idx-1] ? mtd_banks[idx-1]->size : 0) : 0); ++ */ ++ ++ if (idx && mtd_banks[idx-1]) { ++ map_banks[idx]->virt += mtd_banks[idx-1]->size; ++ map_banks[idx]->phys += mtd_banks[idx-1]->size; ++ } ++ + //start to probe flash chips + mtd_banks[idx] = do_map_probe("cfi_probe", map_banks[idx]); +- if(mtd_banks[idx]) +- { +- mtd_banks[idx]->module = THIS_MODULE; ++ ++ if (mtd_banks[idx]) { ++ mtd_banks[idx]->owner = THIS_MODULE; + mtd_size += mtd_banks[idx]->size; + num_banks++; +- printk("%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, ++ ++ printk(KERN_INFO "%s: bank%d, name:%s, size:%dbytes \n", __FUNCTION__, num_banks, + mtd_banks[idx]->name, mtd_banks[idx]->size); + } + } + + /* no supported flash chips found */ +- if(!num_banks) +- { +- printk("TQM8xxL: No support flash chips found!\n"); ++ if (!num_banks) { ++ printk(KERN_NOTICE "TQM8xxL: No support flash chips found!\n"); + ret = -ENXIO; + goto error_mem; + } +@@ -231,12 +196,13 @@ + */ + part_banks[0].mtd_part = tqm8xxl_partitions; + part_banks[0].type = "Static image"; +- part_banks[0].nums = NB_OF(tqm8xxl_partitions); ++ part_banks[0].nums = ARRAY_SIZE(tqm8xxl_partitions); ++ + part_banks[1].mtd_part = tqm8xxl_fs_partitions; + part_banks[1].type = "Static file system"; +- part_banks[1].nums = NB_OF(tqm8xxl_fs_partitions); +- for(idx = 0; idx < num_banks ; idx++) +- { ++ part_banks[1].nums = ARRAY_SIZE(tqm8xxl_fs_partitions); ++ ++ for(idx = 0; idx < num_banks ; idx++) { + if (part_banks[idx].nums == 0) { + printk(KERN_NOTICE "TQM flash%d: no partition info available, registering whole flash at once\n", idx); + add_mtd_device(mtd_banks[idx]); +@@ -254,12 +220,9 @@ + #endif + return 0; + error_mem: +- for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) +- { +- if(map_banks[idx] != NULL) +- { +- if(map_banks[idx]->name != NULL) +- { ++ for(idx = 0 ; idx < FLASH_BANK_MAX ; idx++) { ++ if(map_banks[idx] != NULL) { ++ if(map_banks[idx]->name != NULL) { + kfree(map_banks[idx]->name); + map_banks[idx]->name = NULL; + } +@@ -267,18 +230,15 @@ + map_banks[idx] = NULL; + } + } +- //return -ENOMEM; + error: + iounmap((void *)start_scan_addr); +- //return -ENXIO; + return ret; + } + + static void __exit cleanup_tqm_mtd(void) + { + unsigned int idx = 0; +- for(idx = 0 ; idx < num_banks ; idx++) +- { ++ for(idx = 0 ; idx < num_banks ; idx++) { + /* destroy mtd_info previously allocated */ + if (mtd_banks[idx]) { + del_mtd_partitions(mtd_banks[idx]); +@@ -288,6 +248,7 @@ + kfree(map_banks[idx]->name); + kfree(map_banks[idx]); + } ++ + if (start_scan_addr) { + iounmap((void *)start_scan_addr); + start_scan_addr = 0; +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/tsunami_flash.c linux/drivers/mtd/maps/tsunami_flash.c +--- linux-mips-2.4.27/drivers/mtd/maps/tsunami_flash.c 2002-06-27 00:35:50.000000000 +0200 ++++ linux/drivers/mtd/maps/tsunami_flash.c 2004-11-19 10:25:11.979187632 +0100 +@@ -2,11 +2,13 @@ + * tsunami_flash.c + * + * flash chip on alpha ds10... +- * $Id: tsunami_flash.c,v 1.1 2002/01/10 22:59:13 eric Exp $ ++ * $Id: tsunami_flash.c,v 1.6 2003/05/21 15:15:08 dwmw2 Exp $ + */ + #include <asm/io.h> + #include <asm/core_tsunami.h> ++#include <linux/init.h> + #include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> + + #define FLASH_ENABLE_PORT 0x00C00001 + #define FLASH_ENABLE_BYTE 0x01 +@@ -58,18 +60,12 @@ + static struct map_info tsunami_flash_map = { + .name = "flash chip on the Tsunami TIG bus", + .size = MAX_TIG_FLASH_SIZE, ++ .phys = NO_XIP; + .buswidth = 1, + .read8 = tsunami_flash_read8, +- .read16 = 0, +- .read32 = 0, + .copy_from = tsunami_flash_copy_from, + .write8 = tsunami_flash_write8, +- .write16 = 0, +- .write32 = 0, + .copy_to = tsunami_flash_copy_to, +- .set_vpp = 0, +- .map_priv_1 = 0, +- + }; + + static struct mtd_info *tsunami_flash_mtd; +@@ -99,7 +95,7 @@ + tsunami_flash_mtd = do_map_probe(*type, &tsunami_flash_map); + } + if (tsunami_flash_mtd) { +- tsunami_flash_mtd->module = THIS_MODULE; ++ tsunami_flash_mtd->owner = THIS_MODULE; + add_mtd_device(tsunami_flash_mtd); + return 0; + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/uclinux.c linux/drivers/mtd/maps/uclinux.c +--- linux-mips-2.4.27/drivers/mtd/maps/uclinux.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/maps/uclinux.c 2004-11-19 10:25:11.980187480 +0100 +@@ -5,7 +5,7 @@ + * + * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) + * +- * $Id: uclinux.c,v 1.2 2002/08/07 00:43:45 gerg Exp $ ++ * $Id: uclinux.c,v 1.5 2003/05/20 20:59:32 dwmw2 Exp $ + */ + + /****************************************************************************/ +@@ -24,58 +24,11 @@ + + /****************************************************************************/ + +-__u8 uclinux_read8(struct map_info *map, unsigned long ofs) +-{ +- return(*((__u8 *) (map->map_priv_1 + ofs))); +-} +- +-__u16 uclinux_read16(struct map_info *map, unsigned long ofs) +-{ +- return(*((__u16 *) (map->map_priv_1 + ofs))); +-} +- +-__u32 uclinux_read32(struct map_info *map, unsigned long ofs) +-{ +- return(*((__u32 *) (map->map_priv_1 + ofs))); +-} +- +-void uclinux_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +-{ +- memcpy(to, (void *)(map->map_priv_1 + from), len); +-} +- +-void uclinux_write8(struct map_info *map, __u8 d, unsigned long adr) +-{ +- *((__u8 *) (map->map_priv_1 + adr)) = d; +-} +- +-void uclinux_write16(struct map_info *map, __u16 d, unsigned long adr) +-{ +- *((__u16 *) (map->map_priv_1 + adr)) = d; +-} +- +-void uclinux_write32(struct map_info *map, __u32 d, unsigned long adr) +-{ +- *((__u32 *) (map->map_priv_1 + adr)) = d; +-} +- +-void uclinux_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +-{ +- memcpy((void *) (map->map_priv_1 + to), from, len); +-} + + /****************************************************************************/ + + struct map_info uclinux_ram_map = { +- name: "RAM", +- read8: uclinux_read8, +- read16: uclinux_read16, +- read32: uclinux_read32, +- copy_from: uclinux_copy_from, +- write8: uclinux_write8, +- write16: uclinux_write16, +- write32: uclinux_write32, +- copy_to: uclinux_copy_to, ++ .name = "RAM", + }; + + struct mtd_info *uclinux_ram_mtdinfo; +@@ -83,7 +36,7 @@ + /****************************************************************************/ + + struct mtd_partition uclinux_romfs[] = { +- { name: "ROMfs", offset: 0 } ++ { .name = "ROMfs" } + }; + + #define NUM_PARTITIONS (sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0])) +@@ -94,7 +47,7 @@ + size_t *retlen, u_char **mtdbuf) + { + struct map_info *map = (struct map_info *) mtd->priv; +- *mtdbuf = (u_char *) (map->map_priv_1 + ((int) from)); ++ *mtdbuf = (u_char *) (map->virt + ((int) from)); + *retlen = len; + return(0); + } +@@ -108,29 +61,31 @@ + extern char _ebss; + + mapp = &uclinux_ram_map; +- mapp->map_priv_2 = (unsigned long) &_ebss; ++ mapp->phys = (unsigned long) &_ebss; + mapp->size = PAGE_ALIGN(*((unsigned long *)((&_ebss) + 8))); + mapp->buswidth = 4; + + printk("uclinux[mtd]: RAM probe address=0x%x size=0x%x\n", + (int) mapp->map_priv_2, (int) mapp->size); + +- mapp->map_priv_1 = (unsigned long) +- ioremap_nocache(mapp->map_priv_2, mapp->size); ++ mapp->virt = (unsigned long) ++ ioremap_nocache(mapp->phys, mapp->size); + +- if (mapp->map_priv_1 == 0) { ++ if (mapp->virt == 0) { + printk("uclinux[mtd]: ioremap_nocache() failed\n"); + return(-EIO); + } + ++ simple_map_init(mapp); ++ + mtd = do_map_probe("map_ram", mapp); + if (!mtd) { + printk("uclinux[mtd]: failed to find a mapping?\n"); +- iounmap((void *) mapp->map_priv_1); ++ iounmap((void *) mapp->virt); + return(-ENXIO); + } + +- mtd->module = THIS_MODULE; ++ mtd->owner = THIS_MODULE; + mtd->point = uclinux_point; + mtd->priv = mapp; + +@@ -155,8 +110,8 @@ + uclinux_ram_mtdinfo = NULL; + } + if (uclinux_ram_map.map_priv_1) { +- iounmap((void *) uclinux_ram_map.map_priv_1); +- uclinux_ram_map.map_priv_1 = 0; ++ iounmap((void *) uclinux_ram_map.virt); ++ uclinux_ram_map.virt = 0; + } + } + +diff -Nurb linux-mips-2.4.27/drivers/mtd/maps/vmax301.c linux/drivers/mtd/maps/vmax301.c +--- linux-mips-2.4.27/drivers/mtd/maps/vmax301.c 2001-11-05 21:15:52.000000000 +0100 ++++ linux/drivers/mtd/maps/vmax301.c 2004-11-19 10:25:11.982187176 +0100 +@@ -1,4 +1,4 @@ +-// $Id: vmax301.c,v 1.24 2001/10/02 15:05:14 dwmw2 Exp $ ++// $Id: vmax301.c,v 1.28 2003/05/21 15:15:08 dwmw2 Exp $ + /* ###################################################################### + + Tempustech VMAX SBC301 MTD Driver. +@@ -24,6 +24,7 @@ + #include <asm/io.h> + + #include <linux/mtd/map.h> ++#include <linux/mtd/mtd.h> + + + #define WINDOW_START 0xd8000 +@@ -142,34 +143,36 @@ + + static struct map_info vmax_map[2] = { + { +- name: "VMAX301 Internal Flash", +- size: 3*2*1024*1024, +- buswidth: 1, +- read8: vmax301_read8, +- read16: vmax301_read16, +- read32: vmax301_read32, +- copy_from: vmax301_copy_from, +- write8: vmax301_write8, +- write16: vmax301_write16, +- write32: vmax301_write32, +- copy_to: vmax301_copy_to, +- map_priv_1: WINDOW_START + WINDOW_LENGTH, +- map_priv_2: 0xFFFFFFFF ++ .name = "VMAX301 Internal Flash", ++ .phys = NO_XIP, ++ .size = 3*2*1024*1024, ++ .buswidth = 1, ++ .read8 = vmax301_read8, ++ .read16 = vmax301_read16, ++ .read32 = vmax301_read32, ++ .copy_from = vmax301_copy_from, ++ .write8 = vmax301_write8, ++ .write16 = vmax301_write16, ++ .write32 = vmax301_write32, ++ .copy_to = vmax301_copy_to, ++ .map_priv_1 = WINDOW_START + WINDOW_LENGTH, ++ .map_priv_2 = 0xFFFFFFFF + }, + { +- name: "VMAX301 Socket", +- size: 0, +- buswidth: 1, +- read8: vmax301_read8, +- read16: vmax301_read16, +- read32: vmax301_read32, +- copy_from: vmax301_copy_from, +- write8: vmax301_write8, +- write16: vmax301_write16, +- write32: vmax301_write32, +- copy_to: vmax301_copy_to, +- map_priv_1: WINDOW_START + (3*WINDOW_LENGTH), +- map_priv_2: 0xFFFFFFFF ++ .name = "VMAX301 Socket", ++ .phys = NO_XIP, ++ .size = 0, ++ .buswidth = 1, ++ .read8 = vmax301_read8, ++ .read16 = vmax301_read16, ++ .read32 = vmax301_read32, ++ .copy_from = vmax301_copy_from, ++ .write8 = vmax301_write8, ++ .write16 = vmax301_write16, ++ .write32 = vmax301_write32, ++ .copy_to = vmax301_copy_to, ++ .map_priv_1 = WINDOW_START + (3*WINDOW_LENGTH), ++ .map_priv_2 = 0xFFFFFFFF + } + }; + +@@ -206,8 +209,8 @@ + address of the first half, because it's used more + often. + */ +- vmax_map[0].map_priv_1 = iomapadr + WINDOW_START; +- vmax_map[1].map_priv_1 = iomapadr + (3*WINDOW_START); ++ vmax_map[0].map_priv_2 = iomapadr + WINDOW_START; ++ vmax_map[1].map_priv_2 = iomapadr + (3*WINDOW_START); + + for (i=0; i<2; i++) { + vmax_mtd[i] = do_map_probe("cfi_probe", &vmax_map[i]); +@@ -218,7 +221,7 @@ + if (!vmax_mtd[i]) + vmax_mtd[i] = do_map_probe("map_rom", &vmax_map[i]); + if (vmax_mtd[i]) { +- vmax_mtd[i]->module = THIS_MODULE; ++ vmax_mtd[i]->owner = THIS_MODULE; + add_mtd_device(vmax_mtd[i]); + } + } +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtd_blkdevs-24.c linux/drivers/mtd/mtd_blkdevs-24.c +--- linux-mips-2.4.27/drivers/mtd/mtd_blkdevs-24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/mtd_blkdevs-24.c 2004-11-19 10:25:11.640239160 +0100 +@@ -0,0 +1,699 @@ ++/* ++ * $Id: mtd_blkdevs-24.c,v 1.15 2003/10/10 08:55:03 dwmw2 Exp $ ++ * ++ * (C) 2003 David Woodhouse <dwmw2@infradead.org> ++ * ++ * Interface to Linux 2.4 block layer for MTD 'translation layers'. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/list.h> ++#include <linux/fs.h> ++#include <linux/mtd/blktrans.h> ++#include <linux/mtd/mtd.h> ++#include <linux/blkdev.h> ++#include <linux/blk.h> ++#include <linux/blkpg.h> ++#include <linux/spinlock.h> ++#include <linux/hdreg.h> ++#include <linux/init.h> ++#include <asm/semaphore.h> ++#include <asm/uaccess.h> ++ ++static LIST_HEAD(blktrans_majors); ++ ++extern struct semaphore mtd_table_mutex; ++extern struct mtd_info *mtd_table[]; ++ ++struct mtd_blkcore_priv { ++ devfs_handle_t devfs_dir_handle; ++ int blksizes[256]; ++ int sizes[256]; ++ struct hd_struct part_table[256]; ++ struct gendisk gd; ++ spinlock_t devs_lock; /* See comment in _request function */ ++ struct completion thread_dead; ++ int exiting; ++ wait_queue_head_t thread_wq; ++}; ++ ++static inline struct mtd_blktrans_dev *tr_get_dev(struct mtd_blktrans_ops *tr, ++ int devnum) ++{ ++ struct list_head *this; ++ struct mtd_blktrans_dev *d; ++ ++ list_for_each(this, &tr->devs) { ++ d = list_entry(this, struct mtd_blktrans_dev, list); ++ ++ if (d->devnum == devnum) ++ return d; ++ } ++ return NULL; ++} ++ ++static inline struct mtd_blktrans_ops *get_tr(int major) ++{ ++ struct list_head *this; ++ struct mtd_blktrans_ops *t; ++ ++ list_for_each(this, &blktrans_majors) { ++ t = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ if (t->major == major) ++ return t; ++ } ++ return NULL; ++} ++ ++static int do_blktrans_request(struct mtd_blktrans_ops *tr, ++ struct mtd_blktrans_dev *dev, ++ struct request *req) ++{ ++ unsigned long block, nsect; ++ char *buf; ++ int minor; ++ ++ minor = MINOR(req->rq_dev); ++ block = req->sector; ++ nsect = req->current_nr_sectors; ++ buf = req->buffer; ++ ++ if (block + nsect > tr->blkcore_priv->part_table[minor].nr_sects) { ++ printk(KERN_WARNING "Access beyond end of device.\n"); ++ return 0; ++ } ++ block += tr->blkcore_priv->part_table[minor].start_sect; ++ ++ switch(req->cmd) { ++ case READ: ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->readsect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ case WRITE: ++ if (!tr->writesect) ++ return 0; ++ ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->writesect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ default: ++ printk(KERN_NOTICE "Unknown request cmd %d\n", req->cmd); ++ return 0; ++ } ++} ++ ++static int mtd_blktrans_thread(void *arg) ++{ ++ struct mtd_blktrans_ops *tr = arg; ++ struct request_queue *rq = BLK_DEFAULT_QUEUE(tr->major); ++ ++ /* we might get involved when memory gets low, so use PF_MEMALLOC */ ++ current->flags |= PF_MEMALLOC; ++ ++ snprintf(current->comm, sizeof(current->comm), "%sd", tr->name); ++ ++ /* daemonize() doesn't do this for us since some kernel threads ++ actually want to deal with signals. We can't just call ++ exit_sighand() since that'll cause an oops when we finally ++ do exit. */ ++ ++#ifndef __rh_config_h__ /* HAVE_NPTL */ ++ spin_lock_irq(¤t->sigmask_lock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(current); ++ spin_unlock_irq(¤t->sigmask_lock); ++#else ++ spin_lock_irq(¤t->sighand->siglock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++#endif ++ daemonize(); ++ ++ while (!tr->blkcore_priv->exiting) { ++ struct request *req; ++ struct mtd_blktrans_dev *dev; ++ int devnum; ++ int res = 0; ++ DECLARE_WAITQUEUE(wait, current); ++ ++ spin_lock_irq(&io_request_lock); ++ ++ if (list_empty(&rq->queue_head)) { ++ ++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ spin_unlock_irq(&io_request_lock); ++ ++ schedule(); ++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ ++ continue; ++ } ++ ++ req = blkdev_entry_next_request(&rq->queue_head); ++ ++ devnum = MINOR(req->rq_dev) >> tr->part_bits; ++ ++ /* The ll_rw_blk code knows not to touch the request ++ at the head of the queue */ ++ spin_unlock_irq(&io_request_lock); ++ ++ /* FIXME: Where can we store the dev, on which ++ we already have a refcount anyway? We need to ++ lock against concurrent addition/removal of devices, ++ but if we use the mtd_table_mutex we deadlock when ++ grok_partitions is called from the registration ++ callbacks. */ ++ spin_lock(&tr->blkcore_priv->devs_lock); ++ dev = tr_get_dev(tr, devnum); ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ ++ BUG_ON(!dev); ++ ++ /* Ensure serialisation of requests */ ++ down(&dev->sem); ++ ++ res = do_blktrans_request(tr, dev, req); ++ up(&dev->sem); ++ ++ if (!end_that_request_first(req, res, tr->name)) { ++ spin_lock_irq(&io_request_lock); ++ blkdev_dequeue_request(req); ++ end_that_request_last(req); ++ spin_unlock_irq(&io_request_lock); ++ } ++ } ++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0); ++} ++ ++static void mtd_blktrans_request(struct request_queue *rq) ++{ ++ struct mtd_blktrans_ops *tr = rq->queuedata; ++ wake_up(&tr->blkcore_priv->thread_wq); ++} ++ ++int blktrans_open(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_ops *tr = NULL; ++ struct mtd_blktrans_dev *dev = NULL; ++ int major_nr = MAJOR(i->i_rdev); ++ int minor_nr = MINOR(i->i_rdev); ++ int devnum; ++ int ret = -ENODEV; ++ ++ if (is_read_only(i->i_rdev) && (f->f_mode & FMODE_WRITE)) ++ return -EROFS; ++ ++ down(&mtd_table_mutex); ++ ++ tr = get_tr(major_nr); ++ ++ if (!tr) ++ goto out; ++ ++ devnum = minor_nr >> tr->part_bits; ++ ++ dev = tr_get_dev(tr, devnum); ++ ++ if (!dev) ++ goto out; ++ ++ if (!tr->blkcore_priv->part_table[minor_nr].nr_sects) { ++ ret = -ENODEV; ++ goto out; ++ } ++ ++ if (!try_inc_mod_count(dev->mtd->owner)) ++ goto out; ++ ++ if (!try_inc_mod_count(tr->owner)) ++ goto out_tr; ++ ++ dev->mtd->usecount++; ++ ++ ret = 0; ++ if (tr->open && (ret = tr->open(dev))) { ++ dev->mtd->usecount--; ++ if (dev->mtd->owner) ++ __MOD_DEC_USE_COUNT(dev->mtd->owner); ++ out_tr: ++ if (tr->owner) ++ __MOD_DEC_USE_COUNT(tr->owner); ++ } ++ out: ++ up(&mtd_table_mutex); ++ ++ return ret; ++} ++ ++int blktrans_release(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int ret = 0; ++ int devnum; ++ ++ down(&mtd_table_mutex); ++ ++ tr = get_tr(MAJOR(i->i_rdev)); ++ if (!tr) { ++ up(&mtd_table_mutex); ++ return -ENODEV; ++ } ++ ++ devnum = MINOR(i->i_rdev) >> tr->part_bits; ++ dev = tr_get_dev(tr, devnum); ++ ++ if (!dev) { ++ up(&mtd_table_mutex); ++ return -ENODEV; ++ } ++ ++ if (tr->release) ++ ret = tr->release(dev); ++ ++ if (!ret) { ++ dev->mtd->usecount--; ++ if (dev->mtd->owner) ++ __MOD_DEC_USE_COUNT(dev->mtd->owner); ++ if (tr->owner) ++ __MOD_DEC_USE_COUNT(tr->owner); ++ } ++ ++ up(&mtd_table_mutex); ++ ++ return ret; ++} ++ ++static int mtd_blktrans_rrpart(kdev_t rdev, struct mtd_blktrans_ops *tr, ++ struct mtd_blktrans_dev *dev) ++{ ++ struct gendisk *gd = &(tr->blkcore_priv->gd); ++ int i; ++ int minor = MINOR(rdev); ++ ++ if (minor & ((1<<tr->part_bits)-1) || !tr->part_bits) { ++ /* BLKRRPART on a partition. Go away. */ ++ return -ENOTTY; ++ } ++ ++ if (!capable(CAP_SYS_ADMIN)) ++ return -EACCES; ++ ++ /* We are required to prevent simultaneous open() ourselves. ++ The core doesn't do that for us. Did I ever mention how ++ much the Linux block layer sucks? Sledgehammer approach... */ ++ down(&mtd_table_mutex); ++ ++ for (i=0; i < (1<<tr->part_bits); i++) { ++ invalidate_device(MKDEV(tr->major, minor+i), 1); ++ gd->part[minor + i].start_sect = 0; ++ gd->part[minor + i].nr_sects = 0; ++ } ++ ++ grok_partitions(gd, minor, 1 << tr->part_bits, ++ tr->blkcore_priv->sizes[minor]); ++ up(&mtd_table_mutex); ++ ++ return 0; ++} ++ ++static int blktrans_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int devnum; ++ ++ switch(cmd) { ++ case BLKGETSIZE: ++ case BLKGETSIZE64: ++ case BLKBSZSET: ++ case BLKBSZGET: ++ case BLKROSET: ++ case BLKROGET: ++ case BLKRASET: ++ case BLKRAGET: ++ case BLKPG: ++ case BLKELVGET: ++ case BLKELVSET: ++ return blk_ioctl(inode->i_rdev, cmd, arg); ++ } ++ ++ down(&mtd_table_mutex); ++ ++ tr = get_tr(MAJOR(inode->i_rdev)); ++ if (!tr) { ++ up(&mtd_table_mutex); ++ return -ENODEV; ++ } ++ ++ devnum = MINOR(inode->i_rdev) >> tr->part_bits; ++ dev = tr_get_dev(tr, devnum); ++ ++ up(&mtd_table_mutex); ++ ++ if (!dev) ++ return -ENODEV; ++ ++ switch(cmd) { ++ case BLKRRPART: ++ return mtd_blktrans_rrpart(inode->i_rdev, tr, dev); ++ ++ case BLKFLSBUF: ++ blk_ioctl(inode->i_rdev, cmd, arg); ++ if (tr->flush) ++ return tr->flush(dev); ++ /* The core code did the work, we had nothing to do. */ ++ return 0; ++ ++ case HDIO_GETGEO: ++ if (tr->getgeo) { ++ struct hd_geometry g; ++ struct gendisk *gd = &(tr->blkcore_priv->gd); ++ int ret; ++ ++ memset(&g, 0, sizeof(g)); ++ ret = tr->getgeo(dev, &g); ++ if (ret) ++ return ret; ++ ++ g.start = gd->part[MINOR(inode->i_rdev)].start_sect; ++ if (copy_to_user((void *)arg, &g, sizeof(g))) ++ return -EFAULT; ++ return 0; ++ } /* else */ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++struct block_device_operations mtd_blktrans_ops = { ++ .owner = THIS_MODULE, ++ .open = blktrans_open, ++ .release = blktrans_release, ++ .ioctl = blktrans_ioctl, ++}; ++ ++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) ++{ ++ struct mtd_blktrans_ops *tr = new->tr; ++ struct list_head *this; ++ int last_devnum = -1; ++ int i; ++ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++ spin_lock(&tr->blkcore_priv->devs_lock); ++ ++ list_for_each(this, &tr->devs) { ++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); ++ if (new->devnum == -1) { ++ /* Use first free number */ ++ if (d->devnum != last_devnum+1) { ++ /* Found a free devnum. Plug it in here */ ++ new->devnum = last_devnum+1; ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ } else if (d->devnum == new->devnum) { ++ /* Required number taken */ ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ return -EBUSY; ++ } else if (d->devnum > new->devnum) { ++ /* Required number was free */ ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ last_devnum = d->devnum; ++ } ++ if (new->devnum == -1) ++ new->devnum = last_devnum+1; ++ ++ if ((new->devnum << tr->part_bits) > 256) { ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ return -EBUSY; ++ } ++ ++ init_MUTEX(&new->sem); ++ list_add_tail(&new->list, &tr->devs); ++ added: ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ ++ if (!tr->writesect) ++ new->readonly = 1; ++ ++ for (i = new->devnum << tr->part_bits; ++ i < (new->devnum+1) << tr->part_bits; ++ i++) { ++ set_device_ro(MKDEV(tr->major, i), new->readonly); ++ tr->blkcore_priv->blksizes[i] = new->blksize; ++ tr->blkcore_priv->sizes[i] = 0; ++ tr->blkcore_priv->part_table[i].nr_sects = 0; ++ tr->blkcore_priv->part_table[i].start_sect = 0; ++ } ++ ++ /* ++ <viro_zzz> dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices ++ <viro> dwmw2: any code which sets blk_size[][] should be ++ size >> 10 /+ 2.4 and its dumb units */ ++ ++ tr->blkcore_priv->sizes[new->devnum << tr->part_bits] = ++ (new->size * new->blksize) >> 10; /* 2.4 and its dumb units */ ++ ++ /* But this is still in device's sectors? $DEITY knows */ ++ tr->blkcore_priv->part_table[new->devnum << tr->part_bits].nr_sects = new->size; ++ ++ if (tr->part_bits) { ++ grok_partitions(&tr->blkcore_priv->gd, new->devnum, ++ 1 << tr->part_bits, new->size); ++ } ++#ifdef CONFIG_DEVFS_FS ++ if (!tr->part_bits) { ++ char name[2]; ++ ++ name[0] = '0' + new->devnum; ++ name[1] = 0; ++ ++ new->blkcore_priv = ++ devfs_register(tr->blkcore_priv->devfs_dir_handle, ++ name, DEVFS_FL_DEFAULT, tr->major, ++ new->devnum, S_IFBLK|S_IRUGO|S_IWUGO, ++ &mtd_blktrans_ops, NULL); ++ } ++#endif ++ return 0; ++} ++ ++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) ++{ ++ struct mtd_blktrans_ops *tr = old->tr; ++ int i; ++ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++#ifdef CONFIG_DEVFS_FS ++ if (!tr->part_bits) { ++ devfs_unregister(old->blkcore_priv); ++ old->blkcore_priv = NULL; ++ } else { ++ devfs_register_partitions(&tr->blkcore_priv->gd, ++ old->devnum << tr->part_bits, 1); ++ } ++#endif ++ spin_lock(&tr->blkcore_priv->devs_lock); ++ list_del(&old->list); ++ spin_unlock(&tr->blkcore_priv->devs_lock); ++ ++ for (i = (old->devnum << tr->part_bits); ++ i < ((old->devnum+1) << tr->part_bits); i++) { ++ tr->blkcore_priv->sizes[i] = 0; ++ tr->blkcore_priv->part_table[i].nr_sects = 0; ++ tr->blkcore_priv->part_table[i].start_sect = 0; ++ } ++ ++ return 0; ++} ++ ++void blktrans_notify_remove(struct mtd_info *mtd) ++{ ++ struct list_head *this, *this2, *next; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ list_for_each_safe(this2, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); ++ ++ if (dev->mtd == mtd) ++ tr->remove_dev(dev); ++ } ++ } ++} ++ ++void blktrans_notify_add(struct mtd_info *mtd) ++{ ++ struct list_head *this; ++ ++ if (mtd->type == MTD_ABSENT) ++ return; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ tr->add_mtd(tr, mtd); ++ } ++ ++} ++ ++static struct mtd_notifier blktrans_notifier = { ++ .add = blktrans_notify_add, ++ .remove = blktrans_notify_remove, ++}; ++ ++int register_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ int ret, i; ++ ++ /* Register the notifier if/when the first device type is ++ registered, to prevent the link/init ordering from fucking ++ us over. */ ++ if (!blktrans_notifier.list.next) ++ register_mtd_user(&blktrans_notifier); ++ ++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); ++ if (!tr->blkcore_priv) ++ return -ENOMEM; ++ ++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); ++ ++ down(&mtd_table_mutex); ++ ++ ret = devfs_register_blkdev(tr->major, tr->name, &mtd_blktrans_ops); ++ if (ret) { ++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", ++ tr->name, tr->major, ret); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ ++ blk_init_queue(BLK_DEFAULT_QUEUE(tr->major), &mtd_blktrans_request); ++ (BLK_DEFAULT_QUEUE(tr->major))->queuedata = tr; ++ ++ init_completion(&tr->blkcore_priv->thread_dead); ++ init_waitqueue_head(&tr->blkcore_priv->thread_wq); ++ ++ ret = kernel_thread(mtd_blktrans_thread, tr, ++ CLONE_FS|CLONE_FILES|CLONE_SIGHAND); ++ if (ret < 0) { ++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); ++ devfs_unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ ++ tr->blkcore_priv->devfs_dir_handle = ++ devfs_mk_dir(NULL, tr->name, NULL); ++ ++ blksize_size[tr->major] = tr->blkcore_priv->blksizes; ++ blk_size[tr->major] = tr->blkcore_priv->sizes; ++ ++ tr->blkcore_priv->gd.major = tr->major; ++ tr->blkcore_priv->gd.major_name = tr->name; ++ tr->blkcore_priv->gd.minor_shift = tr->part_bits; ++ tr->blkcore_priv->gd.max_p = (1<<tr->part_bits) - 1; ++ tr->blkcore_priv->gd.part = tr->blkcore_priv->part_table; ++ tr->blkcore_priv->gd.sizes = tr->blkcore_priv->sizes; ++ tr->blkcore_priv->gd.nr_real = 256 >> tr->part_bits; ++ ++ spin_lock_init(&tr->blkcore_priv->devs_lock); ++ ++ add_gendisk(&tr->blkcore_priv->gd); ++ ++ INIT_LIST_HEAD(&tr->devs); ++ list_add(&tr->list, &blktrans_majors); ++ ++ for (i=0; i<MAX_MTD_DEVICES; i++) { ++ if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT) ++ tr->add_mtd(tr, mtd_table[i]); ++ } ++ up(&mtd_table_mutex); ++ ++ return 0; ++} ++ ++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ struct list_head *this, *next; ++ ++ down(&mtd_table_mutex); ++ ++ /* Clean up the kernel thread */ ++ tr->blkcore_priv->exiting = 1; ++ wake_up(&tr->blkcore_priv->thread_wq); ++ wait_for_completion(&tr->blkcore_priv->thread_dead); ++ ++ /* Remove it from the list of active majors */ ++ list_del(&tr->list); ++ ++ /* Remove each of its devices */ ++ list_for_each_safe(this, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); ++ tr->remove_dev(dev); ++ } ++ ++ blksize_size[tr->major] = NULL; ++ blk_size[tr->major] = NULL; ++ ++ del_gendisk(&tr->blkcore_priv->gd); ++ ++ blk_cleanup_queue(BLK_DEFAULT_QUEUE(tr->major)); ++ devfs_unregister_blkdev(tr->major, tr->name); ++ ++ devfs_unregister(tr->blkcore_priv->devfs_dir_handle); ++ ++ up(&mtd_table_mutex); ++ ++ kfree(tr->blkcore_priv); ++ ++ if (!list_empty(&tr->devs)) ++ BUG(); ++ return 0; ++} ++ ++static void __exit mtd_blktrans_exit(void) ++{ ++ /* No race here -- if someone's currently in register_mtd_blktrans ++ we're screwed anyway. */ ++ if (blktrans_notifier.list.next) ++ unregister_mtd_user(&blktrans_notifier); ++} ++ ++module_exit(mtd_blktrans_exit); ++ ++EXPORT_SYMBOL_GPL(register_mtd_blktrans); ++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); ++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); ++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); ++ ++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtd_blkdevs.c linux/drivers/mtd/mtd_blkdevs.c +--- linux-mips-2.4.27/drivers/mtd/mtd_blkdevs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/mtd_blkdevs.c 2004-11-19 10:25:11.642238856 +0100 +@@ -0,0 +1,479 @@ ++/* ++ * $Id: mtd_blkdevs.c,v 1.19 2003/11/07 09:52:46 dwmw2 Exp $ ++ * ++ * (C) 2003 David Woodhouse <dwmw2@infradead.org> ++ * ++ * Interface to Linux 2.5 block layer for MTD 'translation layers'. ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/module.h> ++#include <linux/list.h> ++#include <linux/fs.h> ++#include <linux/mtd/blktrans.h> ++#include <linux/mtd/mtd.h> ++#include <linux/blkdev.h> ++#include <linux/blk.h> ++#include <linux/blkpg.h> ++#include <linux/spinlock.h> ++#include <linux/hdreg.h> ++#include <linux/init.h> ++#include <asm/semaphore.h> ++#include <asm/uaccess.h> ++#include <linux/devfs_fs_kernel.h> ++ ++static LIST_HEAD(blktrans_majors); ++ ++extern struct semaphore mtd_table_mutex; ++extern struct mtd_info *mtd_table[]; ++ ++struct mtd_blkcore_priv { ++ struct completion thread_dead; ++ int exiting; ++ wait_queue_head_t thread_wq; ++ struct request_queue *rq; ++ spinlock_t queue_lock; ++}; ++ ++static int do_blktrans_request(struct mtd_blktrans_ops *tr, ++ struct mtd_blktrans_dev *dev, ++ struct request *req) ++{ ++ unsigned long block, nsect; ++ char *buf; ++ ++ block = req->sector; ++ nsect = req->current_nr_sectors; ++ buf = req->buffer; ++ ++ if (!(req->flags & REQ_CMD)) ++ return 0; ++ ++ if (block + nsect > get_capacity(req->rq_disk)) ++ return 0; ++ ++ switch(rq_data_dir(req)) { ++ case READ: ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->readsect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ case WRITE: ++ if (!tr->writesect) ++ return 0; ++ ++ for (; nsect > 0; nsect--, block++, buf += 512) ++ if (tr->writesect(dev, block, buf)) ++ return 0; ++ return 1; ++ ++ default: ++ printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req)); ++ return 0; ++ } ++} ++ ++static int mtd_blktrans_thread(void *arg) ++{ ++ struct mtd_blktrans_ops *tr = arg; ++ struct request_queue *rq = tr->blkcore_priv->rq; ++ ++ /* we might get involved when memory gets low, so use PF_MEMALLOC */ ++ current->flags |= PF_MEMALLOC; ++ ++ daemonize("%sd", tr->name); ++ ++ /* daemonize() doesn't do this for us since some kernel threads ++ actually want to deal with signals. We can't just call ++ exit_sighand() since that'll cause an oops when we finally ++ do exit. */ ++ spin_lock_irq(¤t->sighand->siglock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sighand->siglock); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ while (!tr->blkcore_priv->exiting) { ++ struct request *req; ++ struct mtd_blktrans_dev *dev; ++ int res = 0; ++ DECLARE_WAITQUEUE(wait, current); ++ ++ req = elv_next_request(rq); ++ ++ if (!req) { ++ add_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ set_current_state(TASK_INTERRUPTIBLE); ++ ++ spin_unlock_irq(rq->queue_lock); ++ ++ schedule(); ++ remove_wait_queue(&tr->blkcore_priv->thread_wq, &wait); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ continue; ++ } ++ ++ dev = req->rq_disk->private_data; ++ tr = dev->tr; ++ ++ spin_unlock_irq(rq->queue_lock); ++ ++ down(&dev->sem); ++ res = do_blktrans_request(tr, dev, req); ++ up(&dev->sem); ++ ++ spin_lock_irq(rq->queue_lock); ++ ++ end_request(req, res); ++ } ++ complete_and_exit(&tr->blkcore_priv->thread_dead, 0); ++} ++ ++static void mtd_blktrans_request(struct request_queue *rq) ++{ ++ struct mtd_blktrans_ops *tr = rq->queuedata; ++ wake_up(&tr->blkcore_priv->thread_wq); ++} ++ ++ ++int blktrans_open(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int ret = -ENODEV; ++ ++ dev = i->i_bdev->bd_disk->private_data; ++ tr = dev->tr; ++ ++ if (!try_module_get(dev->mtd->owner)) ++ goto out; ++ ++ if (!try_module_get(tr->owner)) ++ goto out_tr; ++ ++ /* FIXME: Locking. A hot pluggable device can go away ++ (del_mtd_device can be called for it) without its module ++ being unloaded. */ ++ dev->mtd->usecount++; ++ ++ ret = 0; ++ if (tr->open && (ret = tr->open(dev))) { ++ dev->mtd->usecount--; ++ module_put(dev->mtd->owner); ++ out_tr: ++ module_put(tr->owner); ++ } ++ out: ++ return ret; ++} ++ ++int blktrans_release(struct inode *i, struct file *f) ++{ ++ struct mtd_blktrans_dev *dev; ++ struct mtd_blktrans_ops *tr; ++ int ret = 0; ++ ++ dev = i->i_bdev->bd_disk->private_data; ++ tr = dev->tr; ++ ++ if (tr->release) ++ ret = tr->release(dev); ++ ++ if (!ret) { ++ dev->mtd->usecount--; ++ module_put(dev->mtd->owner); ++ module_put(tr->owner); ++ } ++ ++ return ret; ++} ++ ++ ++static int blktrans_ioctl(struct inode *inode, struct file *file, ++ unsigned int cmd, unsigned long arg) ++{ ++ struct mtd_blktrans_dev *dev = inode->i_bdev->bd_disk->private_data; ++ struct mtd_blktrans_ops *tr = dev->tr; ++ ++ switch (cmd) { ++ case BLKFLSBUF: ++ if (tr->flush) ++ return tr->flush(dev); ++ /* The core code did the work, we had nothing to do. */ ++ return 0; ++ ++ case HDIO_GETGEO: ++ if (tr->getgeo) { ++ struct hd_geometry g; ++ int ret; ++ ++ memset(&g, 0, sizeof(g)); ++ ret = tr->getgeo(dev, &g); ++ ++ if (ret) ++ return ret; ++ ++ g.start = get_start_sect(inode->i_bdev); ++ if (copy_to_user((void *)arg, &g, sizeof(g))) ++ return -EFAULT; ++ return 0; ++ } /* else */ ++ default: ++ return -ENOTTY; ++ } ++} ++ ++struct block_device_operations mtd_blktrans_ops = { ++ .owner = THIS_MODULE, ++ .open = blktrans_open, ++ .release = blktrans_release, ++ .ioctl = blktrans_ioctl, ++}; ++ ++int add_mtd_blktrans_dev(struct mtd_blktrans_dev *new) ++{ ++ struct mtd_blktrans_ops *tr = new->tr; ++ struct list_head *this; ++ int last_devnum = -1; ++ struct gendisk *gd; ++ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++ list_for_each(this, &tr->devs) { ++ struct mtd_blktrans_dev *d = list_entry(this, struct mtd_blktrans_dev, list); ++ if (new->devnum == -1) { ++ /* Use first free number */ ++ if (d->devnum != last_devnum+1) { ++ /* Found a free devnum. Plug it in here */ ++ new->devnum = last_devnum+1; ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ } else if (d->devnum == new->devnum) { ++ /* Required number taken */ ++ return -EBUSY; ++ } else if (d->devnum > new->devnum) { ++ /* Required number was free */ ++ list_add_tail(&new->list, &d->list); ++ goto added; ++ } ++ last_devnum = d->devnum; ++ } ++ if (new->devnum == -1) ++ new->devnum = last_devnum+1; ++ ++ if ((new->devnum << tr->part_bits) > 256) { ++ return -EBUSY; ++ } ++ ++ init_MUTEX(&new->sem); ++ list_add_tail(&new->list, &tr->devs); ++ added: ++ if (!tr->writesect) ++ new->readonly = 1; ++ ++ gd = alloc_disk(1 << tr->part_bits); ++ if (!gd) { ++ list_del(&new->list); ++ return -ENOMEM; ++ } ++ gd->major = tr->major; ++ gd->first_minor = (new->devnum) << tr->part_bits; ++ gd->fops = &mtd_blktrans_ops; ++ ++ snprintf(gd->disk_name, sizeof(gd->disk_name), ++ "%s%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); ++ snprintf(gd->devfs_name, sizeof(gd->devfs_name), ++ "%s/%c", tr->name, (tr->part_bits?'a':'0') + new->devnum); ++ ++ /* 2.5 has capacity in units of 512 bytes while still ++ having BLOCK_SIZE_BITS set to 10. Just to keep us amused. */ ++ set_capacity(gd, (new->size * new->blksize) >> 9); ++ ++ gd->private_data = new; ++ new->blkcore_priv = gd; ++ gd->queue = tr->blkcore_priv->rq; ++ ++ if (new->readonly) ++ set_disk_ro(gd, 1); ++ ++ add_disk(gd); ++ ++ return 0; ++} ++ ++int del_mtd_blktrans_dev(struct mtd_blktrans_dev *old) ++{ ++ if (!down_trylock(&mtd_table_mutex)) { ++ up(&mtd_table_mutex); ++ BUG(); ++ } ++ ++ list_del(&old->list); ++ ++ del_gendisk(old->blkcore_priv); ++ put_disk(old->blkcore_priv); ++ ++ return 0; ++} ++ ++void blktrans_notify_remove(struct mtd_info *mtd) ++{ ++ struct list_head *this, *this2, *next; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ list_for_each_safe(this2, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this2, struct mtd_blktrans_dev, list); ++ ++ if (dev->mtd == mtd) ++ tr->remove_dev(dev); ++ } ++ } ++} ++ ++void blktrans_notify_add(struct mtd_info *mtd) ++{ ++ struct list_head *this; ++ ++ if (mtd->type == MTD_ABSENT) ++ return; ++ ++ list_for_each(this, &blktrans_majors) { ++ struct mtd_blktrans_ops *tr = list_entry(this, struct mtd_blktrans_ops, list); ++ ++ tr->add_mtd(tr, mtd); ++ } ++ ++} ++ ++static struct mtd_notifier blktrans_notifier = { ++ .add = blktrans_notify_add, ++ .remove = blktrans_notify_remove, ++}; ++ ++int register_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ int ret, i; ++ ++ /* Register the notifier if/when the first device type is ++ registered, to prevent the link/init ordering from fucking ++ us over. */ ++ if (!blktrans_notifier.list.next) ++ register_mtd_user(&blktrans_notifier); ++ ++ tr->blkcore_priv = kmalloc(sizeof(*tr->blkcore_priv), GFP_KERNEL); ++ if (!tr->blkcore_priv) ++ return -ENOMEM; ++ ++ memset(tr->blkcore_priv, 0, sizeof(*tr->blkcore_priv)); ++ ++ down(&mtd_table_mutex); ++ ++ ret = register_blkdev(tr->major, tr->name); ++ if (ret) { ++ printk(KERN_WARNING "Unable to register %s block device on major %d: %d\n", ++ tr->name, tr->major, ret); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ spin_lock_init(&tr->blkcore_priv->queue_lock); ++ init_completion(&tr->blkcore_priv->thread_dead); ++ init_waitqueue_head(&tr->blkcore_priv->thread_wq); ++ ++ tr->blkcore_priv->rq = blk_init_queue(mtd_blktrans_request, &tr->blkcore_priv->queue_lock); ++ if (!tr->blkcore_priv->rq) { ++ unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return -ENOMEM; ++ } ++ ++ tr->blkcore_priv->rq->queuedata = tr; ++ ++ ret = kernel_thread(mtd_blktrans_thread, tr, ++ CLONE_FS|CLONE_FILES|CLONE_SIGHAND); ++ if (ret < 0) { ++ blk_cleanup_queue(tr->blkcore_priv->rq); ++ unregister_blkdev(tr->major, tr->name); ++ kfree(tr->blkcore_priv); ++ up(&mtd_table_mutex); ++ return ret; ++ } ++ ++ devfs_mk_dir(tr->name); ++ ++ INIT_LIST_HEAD(&tr->devs); ++ list_add(&tr->list, &blktrans_majors); ++ ++ for (i=0; i<MAX_MTD_DEVICES; i++) { ++ if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT) ++ tr->add_mtd(tr, mtd_table[i]); ++ } ++ ++ up(&mtd_table_mutex); ++ ++ return 0; ++} ++ ++int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) ++{ ++ struct list_head *this, *next; ++ ++ down(&mtd_table_mutex); ++ ++ /* Clean up the kernel thread */ ++ tr->blkcore_priv->exiting = 1; ++ wake_up(&tr->blkcore_priv->thread_wq); ++ wait_for_completion(&tr->blkcore_priv->thread_dead); ++ ++ /* Remove it from the list of active majors */ ++ list_del(&tr->list); ++ ++ list_for_each_safe(this, next, &tr->devs) { ++ struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); ++ tr->remove_dev(dev); ++ } ++ ++ devfs_remove(tr->name); ++ blk_cleanup_queue(tr->blkcore_priv->rq); ++ unregister_blkdev(tr->major, tr->name); ++ ++ up(&mtd_table_mutex); ++ ++ kfree(tr->blkcore_priv); ++ ++ if (!list_empty(&tr->devs)) ++ BUG(); ++ return 0; ++} ++ ++static void __exit mtd_blktrans_exit(void) ++{ ++ /* No race here -- if someone's currently in register_mtd_blktrans ++ we're screwed anyway. */ ++ if (blktrans_notifier.list.next) ++ unregister_mtd_user(&blktrans_notifier); ++} ++ ++module_exit(mtd_blktrans_exit); ++ ++EXPORT_SYMBOL_GPL(register_mtd_blktrans); ++EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); ++EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); ++EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); ++ ++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); ++MODULE_LICENSE("GPL"); ++MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdblock.c linux/drivers/mtd/mtdblock.c +--- linux-mips-2.4.27/drivers/mtd/mtdblock.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdblock.c 2004-11-19 10:25:11.643238704 +0100 +@@ -1,52 +1,25 @@ + /* + * Direct MTD block device access + * +- * $Id: mtdblock.c,v 1.51 2001/11/20 11:42:33 dwmw2 Exp $ ++ * $Id: mtdblock.c,v 1.64 2003/10/04 17:14:14 dwmw2 Exp $ + * +- * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache ++ * (C) 2000-2003 Nicolas Pitre <nico@cam.org> ++ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> + */ + + #include <linux/config.h> + #include <linux/types.h> + #include <linux/module.h> + #include <linux/kernel.h> ++#include <linux/fs.h> ++#include <linux/init.h> + #include <linux/slab.h> ++#include <linux/vmalloc.h> + #include <linux/mtd/mtd.h> +-#include <linux/mtd/compatmac.h> +- +-#define MAJOR_NR MTD_BLOCK_MAJOR +-#define DEVICE_NAME "mtdblock" +-#define DEVICE_REQUEST mtdblock_request +-#define DEVICE_NR(device) (device) +-#define DEVICE_ON(device) +-#define DEVICE_OFF(device) +-#define DEVICE_NO_RANDOM +-#include <linux/blk.h> +-/* for old kernels... */ +-#ifndef QUEUE_EMPTY +-#define QUEUE_EMPTY (!CURRENT) +-#endif +-#if LINUX_VERSION_CODE < 0x20300 +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync) +-#else +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged) +-#endif +- +-#ifdef CONFIG_DEVFS_FS +-#include <linux/devfs_fs_kernel.h> +-static void mtd_notify_add(struct mtd_info* mtd); +-static void mtd_notify_remove(struct mtd_info* mtd); +-static struct mtd_notifier notifier = { +- mtd_notify_add, +- mtd_notify_remove, +- NULL +-}; +-static devfs_handle_t devfs_dir_handle = NULL; +-static devfs_handle_t devfs_rw_handle[MAX_MTD_DEVICES]; +-#endif ++#include <linux/mtd/blktrans.h> + + static struct mtdblk_dev { +- struct mtd_info *mtd; /* Locked */ ++ struct mtd_info *mtd; + int count; + struct semaphore cache_sem; + unsigned char *cache_data; +@@ -55,19 +28,6 @@ + enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; + } *mtdblks[MAX_MTD_DEVICES]; + +-static spinlock_t mtdblks_lock; +- +-static int mtd_sizes[MAX_MTD_DEVICES]; +-static int mtd_blksizes[MAX_MTD_DEVICES]; +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif +- + /* + * Cache stuff... + * +@@ -151,7 +111,7 @@ + return ret; + + /* +- * Here we could argably set the cache state to STATE_CLEAN. ++ * Here we could argubly set the cache state to STATE_CLEAN. + * However this could lead to inconsistency since we will not + * be notified if this content is altered on the flash by other + * means. Let's declare it empty and leave buffering tasks to +@@ -277,57 +237,47 @@ + return 0; + } + ++static int mtdblock_readsect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; ++ return do_cached_read(mtdblk, block<<9, 512, buf); ++} + ++static int mtdblock_writesect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) ++{ ++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; ++ if (unlikely(!mtdblk->cache_data)) { ++ mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); ++ if (!mtdblk->cache_data) ++ return -EINTR; ++ /* -EINTR is not really correct, but it is the best match ++ * documented in man 2 write for all cases. We could also ++ * return -EAGAIN sometimes, but why bother? ++ */ ++ } ++ return do_cached_write(mtdblk, block<<9, 512, buf); ++} + +-static int mtdblock_open(struct inode *inode, struct file *file) ++static int mtdblock_open(struct mtd_blktrans_dev *mbd) + { + struct mtdblk_dev *mtdblk; +- struct mtd_info *mtd; +- int dev; ++ struct mtd_info *mtd = mbd->mtd; ++ int dev = mbd->devnum; + + DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); + +- if (!inode) +- return -EINVAL; +- +- dev = MINOR(inode->i_rdev); +- if (dev >= MAX_MTD_DEVICES) +- return -EINVAL; +- +- BLK_INC_USE_COUNT; +- +- mtd = get_mtd_device(NULL, dev); +- if (!mtd) +- return -ENODEV; +- if (MTD_ABSENT == mtd->type) { +- put_mtd_device(mtd); +- BLK_DEC_USE_COUNT; +- return -ENODEV; +- } +- +- spin_lock(&mtdblks_lock); +- +- /* If it's already open, no need to piss about. */ + if (mtdblks[dev]) { + mtdblks[dev]->count++; +- spin_unlock(&mtdblks_lock); +- put_mtd_device(mtd); + return 0; + } + +- /* OK, it's not open. Try to find it */ +- +- /* First we have to drop the lock, because we have to +- to things which might sleep. +- */ +- spin_unlock(&mtdblks_lock); +- ++ /* OK, it's not open. Create cache info for it */ + mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); +- if (!mtdblk) { +- put_mtd_device(mtd); +- BLK_DEC_USE_COUNT; ++ if (!mtdblk) + return -ENOMEM; +- } ++ + memset(mtdblk, 0, sizeof(*mtdblk)); + mtdblk->count = 1; + mtdblk->mtd = mtd; +@@ -337,336 +287,102 @@ + if ((mtdblk->mtd->flags & MTD_CAP_RAM) != MTD_CAP_RAM && + mtdblk->mtd->erasesize) { + mtdblk->cache_size = mtdblk->mtd->erasesize; +- mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); +- if (!mtdblk->cache_data) { +- put_mtd_device(mtdblk->mtd); +- kfree(mtdblk); +- BLK_DEC_USE_COUNT; +- return -ENOMEM; +- } +- } +- +- /* OK, we've created a new one. Add it to the list. */ +- +- spin_lock(&mtdblks_lock); +- +- if (mtdblks[dev]) { +- /* Another CPU made one at the same time as us. */ +- mtdblks[dev]->count++; +- spin_unlock(&mtdblks_lock); +- put_mtd_device(mtdblk->mtd); +- vfree(mtdblk->cache_data); +- kfree(mtdblk); +- return 0; ++ mtdblk->cache_data = NULL; + } + + mtdblks[dev] = mtdblk; +- mtd_sizes[dev] = mtdblk->mtd->size/1024; +- if (mtdblk->mtd->erasesize) +- mtd_blksizes[dev] = mtdblk->mtd->erasesize; +- if (mtd_blksizes[dev] > PAGE_SIZE) +- mtd_blksizes[dev] = PAGE_SIZE; +- set_device_ro (inode->i_rdev, !(mtdblk->mtd->flags & MTD_WRITEABLE)); +- +- spin_unlock(&mtdblks_lock); + + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); + + return 0; + } + +-static release_t mtdblock_release(struct inode *inode, struct file *file) ++static int mtdblock_release(struct mtd_blktrans_dev *mbd) + { +- int dev; +- struct mtdblk_dev *mtdblk; +- DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); ++ int dev = mbd->devnum; ++ struct mtdblk_dev *mtdblk = mtdblks[dev]; + +- if (inode == NULL) +- release_return(-ENODEV); +- +- dev = MINOR(inode->i_rdev); +- mtdblk = mtdblks[dev]; ++ DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); + + down(&mtdblk->cache_sem); + write_cached_data(mtdblk); + up(&mtdblk->cache_sem); + +- spin_lock(&mtdblks_lock); + if (!--mtdblk->count) { + /* It was the last usage. Free the device */ + mtdblks[dev] = NULL; +- spin_unlock(&mtdblks_lock); + if (mtdblk->mtd->sync) + mtdblk->mtd->sync(mtdblk->mtd); +- put_mtd_device(mtdblk->mtd); + vfree(mtdblk->cache_data); + kfree(mtdblk); +- } else { +- spin_unlock(&mtdblks_lock); + } +- + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); + +- BLK_DEC_USE_COUNT; +- release_return(0); +-} +- +- +-/* +- * This is a special request_fn because it is executed in a process context +- * to be able to sleep independently of the caller. The io_request_lock +- * is held upon entry and exit. +- * The head of our request queue is considered active so there is no need +- * to dequeue requests before we are done. +- */ +-static void handle_mtdblock_request(void) +-{ +- struct request *req; +- struct mtdblk_dev *mtdblk; +- unsigned int res; +- +- for (;;) { +- INIT_REQUEST; +- req = CURRENT; +- spin_unlock_irq(&io_request_lock); +- mtdblk = mtdblks[MINOR(req->rq_dev)]; +- res = 0; +- +- if (MINOR(req->rq_dev) >= MAX_MTD_DEVICES) +- panic("%s: minor out of bounds", __FUNCTION__); +- +- if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9)) +- goto end_req; +- +- // Handle the request +- switch (req->cmd) +- { +- int err; +- +- case READ: +- down(&mtdblk->cache_sem); +- err = do_cached_read (mtdblk, req->sector << 9, +- req->current_nr_sectors << 9, +- req->buffer); +- up(&mtdblk->cache_sem); +- if (!err) +- res = 1; +- break; +- +- case WRITE: +- // Read only device +- if ( !(mtdblk->mtd->flags & MTD_WRITEABLE) ) +- break; +- +- // Do the write +- down(&mtdblk->cache_sem); +- err = do_cached_write (mtdblk, req->sector << 9, +- req->current_nr_sectors << 9, +- req->buffer); +- up(&mtdblk->cache_sem); +- if (!err) +- res = 1; +- break; +- } +- +-end_req: +- spin_lock_irq(&io_request_lock); +- end_request(res); +- } +-} +- +-static volatile int leaving = 0; +-static DECLARE_MUTEX_LOCKED(thread_sem); +-static DECLARE_WAIT_QUEUE_HEAD(thr_wq); +- +-int mtdblock_thread(void *dummy) +-{ +- struct task_struct *tsk = current; +- DECLARE_WAITQUEUE(wait, tsk); +- +- /* we might get involved when memory gets low, so use PF_MEMALLOC */ +- tsk->flags |= PF_MEMALLOC; +- strcpy(tsk->comm, "mtdblockd"); +- spin_lock_irq(&tsk->sigmask_lock); +- sigfillset(&tsk->blocked); +- recalc_sigpending(tsk); +- spin_unlock_irq(&tsk->sigmask_lock); +- daemonize(); +- +- while (!leaving) { +- add_wait_queue(&thr_wq, &wait); +- set_current_state(TASK_INTERRUPTIBLE); +- spin_lock_irq(&io_request_lock); +- if (QUEUE_EMPTY || QUEUE_PLUGGED) { +- spin_unlock_irq(&io_request_lock); +- schedule(); +- remove_wait_queue(&thr_wq, &wait); +- } else { +- remove_wait_queue(&thr_wq, &wait); +- set_current_state(TASK_RUNNING); +- handle_mtdblock_request(); +- spin_unlock_irq(&io_request_lock); +- } +- } +- +- up(&thread_sem); + return 0; + } + +-#if LINUX_VERSION_CODE < 0x20300 +-#define RQFUNC_ARG void +-#else +-#define RQFUNC_ARG request_queue_t *q +-#endif +- +-static void mtdblock_request(RQFUNC_ARG) ++static int mtdblock_flush(struct mtd_blktrans_dev *dev) + { +- /* Don't do anything, except wake the thread if necessary */ +- wake_up(&thr_wq); +-} ++ struct mtdblk_dev *mtdblk = mtdblks[dev->devnum]; + +- +-static int mtdblock_ioctl(struct inode * inode, struct file * file, +- unsigned int cmd, unsigned long arg) +-{ +- struct mtdblk_dev *mtdblk; +- +- mtdblk = mtdblks[MINOR(inode->i_rdev)]; +- +-#ifdef PARANOIA +- if (!mtdblk) +- BUG(); +-#endif +- +- switch (cmd) { +- case BLKGETSIZE: /* Return device size */ +- return put_user((mtdblk->mtd->size >> 9), (unsigned long *) arg); +- +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- return put_user((u64)mtdblk->mtd->size, (u64 *)arg); +-#endif +- +- case BLKFLSBUF: +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if(!capable(CAP_SYS_ADMIN)) +- return -EACCES; +-#endif +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); + down(&mtdblk->cache_sem); + write_cached_data(mtdblk); + up(&mtdblk->cache_sem); ++ + if (mtdblk->mtd->sync) + mtdblk->mtd->sync(mtdblk->mtd); + return 0; +- +- default: +- return -EINVAL; +- } + } + +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations mtd_fops = ++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { +- open: mtdblock_open, +- ioctl: mtdblock_ioctl, +- release: mtdblock_release, +- read: block_read, +- write: block_write +-}; +-#else +-static struct block_device_operations mtd_fops = +-{ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: mtdblock_open, +- release: mtdblock_release, +- ioctl: mtdblock_ioctl +-}; +-#endif ++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); + +-#ifdef CONFIG_DEVFS_FS +-/* Notification that a new device has been added. Create the devfs entry for +- * it. */ +- +-static void mtd_notify_add(struct mtd_info* mtd) +-{ +- char name[8]; +- +- if (!mtd || mtd->type == MTD_ABSENT) ++ if (!dev) + return; + +- sprintf(name, "%d", mtd->index); +- devfs_rw_handle[mtd->index] = devfs_register(devfs_dir_handle, name, +- DEVFS_FL_DEFAULT, MTD_BLOCK_MAJOR, mtd->index, +- S_IFBLK | S_IRUGO | S_IWUGO, +- &mtd_fops, NULL); +-} +- +-static void mtd_notify_remove(struct mtd_info* mtd) +-{ +- if (!mtd || mtd->type == MTD_ABSENT) +- return; ++ memset(dev, 0, sizeof(*dev)); + +- devfs_unregister(devfs_rw_handle[mtd->index]); +-} +-#endif ++ dev->mtd = mtd; ++ dev->devnum = mtd->index; ++ dev->blksize = 512; ++ dev->size = mtd->size >> 9; ++ dev->tr = tr; ++ ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ dev->readonly = 1; ++ ++ add_mtd_blktrans_dev(dev); ++} ++ ++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) ++{ ++ del_mtd_blktrans_dev(dev); ++ kfree(dev); ++} ++ ++struct mtd_blktrans_ops mtdblock_tr = { ++ .name = "mtdblock", ++ .major = 31, ++ .part_bits = 0, ++ .open = mtdblock_open, ++ .flush = mtdblock_flush, ++ .release = mtdblock_release, ++ .readsect = mtdblock_readsect, ++ .writesect = mtdblock_writesect, ++ .add_mtd = mtdblock_add_mtd, ++ .remove_dev = mtdblock_remove_dev, ++ .owner = THIS_MODULE, ++}; + + int __init init_mtdblock(void) + { +- int i; +- +- spin_lock_init(&mtdblks_lock); +-#ifdef CONFIG_DEVFS_FS +- if (devfs_register_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME, &mtd_fops)) +- { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_BLOCK_MAJOR); +- return -EAGAIN; +- } +- +- devfs_dir_handle = devfs_mk_dir(NULL, DEVICE_NAME, NULL); +- register_mtd_user(¬ifier); +-#else +- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_BLOCK_MAJOR); +- return -EAGAIN; +- } +-#endif +- +- /* We fill it in at open() time. */ +- for (i=0; i< MAX_MTD_DEVICES; i++) { +- mtd_sizes[i] = 0; +- mtd_blksizes[i] = BLOCK_SIZE; +- } +- init_waitqueue_head(&thr_wq); +- /* Allow the block size to default to BLOCK_SIZE. */ +- blksize_size[MAJOR_NR] = mtd_blksizes; +- blk_size[MAJOR_NR] = mtd_sizes; +- +- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); +- kernel_thread (mtdblock_thread, NULL, CLONE_FS|CLONE_FILES|CLONE_SIGHAND); +- return 0; ++ return register_mtd_blktrans(&mtdblock_tr); + } + + static void __exit cleanup_mtdblock(void) + { +- leaving = 1; +- wake_up(&thr_wq); +- down(&thread_sem); +-#ifdef CONFIG_DEVFS_FS +- unregister_mtd_user(¬ifier); +- devfs_unregister(devfs_dir_handle); +- devfs_unregister_blkdev(MTD_BLOCK_MAJOR, DEVICE_NAME); +-#else +- unregister_blkdev(MAJOR_NR,DEVICE_NAME); +-#endif +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); +- blksize_size[MAJOR_NR] = NULL; +- blk_size[MAJOR_NR] = NULL; ++ deregister_mtd_blktrans(&mtdblock_tr); + } + + module_init(init_mtdblock); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdblock_ro.c linux/drivers/mtd/mtdblock_ro.c +--- linux-mips-2.4.27/drivers/mtd/mtdblock_ro.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdblock_ro.c 2004-11-19 10:25:11.645238400 +0100 +@@ -1,301 +1,87 @@ + /* +- * $Id: mtdblock_ro.c,v 1.12 2001/11/20 11:42:33 dwmw2 Exp $ ++ * $Id: mtdblock_ro.c,v 1.18 2003/06/23 12:00:08 dwmw2 Exp $ + * +- * Read-only version of the mtdblock device, without the +- * read/erase/modify/writeback stuff ++ * (C) 2003 David Woodhouse <dwmw2@infradead.org> ++ * ++ * Simple read-only (writable only for RAM) mtdblock driver + */ + +-#ifdef MTDBLOCK_DEBUG +-#define DEBUGLVL debug +-#endif +- +- +-#include <linux/module.h> +-#include <linux/types.h> +- ++#include <linux/init.h> ++#include <linux/slab.h> + #include <linux/mtd/mtd.h> +-#include <linux/mtd/compatmac.h> +- +-#define MAJOR_NR MTD_BLOCK_MAJOR +-#define DEVICE_NAME "mtdblock" +-#define DEVICE_REQUEST mtdblock_request +-#define DEVICE_NR(device) (device) +-#define DEVICE_ON(device) +-#define DEVICE_OFF(device) +-#define DEVICE_NO_RANDOM +-#include <linux/blk.h> +- +-#if LINUX_VERSION_CODE < 0x20300 +-#define RQFUNC_ARG void +-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) +-#else +-#define RQFUNC_ARG request_queue_t *q +-#endif +- +-#ifdef MTDBLOCK_DEBUG +-static int debug = MTDBLOCK_DEBUG; +-MODULE_PARM(debug, "i"); +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif +- +-static int mtd_sizes[MAX_MTD_DEVICES]; ++#include <linux/mtd/blktrans.h> + +- +-static int mtdblock_open(struct inode *inode, struct file *file) ++static int mtdblock_readsect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) + { +- struct mtd_info *mtd = NULL; +- +- int dev; +- +- DEBUG(1,"mtdblock_open\n"); +- +- if (inode == 0) +- return -EINVAL; +- +- dev = MINOR(inode->i_rdev); +- +- mtd = get_mtd_device(NULL, dev); +- if (!mtd) +- return -EINVAL; +- if (MTD_ABSENT == mtd->type) { +- put_mtd_device(mtd); +- return -EINVAL; +- } +- +- BLK_INC_USE_COUNT; +- +- mtd_sizes[dev] = mtd->size>>9; +- +- DEBUG(1, "ok\n"); ++ size_t retlen; + ++ if (dev->mtd->read(dev->mtd, (block * 512), 512, &retlen, buf)) ++ return 1; + return 0; + } + +-static release_t mtdblock_release(struct inode *inode, struct file *file) ++static int mtdblock_writesect(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buf) + { +- int dev; +- struct mtd_info *mtd; +- +- DEBUG(1, "mtdblock_release\n"); +- +- if (inode == NULL) +- release_return(-ENODEV); +- +- dev = MINOR(inode->i_rdev); +- mtd = __get_mtd_device(NULL, dev); +- +- if (!mtd) { +- printk(KERN_WARNING "MTD device is absent on mtd_release!\n"); +- BLK_DEC_USE_COUNT; +- release_return(-ENODEV); +- } +- +- if (mtd->sync) +- mtd->sync(mtd); +- +- put_mtd_device(mtd); +- +- DEBUG(1, "ok\n"); ++ size_t retlen; + +- BLK_DEC_USE_COUNT; +- release_return(0); ++ if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf)) ++ return 1; ++ return 0; + } + +- +-static void mtdblock_request(RQFUNC_ARG) ++static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { +- struct request *current_request; +- unsigned int res = 0; +- struct mtd_info *mtd; +- +- while (1) +- { +- /* Grab the Request and unlink it from the request list, INIT_REQUEST +- will execute a return if we are done. */ +- INIT_REQUEST; +- current_request = CURRENT; +- +- if (MINOR(current_request->rq_dev) >= MAX_MTD_DEVICES) +- { +- printk("mtd: Unsupported device!\n"); +- end_request(0); +- continue; +- } +- +- // Grab our MTD structure +- +- mtd = __get_mtd_device(NULL, MINOR(current_request->rq_dev)); +- if (!mtd) { +- printk("MTD device %d doesn't appear to exist any more\n", CURRENT_DEV); +- end_request(0); +- } +- +- if (current_request->sector << 9 > mtd->size || +- (current_request->sector + current_request->current_nr_sectors) << 9 > mtd->size) +- { +- printk("mtd: Attempt to read past end of device!\n"); +- printk("size: %x, sector: %lx, nr_sectors %lx\n", mtd->size, +- current_request->sector, current_request->current_nr_sectors); +- end_request(0); +- continue; +- } +- +- /* Remove the request we are handling from the request list so nobody messes +- with it */ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- /* Now drop the lock that the ll_rw_blk functions grabbed for us +- and process the request. This is necessary due to the extreme time +- we spend processing it. */ +- spin_unlock_irq(&io_request_lock); +-#endif +- +- // Handle the request +- switch (current_request->cmd) +- { +- size_t retlen; ++ struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); + +- case READ: +- if (MTD_READ(mtd,current_request->sector<<9, +- current_request->current_nr_sectors << 9, +- &retlen, current_request->buffer) == 0) +- res = 1; +- else +- res = 0; +- break; ++ if (!dev) ++ return; + +- case WRITE: ++ memset(dev, 0, sizeof(*dev)); + +- /* printk("mtdblock_request WRITE sector=%d(%d)\n",current_request->sector, +- current_request->current_nr_sectors); +- */ ++ dev->mtd = mtd; ++ dev->devnum = mtd->index; ++ dev->blksize = 512; ++ dev->size = mtd->size >> 9; ++ dev->tr = tr; ++ if ((mtd->flags & (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) != ++ (MTD_CLEAR_BITS|MTD_SET_BITS|MTD_WRITEABLE)) ++ dev->readonly = 1; + +- // Read only device +- if ((mtd->flags & MTD_CAP_RAM) == 0) +- { +- res = 0; +- break; +- } +- +- // Do the write +- if (MTD_WRITE(mtd,current_request->sector<<9, +- current_request->current_nr_sectors << 9, +- &retlen, current_request->buffer) == 0) +- res = 1; +- else +- res = 0; +- break; +- +- // Shouldn't happen +- default: +- printk("mtd: unknown request\n"); +- break; +- } +- +- // Grab the lock and re-thread the item onto the linked list +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- spin_lock_irq(&io_request_lock); +-#endif +- end_request(res); +- } ++ add_mtd_blktrans_dev(dev); + } + +- +- +-static int mtdblock_ioctl(struct inode * inode, struct file * file, +- unsigned int cmd, unsigned long arg) ++static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) + { +- struct mtd_info *mtd; +- +- mtd = __get_mtd_device(NULL, MINOR(inode->i_rdev)); +- +- if (!mtd) return -EINVAL; +- +- switch (cmd) { +- case BLKGETSIZE: /* Return device size */ +- return put_user((mtd->size >> 9), (unsigned long *) arg); +- +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- return put_user((u64)mtd->size, (u64 *)arg); +-#endif +- +- case BLKFLSBUF: +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) +- if(!capable(CAP_SYS_ADMIN)) return -EACCES; +-#endif +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); +- if (mtd->sync) +- mtd->sync(mtd); +- return 0; +- +- default: +- return -ENOTTY; +- } ++ del_mtd_blktrans_dev(dev); ++ kfree(dev); + } + +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations mtd_fops = +-{ +- open: mtdblock_open, +- ioctl: mtdblock_ioctl, +- release: mtdblock_release, +- read: block_read, +- write: block_write +-}; +-#else +-static struct block_device_operations mtd_fops = +-{ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: mtdblock_open, +- release: mtdblock_release, +- ioctl: mtdblock_ioctl ++struct mtd_blktrans_ops mtdblock_tr = { ++ .name = "mtdblock", ++ .major = 31, ++ .part_bits = 0, ++ .readsect = mtdblock_readsect, ++ .writesect = mtdblock_writesect, ++ .add_mtd = mtdblock_add_mtd, ++ .remove_dev = mtdblock_remove_dev, ++ .owner = THIS_MODULE, + }; +-#endif + +-int __init init_mtdblock(void) ++static int __init mtdblock_init(void) + { +- int i; +- +- if (register_blkdev(MAJOR_NR,DEVICE_NAME,&mtd_fops)) { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_BLOCK_MAJOR); +- return -EAGAIN; +- } +- +- /* We fill it in at open() time. */ +- for (i=0; i< MAX_MTD_DEVICES; i++) { +- mtd_sizes[i] = 0; +- } +- +- /* Allow the block size to default to BLOCK_SIZE. */ +- blksize_size[MAJOR_NR] = NULL; +- blk_size[MAJOR_NR] = mtd_sizes; +- +- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &mtdblock_request); +- return 0; ++ return register_mtd_blktrans(&mtdblock_tr); + } + +-static void __exit cleanup_mtdblock(void) ++static void __exit mtdblock_exit(void) + { +- unregister_blkdev(MAJOR_NR,DEVICE_NAME); +- blk_size[MAJOR_NR] = NULL; +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); ++ deregister_mtd_blktrans(&mtdblock_tr); + } + +-module_init(init_mtdblock); +-module_exit(cleanup_mtdblock); +- ++module_init(mtdblock_init); ++module_exit(mtdblock_exit); + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Erwin Authried <eauth@softsys.co.at> et al."); ++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); + MODULE_DESCRIPTION("Simple read-only block device emulation access to MTD devices"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdchar.c linux/drivers/mtd/mtdchar.c +--- linux-mips-2.4.27/drivers/mtd/mtdchar.c 2004-08-14 20:38:51.000000000 +0200 ++++ linux/drivers/mtd/mtdchar.c 2004-11-19 10:25:11.647238096 +0100 +@@ -1,8 +1,7 @@ + /* +- * $Id: mtdchar.c,v 1.49 2003/01/24 12:02:58 dwmw2 Exp $ ++ * $Id: mtdchar.c,v 1.56 2003/11/14 19:50:03 thayne Exp $ + * + * Character-device access to raw MTD devices. +- * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c + * + */ + +@@ -10,7 +9,11 @@ + #include <linux/kernel.h> + #include <linux/module.h> + #include <linux/mtd/mtd.h> ++#include <linux/mtd/compatmac.h> + #include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/fs.h> ++#include <asm/uaccess.h> + + #ifdef CONFIG_DEVFS_FS + #include <linux/devfs_fs_kernel.h> +@@ -18,8 +21,8 @@ + static void mtd_notify_remove(struct mtd_info* mtd); + + static struct mtd_notifier notifier = { +- add: mtd_notify_add, +- remove: mtd_notify_remove, ++ .add = mtd_notify_add, ++ .remove = mtd_notify_remove, + }; + + static devfs_handle_t devfs_dir_handle; +@@ -60,7 +63,7 @@ + + static int mtd_open(struct inode *inode, struct file *file) + { +- int minor = minor(inode->i_rdev); ++ int minor = iminor(inode); + int devnum = minor >> 1; + struct mtd_info *mtd; + +@@ -125,15 +128,11 @@ + int ret=0; + int len; + char *kbuf; +- loff_t pos = *ppos; + + DEBUG(MTD_DEBUG_LEVEL0,"MTD_read\n"); + +- if (pos < 0 || pos > mtd->size) +- return 0; +- +- if (count > mtd->size - pos) +- count = mtd->size - pos; ++ if (*ppos + count > mtd->size) ++ count = mtd->size - *ppos; + + if (!count) + return 0; +@@ -150,9 +149,9 @@ + if (!kbuf) + return -ENOMEM; + +- ret = MTD_READ(mtd, pos, len, &retlen, kbuf); ++ ret = MTD_READ(mtd, *ppos, len, &retlen, kbuf); + if (!ret) { +- pos += retlen; ++ *ppos += retlen; + if (copy_to_user(buf, kbuf, retlen)) { + kfree(kbuf); + return -EFAULT; +@@ -171,8 +170,6 @@ + kfree(kbuf); + } + +- *ppos = pos; +- + return total_retlen; + } /* mtd_read */ + +@@ -182,17 +179,16 @@ + char *kbuf; + size_t retlen; + size_t total_retlen=0; +- loff_t pos = *ppos; + int ret=0; + int len; + + DEBUG(MTD_DEBUG_LEVEL0,"MTD_write\n"); + +- if (pos < 0 || pos >= mtd->size) ++ if (*ppos == mtd->size) + return -ENOSPC; + +- if (count > mtd->size - pos) +- count = mtd->size - pos; ++ if (*ppos + count > mtd->size) ++ count = mtd->size - *ppos; + + if (!count) + return 0; +@@ -214,9 +210,9 @@ + return -EFAULT; + } + +- ret = (*(mtd->write))(mtd, pos, len, &retlen, kbuf); ++ ret = (*(mtd->write))(mtd, *ppos, len, &retlen, kbuf); + if (!ret) { +- pos += retlen; ++ *ppos += retlen; + total_retlen += retlen; + count -= retlen; + buf += retlen; +@@ -228,7 +224,6 @@ + + kfree(kbuf); + } +- *ppos = pos; + + return total_retlen; + } /* mtd_write */ +@@ -450,81 +445,13 @@ + break; + } + +- case MEMWRITEDATA: +- { +- struct mtd_oob_buf buf; +- void *databuf; +- ssize_t retlen; +- +- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) +- return -EFAULT; +- +- if (buf.length > 0x4096) +- return -EINVAL; +- +- if (!mtd->write_ecc) +- ret = -EOPNOTSUPP; +- else +- ret = verify_area(VERIFY_READ, (char *)buf.ptr, buf.length); +- +- if (ret) +- return ret; +- +- databuf = kmalloc(buf.length, GFP_KERNEL); +- if (!databuf) +- return -ENOMEM; +- +- if (copy_from_user(databuf, buf.ptr, buf.length)) { +- kfree(databuf); +- return -EFAULT; +- } +- +- ret = (mtd->write_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0); +- +- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) +- ret = -EFAULT; +- +- kfree(databuf); +- break; +- +- } +- +- case MEMREADDATA: ++ case MEMSETOOBSEL: + { +- struct mtd_oob_buf buf; +- void *databuf; +- ssize_t retlen = 0; +- +- if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) ++ if (copy_from_user(&mtd->oobinfo ,(void *)arg, sizeof(struct nand_oobinfo))) + return -EFAULT; +- +- if (buf.length > 0x4096) +- return -EINVAL; +- +- if (!mtd->read_ecc) +- ret = -EOPNOTSUPP; +- else +- ret = verify_area(VERIFY_WRITE, (char *)buf.ptr, buf.length); +- +- if (ret) +- return ret; +- +- databuf = kmalloc(buf.length, GFP_KERNEL); +- if (!databuf) +- return -ENOMEM; +- +- ret = (mtd->read_ecc)(mtd, buf.start, buf.length, &retlen, databuf, NULL, 0); +- +- if (copy_to_user((void *)arg + sizeof(u_int32_t), &retlen, sizeof(u_int32_t))) +- ret = -EFAULT; +- else if (retlen && copy_to_user(buf.ptr, databuf, retlen)) +- ret = -EFAULT; +- +- kfree(databuf); + break; + } + +- + default: + DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO); + ret = -ENOTTY; +@@ -534,13 +461,13 @@ + } /* memory_ioctl */ + + static struct file_operations mtd_fops = { +- owner: THIS_MODULE, +- llseek: mtd_lseek, /* lseek */ +- read: mtd_read, /* read */ +- write: mtd_write, /* write */ +- ioctl: mtd_ioctl, /* ioctl */ +- open: mtd_open, /* open */ +- release: mtd_close, /* release */ ++ .owner = THIS_MODULE, ++ .llseek = mtd_lseek, ++ .read = mtd_read, ++ .write = mtd_write, ++ .ioctl = mtd_ioctl, ++ .open = mtd_open, ++ .release = mtd_close, + }; + + +@@ -580,26 +507,18 @@ + + static int __init init_mtdchar(void) + { +-#ifdef CONFIG_DEVFS_FS +- if (devfs_register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) ++ if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) + { + printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", + MTD_CHAR_MAJOR); + return -EAGAIN; + } + ++#ifdef CONFIG_DEVFS_FS + devfs_dir_handle = devfs_mk_dir(NULL, "mtd", NULL); + + register_mtd_user(¬ifier); +-#else +- if (register_chrdev(MTD_CHAR_MAJOR, "mtd", &mtd_fops)) +- { +- printk(KERN_NOTICE "Can't allocate major number %d for Memory Technology Devices.\n", +- MTD_CHAR_MAJOR); +- return -EAGAIN; +- } + #endif +- + return 0; + } + +@@ -608,10 +527,8 @@ + #ifdef CONFIG_DEVFS_FS + unregister_mtd_user(¬ifier); + devfs_unregister(devfs_dir_handle); +- devfs_unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); +-#else +- unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + #endif ++ unregister_chrdev(MTD_CHAR_MAJOR, "mtd"); + } + + module_init(init_mtdchar); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdconcat.c linux/drivers/mtd/mtdconcat.c +--- linux-mips-2.4.27/drivers/mtd/mtdconcat.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdconcat.c 2004-11-19 10:25:11.649237792 +0100 +@@ -3,9 +3,11 @@ + * + * (C) 2002 Robert Kaiser <rkaiser@sysgo.de> + * ++ * NAND support by Christian Gan <cgan@iders.ca> ++ * + * This code is GPL + * +- * $Id: mtdconcat.c,v 1.3 2002/05/21 21:04:25 dwmw2 Exp $ ++ * $Id: mtdconcat.c,v 1.8 2003/06/30 11:01:26 dwmw2 Exp $ + */ + + #include <linux/module.h> +@@ -35,21 +37,20 @@ + #define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ + ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) + +- + /* + * Given a pointer to the MTD object in the mtd_concat structure, + * we can retrieve the pointer to that structure with this macro. + */ + #define CONCAT(x) ((struct mtd_concat *)(x)) + +- + /* + * MTD methods which look up the relevant subdevice, translate the + * effective address and pass through to the subdevice. + */ + +-static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf) ++static int ++concat_read(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf) + { + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; +@@ -57,43 +58,43 @@ + + *retlen = 0; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + +- if (from >= subdev->size) +- { ++ if (from >= subdev->size) { ++ /* Not destined for this subdev */ + size = 0; + from -= subdev->size; ++ continue; + } +- else +- { + if (from + len > subdev->size) ++ /* First part goes into this subdev */ + size = subdev->size - from; + else ++ /* Entire transaction goes into this subdev */ + size = len; + + err = subdev->read(subdev, from, size, &retsize, buf); + +- if(err) ++ if (err) + break; + + *retlen += retsize; + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + buf += size; + from = 0; + } +- } + return err; + } + +-static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, +- size_t *retlen, const u_char *buf) ++static int ++concat_write(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf) + { + struct mtd_concat *concat = CONCAT(mtd); + int err = -EINVAL; +@@ -104,18 +105,15 @@ + + *retlen = 0; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size, retsize; + +- if (to >= subdev->size) +- { ++ if (to >= subdev->size) { + size = 0; + to -= subdev->size; ++ continue; + } +- else +- { + if (to + len > subdev->size) + size = subdev->size - to; + else +@@ -126,25 +124,232 @@ + else + err = subdev->write(subdev, to, size, &retsize, buf); + +- if(err) ++ if (err) + break; + + *retlen += retsize; + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } ++ return err; ++} ++ ++static int ++concat_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf, u_char * eccbuf, ++ struct nand_oobinfo *oobsel) ++{ ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (from >= subdev->size) { ++ /* Not destined for this subdev */ ++ size = 0; ++ from -= subdev->size; ++ continue; ++ } ++ ++ if (from + len > subdev->size) ++ /* First part goes into this subdev */ ++ size = subdev->size - from; ++ else ++ /* Entire transaction goes into this subdev */ ++ size = len; ++ ++ if (subdev->read_ecc) ++ err = subdev->read_ecc(subdev, from, size, ++ &retsize, buf, eccbuf, oobsel); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ if (eccbuf) { ++ eccbuf += subdev->oobsize; ++ /* in nand.c at least, eccbufs are ++ tagged with 2 (int)eccstatus'; we ++ must account for these */ ++ eccbuf += 2 * (sizeof (int)); ++ } ++ from = 0; + } + return err; + } + +-static void concat_erase_callback (struct erase_info *instr) ++static int ++concat_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf, u_char * eccbuf, ++ struct nand_oobinfo *oobsel) + { +- wake_up((wait_queue_head_t *)instr->priv); ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ return -EROFS; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (to >= subdev->size) { ++ size = 0; ++ to -= subdev->size; ++ continue; ++ } ++ if (to + len > subdev->size) ++ size = subdev->size - to; ++ else ++ size = len; ++ ++ if (!(subdev->flags & MTD_WRITEABLE)) ++ err = -EROFS; ++ else if (subdev->write_ecc) ++ err = subdev->write_ecc(subdev, to, size, ++ &retsize, buf, eccbuf, oobsel); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ if (eccbuf) ++ eccbuf += subdev->oobsize; ++ to = 0; ++ } ++ return err; ++} ++ ++static int ++concat_read_oob(struct mtd_info *mtd, loff_t from, size_t len, ++ size_t * retlen, u_char * buf) ++{ ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (from >= subdev->size) { ++ /* Not destined for this subdev */ ++ size = 0; ++ from -= subdev->size; ++ continue; ++ } ++ if (from + len > subdev->size) ++ /* First part goes into this subdev */ ++ size = subdev->size - from; ++ else ++ /* Entire transaction goes into this subdev */ ++ size = len; ++ ++ if (subdev->read_oob) ++ err = subdev->read_oob(subdev, from, size, ++ &retsize, buf); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ from = 0; ++ } ++ return err; ++} ++ ++static int ++concat_write_oob(struct mtd_info *mtd, loff_t to, size_t len, ++ size_t * retlen, const u_char * buf) ++{ ++ struct mtd_concat *concat = CONCAT(mtd); ++ int err = -EINVAL; ++ int i; ++ ++ if (!(mtd->flags & MTD_WRITEABLE)) ++ return -EROFS; ++ ++ *retlen = 0; ++ ++ for (i = 0; i < concat->num_subdev; i++) { ++ struct mtd_info *subdev = concat->subdev[i]; ++ size_t size, retsize; ++ ++ if (to >= subdev->size) { ++ size = 0; ++ to -= subdev->size; ++ continue; ++ } ++ if (to + len > subdev->size) ++ size = subdev->size - to; ++ else ++ size = len; ++ ++ if (!(subdev->flags & MTD_WRITEABLE)) ++ err = -EROFS; ++ else if (subdev->write_oob) ++ err = subdev->write_oob(subdev, to, size, &retsize, ++ buf); ++ else ++ err = -EINVAL; ++ ++ if (err) ++ break; ++ ++ *retlen += retsize; ++ len -= size; ++ if (len == 0) ++ break; ++ ++ err = -EINVAL; ++ buf += size; ++ to = 0; ++ } ++ return err; ++} ++ ++static void concat_erase_callback(struct erase_info *instr) ++{ ++ wake_up((wait_queue_head_t *) instr->priv); + } + + static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) +@@ -160,18 +365,18 @@ + + erase->mtd = mtd; + erase->callback = concat_erase_callback; +- erase->priv = (unsigned long)&waitq; ++ erase->priv = (unsigned long) &waitq; + + /* + * FIXME: Allow INTERRUPTIBLE. Which means + * not having the wait_queue head on the stack. + */ + err = mtd->erase(mtd, erase); +- if (!err) +- { ++ if (!err) { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&waitq, &wait); +- if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) ++ if (erase->state != MTD_ERASE_DONE ++ && erase->state != MTD_ERASE_FAILED) + schedule(); + remove_wait_queue(&waitq, &wait); + set_current_state(TASK_RUNNING); +@@ -181,7 +386,7 @@ + return err; + } + +-static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) ++static int concat_erase(struct mtd_info *mtd, struct erase_info *instr) + { + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_info *subdev; +@@ -192,10 +397,10 @@ + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + +- if(instr->addr > concat->mtd.size) ++ if (instr->addr > concat->mtd.size) + return -EINVAL; + +- if(instr->len + instr->addr > concat->mtd.size) ++ if (instr->len + instr->addr > concat->mtd.size) + return -EINVAL; + + /* +@@ -204,23 +409,22 @@ + * region info rather than looking at each particular sub-device + * in turn. + */ +- if (!concat->mtd.numeraseregions) +- { /* the easy case: device has uniform erase block size */ +- if(instr->addr & (concat->mtd.erasesize - 1)) ++ if (!concat->mtd.numeraseregions) { ++ /* the easy case: device has uniform erase block size */ ++ if (instr->addr & (concat->mtd.erasesize - 1)) + return -EINVAL; +- if(instr->len & (concat->mtd.erasesize - 1)) ++ if (instr->len & (concat->mtd.erasesize - 1)) + return -EINVAL; +- } +- else +- { /* device has variable erase size */ +- struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions; ++ } else { ++ /* device has variable erase size */ ++ struct mtd_erase_region_info *erase_regions = ++ concat->mtd.eraseregions; + + /* + * Find the erase region where the to-be-erased area begins: + */ +- for(i = 0; i < concat->mtd.numeraseregions && +- instr->addr >= erase_regions[i].offset; i++) +- ; ++ for (i = 0; i < concat->mtd.numeraseregions && ++ instr->addr >= erase_regions[i].offset; i++) ; + --i; + + /* +@@ -228,25 +432,26 @@ + * to-be-erased area begins. Verify that the starting + * offset is aligned to this region's erase size: + */ +- if (instr->addr & (erase_regions[i].erasesize-1)) ++ if (instr->addr & (erase_regions[i].erasesize - 1)) + return -EINVAL; + + /* + * now find the erase region where the to-be-erased area ends: + */ +- for(; i < concat->mtd.numeraseregions && +- (instr->addr + instr->len) >= erase_regions[i].offset ; ++i) +- ; ++ for (; i < concat->mtd.numeraseregions && ++ (instr->addr + instr->len) >= erase_regions[i].offset; ++ ++i) ; + --i; + /* + * check if the ending offset is aligned to this region's erase size + */ +- if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1)) ++ if ((instr->addr + instr->len) & (erase_regions[i].erasesize - ++ 1)) + return -EINVAL; + } + + /* make a local copy of instr to avoid modifying the caller's struct */ +- erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL); ++ erase = kmalloc(sizeof (struct erase_info), GFP_KERNEL); + + if (!erase) + return -ENOMEM; +@@ -258,39 +463,40 @@ + * find the subdevice where the to-be-erased area begins, adjust + * starting offset to be relative to the subdevice start + */ +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + subdev = concat->subdev[i]; +- if(subdev->size <= erase->addr) ++ if (subdev->size <= erase->addr) + erase->addr -= subdev->size; + else + break; + } +- if(i >= concat->num_subdev) /* must never happen since size */ +- BUG(); /* limit has been verified above */ ++ ++ /* must never happen since size limit has been verified above */ ++ if (i >= concat->num_subdev) ++ BUG(); + + /* now do the erase: */ + err = 0; +- for(;length > 0; i++) /* loop for all subevices affected by this request */ +- { ++ for (; length > 0; i++) { ++ /* loop for all subdevices affected by this request */ + subdev = concat->subdev[i]; /* get current subdevice */ + + /* limit length to subdevice's size: */ +- if(erase->addr + length > subdev->size) ++ if (erase->addr + length > subdev->size) + erase->len = subdev->size - erase->addr; + else + erase->len = length; + +- if (!(subdev->flags & MTD_WRITEABLE)) +- { ++ if (!(subdev->flags & MTD_WRITEABLE)) { + err = -EROFS; + break; + } + length -= erase->len; +- if ((err = concat_dev_erase(subdev, erase))) +- { +- if(err == -EINVAL) /* sanity check: must never happen since */ +- BUG(); /* block alignment has been checked above */ ++ if ((err = concat_dev_erase(subdev, erase))) { ++ /* sanity check: should never happen since ++ * block alignment has been checked above */ ++ if (err == -EINVAL) ++ BUG(); + break; + } + /* +@@ -313,7 +519,7 @@ + return 0; + } + +-static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len) ++static int concat_lock(struct mtd_info *mtd, loff_t ofs, size_t len) + { + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; +@@ -321,18 +527,15 @@ + if ((len + ofs) > mtd->size) + return -EINVAL; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + +- if (ofs >= subdev->size) +- { ++ if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; ++ continue; + } +- else +- { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else +@@ -340,21 +543,21 @@ + + err = subdev->lock(subdev, ofs, size); + +- if(err) ++ if (err) + break; + + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } +- } ++ + return err; + } + +-static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) ++static int concat_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) + { + struct mtd_concat *concat = CONCAT(mtd); + int i, err = 0; +@@ -362,18 +565,15 @@ + if ((len + ofs) > mtd->size) + return -EINVAL; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + +- if (ofs >= subdev->size) +- { ++ if (ofs >= subdev->size) { + size = 0; + ofs -= subdev->size; ++ continue; + } +- else +- { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else +@@ -381,17 +581,17 @@ + + err = subdev->unlock(subdev, ofs, size); + +- if(err) ++ if (err) + break; + + len -= size; +- if(len == 0) ++ if (len == 0) + break; + + err = -EINVAL; + ofs = 0; + } +- } ++ + return err; + } + +@@ -400,8 +600,7 @@ + struct mtd_concat *concat = CONCAT(mtd); + int i; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + subdev->sync(subdev); + } +@@ -412,10 +611,9 @@ + struct mtd_concat *concat = CONCAT(mtd); + int i, rc = 0; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; +- if((rc = subdev->suspend(subdev)) < 0) ++ if ((rc = subdev->suspend(subdev)) < 0) + return rc; + } + return rc; +@@ -426,8 +624,7 @@ + struct mtd_concat *concat = CONCAT(mtd); + int i; + +- for(i = 0; i < concat->num_subdev; i++) +- { ++ for (i = 0; i < concat->num_subdev; i++) { + struct mtd_info *subdev = concat->subdev[i]; + subdev->resume(subdev); + } +@@ -439,11 +636,10 @@ + * stored to *new_dev upon success. This function does _not_ + * register any devices: this is the caller's responsibility. + */ +-struct mtd_info *mtd_concat_create( +- struct mtd_info *subdev[], /* subdevices to concatenate */ ++struct mtd_info *mtd_concat_create(struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ +- char *name) /* name for the new device */ +-{ ++ char *name) ++{ /* name for the new device */ + int i; + size_t size; + struct mtd_concat *concat; +@@ -451,21 +647,21 @@ + int num_erase_region; + + printk(KERN_NOTICE "Concatenating MTD devices:\n"); +- for(i = 0; i < num_devs; i++) ++ for (i = 0; i < num_devs; i++) + printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); + printk(KERN_NOTICE "into device \"%s\"\n", name); + + /* allocate the device structure */ + size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); +- concat = kmalloc (size, GFP_KERNEL); +- if(!concat) +- { +- printk ("memory allocation error while creating concatenated device \"%s\"\n", ++ concat = kmalloc(size, GFP_KERNEL); ++ if (!concat) { ++ printk ++ ("memory allocation error while creating concatenated device \"%s\"\n", + name); + return NULL; + } + memset(concat, 0, size); +- concat->subdev = (struct mtd_info **)(concat + 1); ++ concat->subdev = (struct mtd_info **) (concat + 1); + + /* + * Set up the new "super" device's MTD object structure, check for +@@ -479,39 +675,53 @@ + concat->mtd.oobsize = subdev[0]->oobsize; + concat->mtd.ecctype = subdev[0]->ecctype; + concat->mtd.eccsize = subdev[0]->eccsize; ++ if (subdev[0]->read_ecc) ++ concat->mtd.read_ecc = concat_read_ecc; ++ if (subdev[0]->write_ecc) ++ concat->mtd.write_ecc = concat_write_ecc; ++ if (subdev[0]->read_oob) ++ concat->mtd.read_oob = concat_read_oob; ++ if (subdev[0]->write_oob) ++ concat->mtd.write_oob = concat_write_oob; + + concat->subdev[0] = subdev[0]; + +- for(i = 1; i < num_devs; i++) +- { +- if(concat->mtd.type != subdev[i]->type) +- { ++ for (i = 1; i < num_devs; i++) { ++ if (concat->mtd.type != subdev[i]->type) { + kfree(concat); +- printk ("Incompatible device type on \"%s\"\n", subdev[i]->name); ++ printk("Incompatible device type on \"%s\"\n", ++ subdev[i]->name); + return NULL; + } +- if(concat->mtd.flags != subdev[i]->flags) +- { /* +- * Expect all flags except MTD_WRITEABLE to be equal on +- * all subdevices. ++ if (concat->mtd.flags != subdev[i]->flags) { ++ /* ++ * Expect all flags except MTD_WRITEABLE to be ++ * equal on all subdevices. + */ +- if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE) +- { ++ if ((concat->mtd.flags ^ subdev[i]-> ++ flags) & ~MTD_WRITEABLE) { + kfree(concat); +- printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name); ++ printk("Incompatible device flags on \"%s\"\n", ++ subdev[i]->name); + return NULL; +- } +- else /* if writeable attribute differs, make super device writeable */ +- concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE; ++ } else ++ /* if writeable attribute differs, ++ make super device writeable */ ++ concat->mtd.flags |= ++ subdev[i]->flags & MTD_WRITEABLE; + } + concat->mtd.size += subdev[i]->size; +- if(concat->mtd.oobblock != subdev[i]->oobblock || ++ if (concat->mtd.oobblock != subdev[i]->oobblock || + concat->mtd.oobsize != subdev[i]->oobsize || + concat->mtd.ecctype != subdev[i]->ecctype || +- concat->mtd.eccsize != subdev[i]->eccsize) +- { ++ concat->mtd.eccsize != subdev[i]->eccsize || ++ !concat->mtd.read_ecc != !subdev[i]->read_ecc || ++ !concat->mtd.write_ecc != !subdev[i]->write_ecc || ++ !concat->mtd.read_oob != !subdev[i]->read_oob || ++ !concat->mtd.write_oob != !subdev[i]->write_oob) { + kfree(concat); +- printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name); ++ printk("Incompatible OOB or ECC data on \"%s\"\n", ++ subdev[i]->name); + return NULL; + } + concat->subdev[i] = subdev[i]; +@@ -535,7 +745,6 @@ + concat->mtd.suspend = concat_suspend; + concat->mtd.resume = concat_resume; + +- + /* + * Combine the erase block size info of the subdevices: + * +@@ -544,44 +753,44 @@ + */ + max_erasesize = curr_erasesize = subdev[0]->erasesize; + num_erase_region = 1; +- for(i = 0; i < num_devs; i++) +- { +- if(subdev[i]->numeraseregions == 0) +- { /* current subdevice has uniform erase size */ +- if(subdev[i]->erasesize != curr_erasesize) +- { /* if it differs from the last subdevice's erase size, count it */ ++ for (i = 0; i < num_devs; i++) { ++ if (subdev[i]->numeraseregions == 0) { ++ /* current subdevice has uniform erase size */ ++ if (subdev[i]->erasesize != curr_erasesize) { ++ /* if it differs from the last subdevice's erase size, count it */ + ++num_erase_region; + curr_erasesize = subdev[i]->erasesize; +- if(curr_erasesize > max_erasesize) ++ if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } +- } +- else +- { /* current subdevice has variable erase size */ ++ } else { ++ /* current subdevice has variable erase size */ + int j; +- for(j = 0; j < subdev[i]->numeraseregions; j++) +- { /* walk the list of erase regions, count any changes */ +- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) +- { ++ for (j = 0; j < subdev[i]->numeraseregions; j++) { ++ ++ /* walk the list of erase regions, count any changes */ ++ if (subdev[i]->eraseregions[j].erasesize != ++ curr_erasesize) { + ++num_erase_region; +- curr_erasesize = subdev[i]->eraseregions[j].erasesize; +- if(curr_erasesize > max_erasesize) ++ curr_erasesize = ++ subdev[i]->eraseregions[j]. ++ erasesize; ++ if (curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + } + } + +- if(num_erase_region == 1) +- { /* ++ if (num_erase_region == 1) { ++ /* + * All subdevices have the same uniform erase size. + * This is easy: + */ + concat->mtd.erasesize = curr_erasesize; + concat->mtd.numeraseregions = 0; +- } +- else +- { /* ++ } else { ++ /* + * erase block size varies across the subdevices: allocate + * space to store the data describing the variable erase regions + */ +@@ -590,12 +799,13 @@ + + concat->mtd.erasesize = max_erasesize; + concat->mtd.numeraseregions = num_erase_region; +- concat->mtd.eraseregions = erase_region_p = kmalloc ( +- num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL); +- if(!erase_region_p) +- { ++ concat->mtd.eraseregions = erase_region_p = ++ kmalloc(num_erase_region * ++ sizeof (struct mtd_erase_region_info), GFP_KERNEL); ++ if (!erase_region_p) { + kfree(concat); +- printk ("memory allocation error while creating erase region list" ++ printk ++ ("memory allocation error while creating erase region list" + " for device \"%s\"\n", name); + return NULL; + } +@@ -606,41 +816,48 @@ + */ + curr_erasesize = subdev[0]->erasesize; + begin = position = 0; +- for(i = 0; i < num_devs; i++) +- { +- if(subdev[i]->numeraseregions == 0) +- { /* current subdevice has uniform erase size */ +- if(subdev[i]->erasesize != curr_erasesize) +- { /* ++ for (i = 0; i < num_devs; i++) { ++ if (subdev[i]->numeraseregions == 0) { ++ /* current subdevice has uniform erase size */ ++ if (subdev[i]->erasesize != curr_erasesize) { ++ /* + * fill in an mtd_erase_region_info structure for the area + * we have walked so far: + */ + erase_region_p->offset = begin; +- erase_region_p->erasesize = curr_erasesize; +- erase_region_p->numblocks = (position - begin) / curr_erasesize; ++ erase_region_p->erasesize = ++ curr_erasesize; ++ erase_region_p->numblocks = ++ (position - begin) / curr_erasesize; + begin = position; + + curr_erasesize = subdev[i]->erasesize; + ++erase_region_p; + } + position += subdev[i]->size; +- } +- else +- { /* current subdevice has variable erase size */ ++ } else { ++ /* current subdevice has variable erase size */ + int j; +- for(j = 0; j < subdev[i]->numeraseregions; j++) +- { /* walk the list of erase regions, count any changes */ +- if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) +- { ++ for (j = 0; j < subdev[i]->numeraseregions; j++) { ++ /* walk the list of erase regions, count any changes */ ++ if (subdev[i]->eraseregions[j]. ++ erasesize != curr_erasesize) { + erase_region_p->offset = begin; +- erase_region_p->erasesize = curr_erasesize; +- erase_region_p->numblocks = (position - begin) / curr_erasesize; ++ erase_region_p->erasesize = ++ curr_erasesize; ++ erase_region_p->numblocks = ++ (position - ++ begin) / curr_erasesize; + begin = position; + +- curr_erasesize = subdev[i]->eraseregions[j].erasesize; ++ curr_erasesize = ++ subdev[i]->eraseregions[j]. ++ erasesize; + ++erase_region_p; + } +- position += subdev[i]->eraseregions[j].numblocks * curr_erasesize; ++ position += ++ subdev[i]->eraseregions[j]. ++ numblocks * curr_erasesize; + } + } + } +@@ -660,16 +877,14 @@ + void mtd_concat_destroy(struct mtd_info *mtd) + { + struct mtd_concat *concat = CONCAT(mtd); +- if(concat->mtd.numeraseregions) ++ if (concat->mtd.numeraseregions) + kfree(concat->mtd.eraseregions); + kfree(concat); + } + +- + EXPORT_SYMBOL(mtd_concat_create); + EXPORT_SYMBOL(mtd_concat_destroy); + +- + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>"); + MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdcore.c linux/drivers/mtd/mtdcore.c +--- linux-mips-2.4.27/drivers/mtd/mtdcore.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdcore.c 2004-11-19 10:25:11.650237640 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: mtdcore.c,v 1.34 2003/01/24 23:32:25 dwmw2 Exp $ ++ * $Id: mtdcore.c,v 1.39 2003/05/21 15:15:03 dwmw2 Exp $ + * + * Core registration and callback routines for MTD + * drivers and users. +@@ -17,6 +17,7 @@ + #include <linux/major.h> + #include <linux/fs.h> + #include <linux/ioctl.h> ++#include <linux/init.h> + #include <linux/mtd/compatmac.h> + #ifdef CONFIG_PROC_FS + #include <linux/proc_fs.h> +@@ -24,9 +25,15 @@ + + #include <linux/mtd/mtd.h> + +-static DECLARE_MUTEX(mtd_table_mutex); +-static struct mtd_info *mtd_table[MAX_MTD_DEVICES]; +-static struct mtd_notifier *mtd_notifiers = NULL; ++/* These are exported solely for the purpose of mtd_blkdevs.c. You ++ should not use them for _anything_ else */ ++DECLARE_MUTEX(mtd_table_mutex); ++struct mtd_info *mtd_table[MAX_MTD_DEVICES]; ++ ++EXPORT_SYMBOL_GPL(mtd_table_mutex); ++EXPORT_SYMBOL_GPL(mtd_table); ++ ++static LIST_HEAD(mtd_notifiers); + + /** + * add_mtd_device - register an MTD device +@@ -44,21 +51,28 @@ + + down(&mtd_table_mutex); + +- for (i=0; i< MAX_MTD_DEVICES; i++) +- if (!mtd_table[i]) +- { +- struct mtd_notifier *not=mtd_notifiers; ++ for (i=0; i < MAX_MTD_DEVICES; i++) ++ if (!mtd_table[i]) { ++ struct list_head *this; + + mtd_table[i] = mtd; + mtd->index = i; ++ mtd->usecount = 0; ++ + DEBUG(0, "mtd: Giving out device %d to %s\n",i, mtd->name); +- while (not) +- { +- (*(not->add))(mtd); +- not = not->next; ++ /* No need to get a refcount on the module containing ++ the notifier, since we hold the mtd_table_mutex */ ++ list_for_each(this, &mtd_notifiers) { ++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); ++ not->add(mtd); + } ++ + up(&mtd_table_mutex); +- MOD_INC_USE_COUNT; ++ /* We _know_ we aren't being removed, because ++ our caller is still holding us here. So none ++ of this try_ nonsense, and no bitching about it ++ either. :) */ ++ __module_get(THIS_MODULE); + return 0; + } + +@@ -78,29 +92,34 @@ + + int del_mtd_device (struct mtd_info *mtd) + { +- struct mtd_notifier *not=mtd_notifiers; +- int i; ++ int ret; + + down(&mtd_table_mutex); + +- for (i=0; i < MAX_MTD_DEVICES; i++) +- { +- if (mtd_table[i] == mtd) +- { +- while (not) +- { +- (*(not->remove))(mtd); +- not = not->next; +- } +- mtd_table[i] = NULL; +- up (&mtd_table_mutex); +- MOD_DEC_USE_COUNT; +- return 0; ++ if (mtd_table[mtd->index] != mtd) { ++ ret = -ENODEV; ++ } else if (mtd->usecount) { ++ printk(KERN_NOTICE "Removing MTD device #%d (%s) with use count %d\n", ++ mtd->index, mtd->name, mtd->usecount); ++ ret = -EBUSY; ++ } else { ++ struct list_head *this; ++ ++ /* No need to get a refcount on the module containing ++ the notifier, since we hold the mtd_table_mutex */ ++ list_for_each(this, &mtd_notifiers) { ++ struct mtd_notifier *not = list_entry(this, struct mtd_notifier, list); ++ not->remove(mtd); + } ++ ++ mtd_table[mtd->index] = NULL; ++ ++ module_put(THIS_MODULE); ++ ret = 0; + } + + up(&mtd_table_mutex); +- return 1; ++ return ret; + } + + /** +@@ -118,10 +137,9 @@ + + down(&mtd_table_mutex); + +- new->next = mtd_notifiers; +- mtd_notifiers = new; ++ list_add(&new->list, &mtd_notifiers); + +- MOD_INC_USE_COUNT; ++ __module_get(THIS_MODULE); + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i]) +@@ -142,34 +160,24 @@ + + int unregister_mtd_user (struct mtd_notifier *old) + { +- struct mtd_notifier **prev = &mtd_notifiers; +- struct mtd_notifier *cur; + int i; + + down(&mtd_table_mutex); + +- while ((cur = *prev)) { +- if (cur == old) { +- *prev = cur->next; +- +- MOD_DEC_USE_COUNT; ++ module_put(THIS_MODULE); + + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i]) + old->remove(mtd_table[i]); + ++ list_del(&old->list); + up(&mtd_table_mutex); + return 0; +- } +- prev = &cur->next; +- } +- up(&mtd_table_mutex); +- return 1; + } + + + /** +- * __get_mtd_device - obtain a validated handle for an MTD device ++ * get_mtd_device - obtain a validated handle for an MTD device + * @mtd: last known address of the required MTD device + * @num: internal device number of the required MTD device + * +@@ -177,11 +185,10 @@ + * table, if any. Given an address and num == -1, search the device table + * for a device with that address and return if it's still present. Given + * both, return the num'th driver only if its address matches. Return NULL +- * if not. get_mtd_device() increases the use count, but +- * __get_mtd_device() doesn't - you should generally use get_mtd_device(). ++ * if not. + */ + +-struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num) ++struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) + { + struct mtd_info *ret = NULL; + int i; +@@ -198,10 +205,27 @@ + ret = NULL; + } + ++ if (ret && !try_module_get(ret->owner)) ++ ret = NULL; ++ ++ if (ret) ++ ret->usecount++; ++ + up(&mtd_table_mutex); + return ret; + } + ++void put_mtd_device(struct mtd_info *mtd) ++{ ++ int c; ++ ++ down(&mtd_table_mutex); ++ c = --mtd->usecount; ++ up(&mtd_table_mutex); ++ BUG_ON(c < 0); ++ ++ module_put(mtd->owner); ++} + + /* default_mtd_writev - default mtd writev method for MTD devices that + * dont implement their own +@@ -265,7 +289,8 @@ + + EXPORT_SYMBOL(add_mtd_device); + EXPORT_SYMBOL(del_mtd_device); +-EXPORT_SYMBOL(__get_mtd_device); ++EXPORT_SYMBOL(get_mtd_device); ++EXPORT_SYMBOL(put_mtd_device); + EXPORT_SYMBOL(register_mtd_user); + EXPORT_SYMBOL(unregister_mtd_user); + EXPORT_SYMBOL(default_mtd_writev); +diff -Nurb linux-mips-2.4.27/drivers/mtd/mtdpart.c linux/drivers/mtd/mtdpart.c +--- linux-mips-2.4.27/drivers/mtd/mtdpart.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/mtdpart.c 2004-11-19 10:25:11.652237336 +0100 +@@ -5,7 +5,7 @@ + * + * This code is GPL + * +- * $Id: mtdpart.c,v 1.32 2002/10/21 13:40:05 jocke Exp $ ++ * $Id: mtdpart.c,v 1.42 2003/07/09 11:19:01 dwmw2 Exp $ + * + * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> + * added support for read_oob, write_oob +@@ -16,10 +16,11 @@ + #include <linux/kernel.h> + #include <linux/slab.h> + #include <linux/list.h> +- ++#include <linux/config.h> ++#include <linux/kmod.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> +- ++#include <linux/mtd/compatmac.h> + + /* Our partition linked list */ + static LIST_HEAD(mtd_partitions); +@@ -54,8 +55,12 @@ + len = 0; + else if (from + len > mtd->size) + len = mtd->size - from; ++ if (part->master->read_ecc == NULL) + return part->master->read (part->master, from + part->offset, + len, retlen, buf); ++ else ++ return part->master->read_ecc (part->master, from + part->offset, ++ len, retlen, buf, NULL, &mtd->oobinfo); + } + + static int part_point (struct mtd_info *mtd, loff_t from, size_t len, +@@ -78,9 +83,11 @@ + + + static int part_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, +- size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel) ++ size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + if (from >= mtd->size) + len = 0; + else if (from + len > mtd->size) +@@ -113,7 +120,7 @@ + size_t *retlen, u_char *buf) + { + struct mtd_part *part = PART(mtd); +- return part->master->read_user_prot_reg (part->master, from, ++ return part->master->read_fact_prot_reg (part->master, from, + len, retlen, buf); + } + +@@ -127,17 +134,24 @@ + len = 0; + else if (to + len > mtd->size) + len = mtd->size - to; ++ if (part->master->write_ecc == NULL) + return part->master->write (part->master, to + part->offset, + len, retlen, buf); ++ else ++ return part->master->write_ecc (part->master, to + part->offset, ++ len, retlen, buf, NULL, &mtd->oobinfo); ++ + } + + static int part_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, +- u_char *eccbuf, int oobsel) ++ u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + if (to >= mtd->size) + len = 0; + else if (to + len > mtd->size) +@@ -174,25 +188,37 @@ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; ++ if (part->master->writev_ecc == NULL) + return part->master->writev (part->master, vecs, count, + to + part->offset, retlen); ++ else ++ return part->master->writev_ecc (part->master, vecs, count, ++ to + part->offset, retlen, ++ NULL, &mtd->oobinfo); + } + + static int part_readv (struct mtd_info *mtd, struct iovec *vecs, + unsigned long count, loff_t from, size_t *retlen) + { + struct mtd_part *part = PART(mtd); ++ if (part->master->readv_ecc == NULL) + return part->master->readv (part->master, vecs, count, + from + part->offset, retlen); ++ else ++ return part->master->readv_ecc (part->master, vecs, count, ++ from + part->offset, retlen, ++ NULL, &mtd->oobinfo); + } + + static int part_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen, +- u_char *eccbuf, int oobsel) ++ u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + return part->master->writev_ecc (part->master, vecs, count, + to + part->offset, retlen, + eccbuf, oobsel); +@@ -200,9 +226,11 @@ + + static int part_readv_ecc (struct mtd_info *mtd, struct iovec *vecs, + unsigned long count, loff_t from, size_t *retlen, +- u_char *eccbuf, int oobsel) ++ u_char *eccbuf, struct nand_oobinfo *oobsel) + { + struct mtd_part *part = PART(mtd); ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + return part->master->readv_ecc (part->master, vecs, count, + from + part->offset, retlen, + eccbuf, oobsel); +@@ -288,7 +316,7 @@ + */ + + int add_mtd_partitions(struct mtd_info *master, +- struct mtd_partition *parts, ++ const struct mtd_partition *parts, + int nbparts) + { + struct mtd_part *slave; +@@ -321,7 +349,7 @@ + + slave->mtd.name = parts[i].name; + slave->mtd.bank_size = master->bank_size; +- slave->mtd.module = master->module; ++ slave->mtd.owner = master->owner; + + slave->mtd.read = part_read; + slave->mtd.write = part_write; +@@ -452,6 +480,75 @@ + EXPORT_SYMBOL(add_mtd_partitions); + EXPORT_SYMBOL(del_mtd_partitions); + ++static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED; ++static LIST_HEAD(part_parsers); ++ ++struct mtd_part_parser *get_partition_parser(const char *name) ++{ ++ struct list_head *this; ++ void *ret = NULL; ++ spin_lock(&part_parser_lock); ++ ++ list_for_each(this, &part_parsers) { ++ struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list); ++ ++ if (!strcmp(p->name, name) && try_module_get(p->owner)) { ++ ret = p; ++ break; ++ } ++ } ++ spin_unlock(&part_parser_lock); ++ ++ return ret; ++} ++ ++int register_mtd_parser(struct mtd_part_parser *p) ++{ ++ spin_lock(&part_parser_lock); ++ list_add(&p->list, &part_parsers); ++ spin_unlock(&part_parser_lock); ++ ++ return 0; ++} ++ ++int deregister_mtd_parser(struct mtd_part_parser *p) ++{ ++ spin_lock(&part_parser_lock); ++ list_del(&p->list); ++ spin_unlock(&part_parser_lock); ++ return 0; ++} ++ ++int parse_mtd_partitions(struct mtd_info *master, const char **types, ++ struct mtd_partition **pparts, unsigned long origin) ++{ ++ struct mtd_part_parser *parser; ++ int ret = 0; ++ ++ for ( ; ret <= 0 && *types; types++) { ++ parser = get_partition_parser(*types); ++#ifdef CONFIG_KMOD ++ if (!parser && !request_module("%s", *types)) ++ parser = get_partition_parser(*types); ++#endif ++ if (!parser) { ++ printk(KERN_NOTICE "%s partition parsing not available\n", ++ *types); ++ continue; ++ } ++ ret = (*parser->parse_fn)(master, pparts, origin); ++ if (ret > 0) { ++ printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", ++ ret, parser->name, master->name); ++ } ++ put_partition_parser(parser); ++ } ++ return ret; ++} ++ ++EXPORT_SYMBOL_GPL(parse_mtd_partitions); ++EXPORT_SYMBOL_GPL(register_mtd_parser); ++EXPORT_SYMBOL_GPL(deregister_mtd_parser); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/Config.in linux/drivers/mtd/nand/Config.in +--- linux-mips-2.4.27/drivers/mtd/nand/Config.in 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/Config.in 2004-11-19 10:25:11.987186416 +0100 +@@ -1,6 +1,6 @@ + # drivers/mtd/nand/Config.in + +-# $Id: Config.in,v 1.11 2002/12/01 13:23:05 gleixner Exp $ ++# $Id: Config.in,v 1.14 2003/11/04 22:59:11 ahennessy Exp $ + + mainmenu_option next_comment + +@@ -11,26 +11,27 @@ + bool ' Verify NAND page writes' CONFIG_MTD_NAND_VERIFY_WRITE + fi + +-if [ "$CONFIG_ARM" = "y" -a "$CONFIG_ARCH_P720T" = "y" ]; then +- dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND ++if [ "$CONFIG_ARM" = "y" ]; then ++ dep_tristate ' NAND Flash device on SPIA board' CONFIG_MTD_NAND_SPIA $CONFIG_MTD_NAND $CONFIG_ARCH_P720T ++ dep_tristate ' NAND Flash device on TOTO board' CONFIG_MTD_NAND_TOTO $CONFIG_MTD_NAND $CONFIG_ARCH_OMAP ++ dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND $CONFIG_ARCH_AUTCPU12 ++ dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND $CONFIG_ARCH_EDB7312 + fi + +-if [ "$CONFIG_ARCH_AUTCPU12" = "y" ]; then +- dep_tristate ' SmartMedia Card on AUTCPU12 board' CONFIG_MTD_NAND_AUTCPU12 $CONFIG_MTD_NAND +-fi +- +-if [ "$CONFIG_ARCH_EDB7312" = "y" ]; then +- dep_tristate ' NAND Flash device on EDP7312 board' CONFIG_MTD_NAND_EDB7312 $CONFIG_MTD_NAND +-fi +- +-if [ "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then ++if [ "$CONFIG_MTD_DOC2001PLUS" = "y" -o "$CONFIG_MTD_DOC2001" = "y" -o "$CONFIG_MTD_DOC2000" = "y" -o "$CONFIG_MTD_NAND" = "y" ]; then + define_bool CONFIG_MTD_NAND_IDS y ++else ++ if [ "$CONFIG_MTD_DOC2001PLUS" = "m" -o "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then ++ define_bool CONFIG_MTD_NAND_IDS m ++ fi + fi + +-if [ "$CONFIG_MTD_NAND_IDS" != "y" ]; then +-if [ "$CONFIG_MTD_DOC2001" = "m" -o "$CONFIG_MTD_DOC2000" = "m" -o "$CONFIG_MTD_NAND" = "m" ]; then +- define_bool CONFIG_MTD_NAND_IDS m ++if [ "$CONFIG_TOSHIBA_RBTX4925" = "y" ]; then ++ dep_tristate ' SmartMedia Card on Toshiba RBTX4925 reference board' CONFIG_MTD_NAND_TX4925NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4925_MPLEX_NAND + fi ++ ++if [ "$CONFIG_TOSHIBA_RBTX4938" = "y" ]; then ++ dep_tristate ' NAND Flash device on Toshiba RBTX4938 reference board' CONFIG_MTD_NAND_TX4938NDFMC $CONFIG_MTD_NAND $CONFIG_TOSHIBA_RBTX4938_MPLEX_NAND + fi + + endmenu +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/Makefile linux/drivers/mtd/nand/Makefile +--- linux-mips-2.4.27/drivers/mtd/nand/Makefile 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/Makefile 2004-11-19 10:25:11.989186112 +0100 +@@ -1,16 +1,20 @@ + # + # linux/drivers/nand/Makefile + # +-# $Id: Makefile,v 1.10 2002/12/01 13:23:05 gleixner Exp $ ++# $Id: Makefile.common,v 1.4 2003/11/04 22:59:11 ahennessy Exp $ + ++ifeq ($(PATCHLEVEL),4) + O_TARGET := nandlink.o +- + export-objs := nand.o nand_ecc.o nand_ids.o ++endif + + obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o + obj-$(CONFIG_MTD_NAND_SPIA) += spia.o ++obj-$(CONFIG_MTD_NAND_TOTO) += toto.o + obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o + obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o ++obj-$(CONFIG_MTD_NAND_TX4925NDFMC) += tx4925ndfmc.o ++obj-$(CONFIG_MTD_NAND_TX4938NDFMC) += tx4938ndfmc.o + obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o + +-include $(TOPDIR)/Rules.make ++-include $(TOPDIR)/Rules.make +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/autcpu12.c linux/drivers/mtd/nand/autcpu12.c +--- linux-mips-2.4.27/drivers/mtd/nand/autcpu12.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/autcpu12.c 2004-11-19 10:25:11.990185960 +0100 +@@ -4,9 +4,9 @@ + * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de> + * + * Derived from drivers/mtd/spia.c +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * +- * $Id: autcpu12.c,v 1.6 2002/11/11 15:47:56 gleixner Exp $ ++ * $Id: autcpu12.c,v 1.13 2003/07/11 15:12:29 dwmw2 Exp $ + * + * 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 +@@ -25,10 +25,10 @@ + * added page_cache + * + * 10-06-2002 TG 128K card support added +- * + */ + + #include <linux/slab.h> ++#include <linux/init.h> + #include <linux/module.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/nand.h> +@@ -70,6 +70,7 @@ + /* + * Define partitions for flash devices + */ ++extern struct nand_oobinfo jffs2_oobinfo; + + static struct mtd_partition partition_info16k[] = { + { name: "AUTCPU12 flash partition 1", +@@ -95,7 +96,7 @@ + size: 16 * SZ_1M }, + { name: "AUTCPU12 flash partition 2", + offset: 16 * SZ_1M, +- size: 48 * SZ_1M}, ++ size: 48 * SZ_1M }, + }; + + static struct mtd_partition partition_info128k[] = { +@@ -104,7 +105,7 @@ + size: 16 * SZ_1M }, + { name: "AUTCPU12 flash partition 2", + offset: 16 * SZ_1M, +- size: 112 * SZ_1M}, ++ size: 112 * SZ_1M }, + }; + + #define NUM_PARTITIONS16K 2 +@@ -114,7 +115,7 @@ + /* + * hardware specific access to control-lines + */ +-void autcpu12_hwcontrol(int cmd) ++static void autcpu12_hwcontrol(struct mtd_info *mtd, int cmd) + { + + switch(cmd){ +@@ -133,7 +134,7 @@ + /* + * read device ready pin + */ +-int autcpu12_device_ready(void) ++int autcpu12_device_ready(struct mtd_info *mtd) + { + + return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0; +@@ -184,7 +185,7 @@ + this->eccmode = NAND_ECC_SOFT; + + /* Scan to find existance of the device */ +- if (nand_scan (autcpu12_mtd)) { ++ if (nand_scan (autcpu12_mtd, 1)) { + err = -ENXIO; + goto out_ior; + } +@@ -197,15 +198,6 @@ + goto out_ior; + } + +- /* Allocate memory for internal data buffer */ +- this->data_cache = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL); +- if (!this->data_cache) { +- printk ("Unable to allocate NAND data cache for AUTCPU12.\n"); +- err = -ENOMEM; +- goto out_buf; +- } +- this->cache_page = -1; +- + /* Register the partitions */ + switch(autcpu12_mtd->size){ + case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break; +@@ -215,13 +207,11 @@ + default: { + printk ("Unsupported SmartMedia device\n"); + err = -ENXIO; +- goto out_cac; ++ goto out_buf; + } + } + goto out; + +-out_cac: +- kfree (this->data_cache); + out_buf: + kfree (this->data_buf); + out_ior: +@@ -250,7 +240,6 @@ + + /* Free internal data buffers */ + kfree (this->data_buf); +- kfree (this->data_cache); + + /* unmap physical adress */ + iounmap((void *)autcpu12_fio_base); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/diskonchip.c linux/drivers/mtd/nand/diskonchip.c +--- linux-mips-2.4.27/drivers/mtd/nand/diskonchip.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/nand/diskonchip.c 2004-11-19 10:25:11.992185656 +0100 +@@ -0,0 +1,534 @@ ++/* ++ * drivers/mtd/nand/diskonchip.c ++ * ++ * (C) 2003 Red Hat, Inc. ++ * ++ * Author: David Woodhouse <dwmw2@infradead.org> ++ * ++ * Interface to generic NAND code for M-Systems DiskOnChip devices ++ * ++ * $Id: diskonchip.c,v 1.8 2003/11/05 16:52:34 dwmw2 Exp $ ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/init.h> ++#include <linux/sched.h> ++#include <linux/delay.h> ++#include <asm/io.h> ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/doc2000.h> ++#include <linux/mtd/compatmac.h> ++ ++struct doc_priv { ++ unsigned long virtadr; ++ unsigned long physadr; ++ u_char ChipID; ++ u_char CDSNControl; ++ int chips_per_floor; /* The number of chips detected on each floor */ ++ int curfloor; ++ int curchip; ++}; ++ ++#define DoC_is_Millennium(doc) ((doc)->ChipID == DOC_ChipID_DocMil) ++#define DoC_is_2000(doc) ((doc)->ChipID == DOC_ChipID_Doc2k) ++ ++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd); ++static void doc200x_select_chip(struct mtd_info *mtd, int chip); ++ ++static int debug=0; ++MODULE_PARM(debug, "i"); ++ ++static int try_dword=1; ++MODULE_PARM(try_dword, "i"); ++ ++static void DoC_Delay(struct doc_priv *doc, unsigned short cycles) ++{ ++ volatile char dummy; ++ int i; ++ ++ for (i = 0; i < cycles; i++) { ++ if (DoC_is_Millennium(doc)) ++ dummy = ReadDOC(doc->virtadr, NOP); ++ else ++ dummy = ReadDOC(doc->virtadr, DOCStatus); ++ } ++ ++} ++/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ ++static int _DoC_WaitReady(struct doc_priv *doc) ++{ ++ unsigned long docptr = doc->virtadr; ++ unsigned long timeo = jiffies + (HZ * 10); ++ ++ if(debug) printk("_DoC_WaitReady...\n"); ++ /* Out-of-line routine to wait for chip response */ ++ while (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ if (time_after(jiffies, timeo)) { ++ printk("_DoC_WaitReady timed out.\n"); ++ return -EIO; ++ } ++ udelay(1); ++ cond_resched(); ++ } ++ ++ return 0; ++} ++ ++static inline int DoC_WaitReady(struct doc_priv *doc) ++{ ++ unsigned long docptr = doc->virtadr; ++ int ret = 0; ++ ++ DoC_Delay(doc, 4); ++ ++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) ++ /* Call the out-of-line routine to wait */ ++ ret = _DoC_WaitReady(doc); ++ ++ DoC_Delay(doc, 2); ++ if(debug) printk("DoC_WaitReady OK\n"); ++ return ret; ++} ++ ++static void doc2000_write_byte(struct mtd_info *mtd, u_char datum) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ if(debug)printk("write_byte %02x\n", datum); ++ WriteDOC(datum, docptr, CDSNSlowIO); ++ WriteDOC(datum, docptr, 2k_CDSN_IO); ++} ++ ++static u_char doc2000_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ ReadDOC(docptr, CDSNSlowIO); ++ u_char ret = ReadDOC(docptr, 2k_CDSN_IO); ++ if (debug) printk("read_byte returns %02x\n", ret); ++ return ret; ++} ++static void doc2000_writebuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ if (debug)printk("writebuf of %d bytes: ", len); ++ for (i=0; i < len; i++) { ++ WriteDOC_(buf[i], docptr, DoC_2k_CDSN_IO + i); ++ if (debug && i < 16) ++ printk("%02x ", buf[i]); ++ } ++ if (debug) printk("\n"); ++} ++ ++static void doc2000_readbuf(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ if (debug)printk("readbuf of %d bytes: ", len); ++ ++ for (i=0; i < len; i++) { ++ buf[i] = ReadDOC(docptr, 2k_CDSN_IO + i); ++ } ++} ++ ++static void doc2000_readbuf_dword(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ if (debug) printk("readbuf_dword of %d bytes: ", len); ++ ++ if (unlikely((((unsigned long)buf)|len) & 3)) { ++ for (i=0; i < len; i++) { ++ *(uint8_t *)(&buf[i]) = ReadDOC(docptr, 2k_CDSN_IO + i); ++ } ++ } else { ++ for (i=0; i < len; i+=4) { ++ *(uint32_t*)(&buf[i]) = readl(docptr + DoC_2k_CDSN_IO + i); ++ } ++ } ++} ++ ++static int doc2000_verifybuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ for (i=0; i < len; i++) ++ if (buf[i] != ReadDOC(docptr, 2k_CDSN_IO)) ++ return i; ++ return 0; ++} ++ ++static uint16_t doc200x_ident_chip(struct mtd_info *mtd, int nr) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ uint16_t ret; ++ ++ doc200x_select_chip(mtd, nr); ++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_READID); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); ++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE); ++ this->write_byte(mtd, 0); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); ++ ++ ret = this->read_byte(mtd) << 8; ++ ret |= this->read_byte(mtd); ++ ++ if (doc->ChipID == DOC_ChipID_Doc2k && try_dword && !nr) { ++ /* First chip probe. See if we get same results by 32-bit access */ ++ union { ++ uint32_t dword; ++ uint8_t byte[4]; ++ } ident; ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ doc200x_hwcontrol(mtd, NAND_CTL_SETCLE); ++ doc2000_write_byte(mtd, NAND_CMD_READID); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRCLE); ++ doc200x_hwcontrol(mtd, NAND_CTL_SETALE); ++ doc2000_write_byte(mtd, 0); ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRALE); ++ ++ ident.dword = readl(docptr + DoC_2k_CDSN_IO); ++ if (((ident.byte[0] << 8) | ident.byte[1]) == ret) { ++ printk(KERN_INFO "DiskOnChip 2000 responds to DWORD access\n"); ++ this->read_buf = &doc2000_readbuf_dword; ++ } ++ } ++ ++ return ret; ++} ++ ++static void doc2000_count_chips(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ uint16_t mfrid; ++ int i; ++ ++ /* Max 4 chips per floor on DiskOnChip 2000 */ ++ doc->chips_per_floor = 4; ++ ++ /* Find out what the first chip is */ ++ mfrid = doc200x_ident_chip(mtd, 0); ++ ++ /* Find how many chips in each floor. */ ++ for (i = 1; i < 4; i++) { ++ if (doc200x_ident_chip(mtd, i) != mfrid) ++ break; ++ } ++ doc->chips_per_floor = i; ++} ++ ++static int doc200x_wait(struct mtd_info *mtd, struct nand_chip *this, int state) ++{ ++ struct doc_priv *doc = (void *)this->priv; ++ ++ int status; ++ ++ DoC_WaitReady(doc); ++ this->cmdfunc(mtd, NAND_CMD_STATUS, -1, -1); ++ DoC_WaitReady(doc); ++ status = (int)this->read_byte(mtd); ++ ++ return status; ++} ++ ++static void doc2001_write_byte(struct mtd_info *mtd, u_char datum) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ WriteDOC(datum, docptr, CDSNSlowIO); ++ WriteDOC(datum, docptr, Mil_CDSN_IO); ++ WriteDOC(datum, docptr, WritePipeTerm); ++} ++ ++static u_char doc2001_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ ReadDOC(docptr, CDSNSlowIO); ++ /* 11.4.5 -- delay twice to allow extended length cycle */ ++ DoC_Delay(doc, 2); ++ ReadDOC(docptr, ReadPipeInit); ++ return ReadDOC(docptr, Mil_CDSN_IO); ++} ++ ++static void doc2001_writebuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ for (i=0; i < len; i++) ++ WriteDOC_(buf[i], docptr, DoC_Mil_CDSN_IO + i); ++ /* Terminate write pipeline */ ++ WriteDOC(0x00, docptr, WritePipeTerm); ++} ++ ++static void doc2001_readbuf(struct mtd_info *mtd, ++ u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, ReadPipeInit); ++ ++ for (i=0; i < len-1; i++) ++ buf[i] = ReadDOC(docptr, Mil_CDSN_IO); ++ ++ /* Terminate read pipeline */ ++ buf[i] = ReadDOC(docptr, LastDataRead); ++} ++static int doc2001_verifybuf(struct mtd_info *mtd, ++ const u_char *buf, int len) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int i; ++ ++ /* Start read pipeline */ ++ ReadDOC(docptr, ReadPipeInit); ++ ++ for (i=0; i < len-1; i++) ++ if (buf[i] != ReadDOC(docptr, Mil_CDSN_IO)) { ++ ReadDOC(docptr, LastDataRead); ++ return i; ++ } ++ if (buf[i] != ReadDOC(docptr, LastDataRead)) ++ return i; ++ return 0; ++} ++ ++static void doc200x_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ int floor = 0; ++ ++ /* 11.4.4 -- deassert CE before changing chip */ ++ doc200x_hwcontrol(mtd, NAND_CTL_CLRNCE); ++ ++ if(debug)printk("select chip (%d)\n", chip); ++ ++ if (chip == -1) ++ return; ++ ++ floor = chip / doc->chips_per_floor; ++ chip -= (floor * doc->chips_per_floor); ++ ++ WriteDOC(floor, docptr, FloorSelect); ++ WriteDOC(chip, docptr, CDSNDeviceSelect); ++ ++ doc200x_hwcontrol(mtd, NAND_CTL_SETNCE); ++ ++ doc->curchip = chip; ++ doc->curfloor = floor; ++} ++ ++static void doc200x_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ switch(cmd) { ++ case NAND_CTL_SETNCE: ++ doc->CDSNControl |= CDSN_CTRL_CE; ++ break; ++ case NAND_CTL_CLRNCE: ++ doc->CDSNControl &= ~CDSN_CTRL_CE; ++ break; ++ case NAND_CTL_SETCLE: ++ doc->CDSNControl |= CDSN_CTRL_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ doc->CDSNControl &= ~CDSN_CTRL_CLE; ++ break; ++ case NAND_CTL_SETALE: ++ doc->CDSNControl |= CDSN_CTRL_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ doc->CDSNControl &= ~CDSN_CTRL_ALE; ++ break; ++ case NAND_CTL_SETWP: ++ doc->CDSNControl |= CDSN_CTRL_WP; ++ break; ++ case NAND_CTL_CLRWP: ++ doc->CDSNControl &= ~CDSN_CTRL_WP; ++ break; ++ } ++ if (debug)printk("hwcontrol(%d): %02x\n", cmd, doc->CDSNControl); ++ WriteDOC(doc->CDSNControl, docptr, CDSNControl); ++ /* 11.4.3 -- 4 NOPs after CSDNControl write */ ++ DoC_Delay(doc, 4); ++} ++ ++static int doc200x_dev_ready(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ struct doc_priv *doc = (void *)this->priv; ++ unsigned long docptr = doc->virtadr; ++ ++ /* 11.4.2 -- must NOP four times before checking FR/B# */ ++ DoC_Delay(doc, 4); ++ if (!(ReadDOC(docptr, CDSNControl) & CDSN_CTRL_FR_B)) { ++ if(debug) ++ printk("not ready\n"); ++ return 0; ++ } ++ /* 11.4.2 -- Must NOP twice if it's ready */ ++ DoC_Delay(doc, 2); ++ if (debug)printk("was ready\n"); ++ return 1; ++} ++ ++static int doc200x_block_bad(struct mtd_info *mtd, unsigned long block) ++{ ++ /* FIXME: Look it up in the BBT */ ++ return 0; ++} ++ ++struct doc_priv mydoc = { ++ .physadr = 0xd4000, ++ .curfloor = -1, ++ .curchip = -1, ++}; ++ ++u_char mydatabuf[528]; ++ ++struct nand_chip mynand = { ++ .priv = (void *)&mydoc, ++ .select_chip = doc200x_select_chip, ++ .hwcontrol = doc200x_hwcontrol, ++ .dev_ready = doc200x_dev_ready, ++ .waitfunc = doc200x_wait, ++ .block_bad = doc200x_block_bad, ++ .eccmode = NAND_ECC_SOFT, ++ .data_buf = mydatabuf, ++}; ++ ++struct mtd_info mymtd = { ++ .priv = (void *)&mynand, ++ .owner = THIS_MODULE, ++}; ++ ++int __init init_nanddoc(void) ++{ ++ mydoc.virtadr = (unsigned long)ioremap(mydoc.physadr, DOC_IOREMAP_LEN); ++ int nrchips = 1; ++ char *name; ++ ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, ++ mydoc.virtadr, DOCControl); ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_RESET, ++ mydoc.virtadr, DOCControl); ++ ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, ++ mydoc.virtadr, DOCControl); ++ WriteDOC(DOC_MODE_CLR_ERR | DOC_MODE_MDWREN | DOC_MODE_NORMAL, ++ mydoc.virtadr, DOCControl); ++ ++ mydoc.ChipID = ReadDOC(mydoc.virtadr, ChipID); ++ ++ switch(mydoc.ChipID) { ++ case DOC_ChipID_DocMil: ++ mynand.write_byte = doc2001_write_byte; ++ mynand.read_byte = doc2001_read_byte; ++ mynand.write_buf = doc2001_writebuf; ++ mynand.read_buf = doc2001_readbuf; ++ mynand.verify_buf = doc2001_verifybuf; ++ ++ ReadDOC(mydoc.virtadr, ChipID); ++ ReadDOC(mydoc.virtadr, ChipID); ++ if (ReadDOC(mydoc.virtadr, ChipID) != DOC_ChipID_DocMil) { ++ /* It's not a Millennium; it's one of the newer ++ DiskOnChip 2000 units with a similar ASIC. ++ Treat it like a Millennium, except that it ++ can have multiple chips. */ ++ doc2000_count_chips(&mymtd); ++ nrchips = 4 * mydoc.chips_per_floor; ++ name = "DiskOnChip 2000 (INFTL Model)"; ++ } else { ++ /* Bog-standard Millennium */ ++ mydoc.chips_per_floor = 1; ++ nrchips = 1; ++ name = "DiskOnChip Millennium"; ++ } ++ break; ++ ++ case DOC_ChipID_Doc2k: ++ mynand.write_byte = doc2000_write_byte; ++ mynand.read_byte = doc2000_read_byte; ++ mynand.write_buf = doc2000_writebuf; ++ mynand.read_buf = doc2000_readbuf; ++ mynand.verify_buf = doc2000_verifybuf; ++ ++ doc2000_count_chips(&mymtd); ++ nrchips = 4 * mydoc.chips_per_floor; ++ name = "DiskOnChip 2000 (NFTL Model)"; ++ mydoc.CDSNControl |= CDSN_CTRL_FLASH_IO; ++ ++ break; ++ ++ default: ++ return -EIO; ++ } ++ if (nand_scan(&mymtd, nrchips)) { ++ iounmap((void *)mydoc.virtadr); ++ return -EIO; ++ } ++ mymtd.name = name; ++ add_mtd_device(&mymtd); ++ ++ return 0; ++} ++ ++void __exit cleanup_nanddoc(void) ++{ ++ del_mtd_device(&mymtd); ++ iounmap((void *)mydoc.virtadr); ++} ++ ++module_init(init_nanddoc); ++module_exit(cleanup_nanddoc); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); ++MODULE_DESCRIPTION("M-Systems DiskOnChip 2000 and Millennium device driver\n"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/edb7312.c linux/drivers/mtd/nand/edb7312.c +--- linux-mips-2.4.27/drivers/mtd/nand/edb7312.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/edb7312.c 2004-11-19 10:25:11.997184896 +0100 +@@ -6,7 +6,7 @@ + * Derived from drivers/mtd/nand/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * +- * $Id: edb7312.c,v 1.3 2002/06/06 12:58:16 mag Exp $ ++ * $Id: edb7312.c,v 1.7 2003/07/11 15:12:29 dwmw2 Exp $ + * + * 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 +@@ -20,6 +20,7 @@ + + #include <linux/slab.h> + #include <linux/module.h> ++#include <linux/init.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/nand.h> + #include <linux/mtd/partitions.h> +@@ -77,16 +78,13 @@ + }; + #define NUM_PARTITIONS 1 + +-extern int parse_cmdline_partitions(struct mtd_info *master, +- struct mtd_partition **pparts, +- const char *mtd_id); + #endif + + + /* + * hardware specific access to control-lines + */ +-static void ep7312_hwcontrol(int cmd) ++static void ep7312_hwcontrol(struct mtd_info *mtd, int cmd) + { + switch(cmd) { + +@@ -116,10 +114,13 @@ + /* + * read device ready pin + */ +-static int ep7312_device_ready(void) ++static int ep7312_device_ready(struct mtd_info *mtd) + { + return 1; + } ++#ifdef CONFIG_MTD_PARTITIONS ++const char *part_probes[] = { "cmdlinepart", NULL }; ++#endif + + /* + * Main initialization routine +@@ -174,7 +175,7 @@ + this->chip_delay = 15; + + /* Scan to find existence of the device */ +- if (nand_scan (ep7312_mtd)) { ++ if (nand_scan (ep7312_mtd, 1)) { + iounmap((void *)ep7312_fio_base); + kfree (ep7312_mtd); + return -ENXIO; +@@ -189,27 +190,16 @@ + return -ENOMEM; + } + +- /* Allocate memory for internal data buffer */ +- this->data_cache = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL); +- if (!this->data_cache) { +- printk("Unable to allocate NAND data cache for EDB7312.\n"); +- kfree (this->data_buf); +- iounmap((void *)ep7312_fio_base); +- kfree (ep7312_mtd); +- return -ENOMEM; +- } +- this->cache_page = -1; +- +-#ifdef CONFIG_MTD_CMDLINE_PARTS +- mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts, +- "edb7312-nand"); ++#ifdef CONFIG_PARTITIONS ++ ep7312_mtd->name = "edb7312-nand"; ++ mtd_parts_nb = parse_mtd_partitions(ep7312_mtd, part_probes, ++ &mtd_parts, 0); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; + #endif +- if (mtd_parts_nb == 0) +- { ++ if (mtd_parts_nb == 0) { + mtd_parts = partition_info; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; +@@ -236,7 +226,6 @@ + + /* Free internal data buffer */ + kfree (this->data_buf); +- kfree (this->data_cache); + + /* Free the MTD device structure */ + kfree (ep7312_mtd); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/nand.c linux/drivers/mtd/nand/nand.c +--- linux-mips-2.4.27/drivers/mtd/nand/nand.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/nand.c 2004-11-19 10:25:12.010182920 +0100 +@@ -8,7 +8,7 @@ + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/tech/nand.html + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002 Thomas Gleixner (tglx@linutronix.de) + * + * 10-29-2001 Thomas Gleixner (tglx@linutronix.de) +@@ -112,10 +112,27 @@ + * for mtd->read_ecc / mtd->write_ecc + * some minor cleanups + * +- * 12-05-2000 tglx: Dave Ellis (DGE@sixnetio) provided the fix for ++ * 12-05-2002 tglx: Dave Ellis (DGE@sixnetio) provided the fix for + * WRITE_VERIFY long time ago. Thanks for remembering me. + * +- * $Id: nand.c,v 1.36 2002/12/05 20:59:11 gleixner Exp $ ++ * 02-14-2003 tglx: Reject non page aligned writes ++ * Fixed ecc select in nand_write_page to match semantics. ++ * ++ * 02-18-2003 tglx: Changed oobsel to pointer. Added a default oob-selector ++ * ++ * 02-18-2003 tglx: Implemented oobsel again. Now it uses a pointer to ++ + a structure, which will be supplied by a filesystem driver ++ * If NULL is given, then the defaults (none or defaults ++ * supplied by ioctl (MEMSETOOBSEL) are used. ++ * For partitions the partition defaults are used (mtdpart.c) ++ * ++ * 06-04-2003 tglx: fix compile errors and fix write verify problem for ++ * some chips, which need either a delay between the readback ++ * and the next write command or have the CE removed. The ++ * CE disable/enable is much faster than a 20us delay and ++ * it should work on all available chips. ++ * ++ * $Id: nand.c,v 1.60 2003/10/23 08:28:43 dwmw2 Exp $ + * + * 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 +@@ -130,102 +147,151 @@ + #include <linux/mtd/mtd.h> + #include <linux/mtd/nand.h> + #include <linux/mtd/nand_ecc.h> ++#include <linux/mtd/compatmac.h> + #include <linux/interrupt.h> + #include <asm/io.h> + + /* +- * Macros for low-level register control +- */ +-#define nand_select() this->hwcontrol(NAND_CTL_SETNCE); +- +-#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE); +- +-/* +- * out of band configuration for different filesystems +- */ +-static int oobconfigs[][6] = { +- { 0,0,0,0,0,0}, +- +- { NAND_JFFS2_OOB_ECCPOS0, NAND_JFFS2_OOB_ECCPOS1, NAND_JFFS2_OOB_ECCPOS2, +- NAND_JFFS2_OOB_ECCPOS3, NAND_JFFS2_OOB_ECCPOS4, NAND_JFFS2_OOB_ECCPOS5 }, +- +- { NAND_YAFFS_OOB_ECCPOS0, NAND_YAFFS_OOB_ECCPOS1, NAND_YAFFS_OOB_ECCPOS2, +- NAND_YAFFS_OOB_ECCPOS3, NAND_YAFFS_OOB_ECCPOS4, NAND_YAFFS_OOB_ECCPOS5 } +-}; +- +-/* + * NAND low-level MTD interface functions + */ ++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len); ++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len); ++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len); ++ + static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); + static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, +- size_t * retlen, u_char * buf, u_char * eccbuf, int oobsel); ++ size_t * retlen, u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); + static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf); + static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf); + static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, +- size_t * retlen, const u_char * buf, u_char * eccbuf, int oobsel); ++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel); + static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char *buf); + static int nand_writev (struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t * retlen); + static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, +- unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, int oobsel); ++ unsigned long count, loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); + static int nand_erase (struct mtd_info *mtd, struct erase_info *instr); + static void nand_sync (struct mtd_info *mtd); +-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, int col, +- int last, u_char *oob_buf, int oobsel); ++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel); ++ ++static u_char nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return readb(this->IO_ADDR_R); ++} ++ ++static void nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ writeb(byte, this->IO_ADDR_W); ++} ++ ++static void nand_select_chip(struct mtd_info *mtd, int chip) ++{ ++ struct nand_chip *this = mtd->priv; ++ switch(chip) { ++ case -1: ++ this->hwcontrol(mtd, NAND_CTL_CLRNCE); ++ break; ++ case 0: ++ this->hwcontrol(mtd, NAND_CTL_SETNCE); ++ break; ++ ++ default: ++ BUG(); ++ } ++} ++ ++static void nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; i<len; i++) ++ writeb(buf[i], this->IO_ADDR_W); ++} ++ ++static void nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; i<len; i++) ++ buf[i] = readb(this->IO_ADDR_R); ++} ++ ++static int nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; i<len; i++) ++ if (buf[i] != readb(this->IO_ADDR_R)) ++ return i; ++ ++ return 0; ++} ++ ++/* Appropriate chip should already be selected */ ++static int nand_block_bad(struct mtd_info *mtd, unsigned long page) ++{ ++ struct nand_chip *this = mtd->priv; ++ ++ this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page); ++ if (this->read_byte(mtd) != 0xff) ++ return 1; ++ ++ return 0; ++} ++ + /* + * Send command to NAND device + */ + static void nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) + { + register struct nand_chip *this = mtd->priv; +- register unsigned long NAND_IO_ADDR = this->IO_ADDR_W; + + /* Begin command latch cycle */ +- this->hwcontrol (NAND_CTL_SETCLE); ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); + /* + * Write out the command to the device. + */ +- if (command != NAND_CMD_SEQIN) +- writeb (command, NAND_IO_ADDR); +- else { +- if (mtd->oobblock == 256 && column >= 256) { +- column -= 256; +- writeb (NAND_CMD_READOOB, NAND_IO_ADDR); +- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); +- } else if (mtd->oobblock == 512 && column >= 256) { +- if (column < 512) { +- column -= 256; +- writeb (NAND_CMD_READ1, NAND_IO_ADDR); +- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); +- } else { +- column -= 512; +- writeb (NAND_CMD_READOOB, NAND_IO_ADDR); +- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); +- } ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; + } else { +- writeb (NAND_CMD_READ0, NAND_IO_ADDR); +- writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); ++ column -= 256; ++ readcmd = NAND_CMD_READ1; + } ++ this->write_byte(mtd, readcmd); + } ++ this->write_byte(mtd, command); + + /* Set ALE and clear CLE to start address cycle */ +- this->hwcontrol (NAND_CTL_CLRCLE); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { +- this->hwcontrol (NAND_CTL_SETALE); ++ this->hwcontrol(mtd, NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) +- writeb (column, NAND_IO_ADDR); ++ this->write_byte(mtd, column); + if (page_addr != -1) { +- writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR); +- writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR); ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); + /* One more address cycle for higher density devices */ + if (mtd->size & 0x0c000000) +- writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); + } + /* Latch in address */ +- this->hwcontrol (NAND_CTL_CLRALE); ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); + } + + /* +@@ -244,10 +310,11 @@ + case NAND_CMD_RESET: + if (this->dev_ready) + break; +- this->hwcontrol (NAND_CTL_SETCLE); +- writeb (NAND_CMD_STATUS, NAND_IO_ADDR); +- this->hwcontrol (NAND_CTL_CLRCLE); +- while ( !(readb (this->IO_ADDR_R) & 0x40)); ++ udelay(this->chip_delay); ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & 0x40)); + return; + + /* This applies to read commands */ +@@ -263,7 +330,7 @@ + } + + /* wait until command is processed */ +- while (!this->dev_ready()); ++ while (!this->dev_ready(mtd)); + } + + /* +@@ -288,17 +355,17 @@ + spin_unlock_bh (&this->chip_lock); + return; + } +- ++#if 0 /* This was broken. And of dubious utility */ + if (this->state == FL_ERASING) { + if (new_state != FL_ERASING) { + this->state = new_state; + spin_unlock_bh (&this->chip_lock); +- nand_select (); /* select in any case */ ++ this->select_chip(mtd, 0); /* select in any case */ + this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + return; + } + } +- ++#endif + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); +@@ -334,17 +401,17 @@ + return 0; + } + if (this->dev_ready) { +- if (this->dev_ready ()) ++ if (this->dev_ready(mtd)) + break; + } +- if (readb (this->IO_ADDR_R) & 0x40) ++ if (this->read_byte(mtd) & 0x40) + break; + + spin_unlock_bh (&this->chip_lock); + yield (); + spin_lock_bh (&this->chip_lock); + } +- status = (int) readb (this->IO_ADDR_R); ++ status = (int) this->read_byte(mtd); + spin_unlock_bh (&this->chip_lock); + + return status; +@@ -352,14 +419,15 @@ + + /* + * Nand_page_program function is used for write and writev ! ++ * This function will always program a full page of data ++ * If you call it with a non page aligned buffer, you're lost :) + */ +-static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, +- int page, int col, int last, u_char *oob_buf, int oobsel) ++static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel) + { + int i, status; + u_char ecc_code[6], *oob_data; +- int eccmode = oobsel ? this->eccmode : NAND_ECC_NONE; +- int *oob_config = oobconfigs[oobsel]; ++ int eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; ++ int *oob_config = oobsel->eccpos; + + /* pad oob area, if we have no oob buffer from fs-driver */ + if (!oob_buf) { +@@ -369,66 +437,42 @@ + } else + oob_data = oob_buf; + +- /* software ecc 3 Bytes ECC / 256 Byte Data ? */ +- if (eccmode == NAND_ECC_SOFT) { +- /* Read back previous written data, if col > 0 */ +- if (col) { +- this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); +- for (i = 0; i < col; i++) +- this->data_poi[i] = readb (this->IO_ADDR_R); +- } +- if ((col < this->eccsize) && (last >= this->eccsize)) { +- this->calculate_ecc (&this->data_poi[0], &(ecc_code[0])); +- for (i = 0; i < 3; i++) +- oob_data[oob_config[i]] = ecc_code[i]; +- } +- /* Calculate and write the second ECC if we have enough data */ +- if ((mtd->oobblock == 512) && (last == 512)) { +- this->calculate_ecc (&this->data_poi[256], &(ecc_code[3])); +- for (i = 3; i < 6; i++) +- oob_data[oob_config[i]] = ecc_code[i]; +- } +- } else { +- /* For hardware ECC skip ECC, if we have no full page write */ +- if (eccmode != NAND_ECC_NONE && (col || last != mtd->oobblock)) +- eccmode = NAND_ECC_NONE; +- } +- +- /* Prepad for partial page programming !!! */ +- for (i = 0; i < col; i++) +- this->data_poi[i] = 0xff; +- +- /* Postpad for partial page programming !!! oob is already padded */ +- for (i = last; i < mtd->oobblock; i++) +- this->data_poi[i] = 0xff; +- + /* Send command to begin auto page programming */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, 0x00, page); + + /* Write out complete page of data, take care of eccmode */ +- switch (this->eccmode) { ++ switch (eccmode) { + /* No ecc and software ecc 3/256, write all */ + case NAND_ECC_NONE: ++ printk (KERN_WARNING "Writing data without ECC to NAND-FLASH is not recommended\n"); ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); ++ break; + case NAND_ECC_SOFT: +- for (i = 0; i < mtd->oobblock; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); ++ this->calculate_ecc(mtd, &this->data_poi[0], &(ecc_code[0])); ++ for (i = 0; i < 3; i++) ++ oob_data[oob_config[i]] = ecc_code[i]; ++ /* Calculate and write the second ECC for 512 Byte page size */ ++ if (mtd->oobblock == 512) { ++ this->calculate_ecc(mtd, &this->data_poi[256], &(ecc_code[3])); ++ for (i = 3; i < 6; i++) ++ oob_data[oob_config[i]] = ecc_code[i]; ++ } ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); + break; + + /* Hardware ecc 3 byte / 256 data, write first half, get ecc, then second, if 512 byte pagesize */ + case NAND_ECC_HW3_256: +- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write */ +- for (i = 0; i < mtd->eccsize; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); ++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write */ ++ this->write_buf(mtd, this->data_poi, mtd->eccsize); + +- this->calculate_ecc (NULL, &(ecc_code[0])); ++ this->calculate_ecc(mtd, NULL, &(ecc_code[0])); + for (i = 0; i < 3; i++) + oob_data[oob_config[i]] = ecc_code[i]; + + if (mtd->oobblock == 512) { +- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic for write*/ +- for (i = mtd->eccsize; i < mtd->oobblock; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); +- this->calculate_ecc (NULL, &(ecc_code[3])); ++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic for write*/ ++ this->write_buf(mtd, &this->data_poi[mtd->eccsize], mtd->oobblock - mtd->eccsize); ++ this->calculate_ecc(mtd, NULL, &(ecc_code[3])); + for (i = 3; i < 6; i++) + oob_data[oob_config[i]] = ecc_code[i]; + } +@@ -436,20 +480,18 @@ + + /* Hardware ecc 3 byte / 512 byte data, write full page */ + case NAND_ECC_HW3_512: +- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */ +- for (i = 0; i < mtd->oobblock; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); +- this->calculate_ecc (NULL, &(ecc_code[0])); ++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */ ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); ++ this->calculate_ecc(mtd, NULL, &(ecc_code[0])); + for (i = 0; i < 3; i++) + oob_data[oob_config[i]] = ecc_code[i]; + break; + + /* Hardware ecc 6 byte / 512 byte data, write full page */ + case NAND_ECC_HW6_512: +- this->enable_hwecc (NAND_ECC_WRITE); /* enable hardware ecc logic */ +- for (i = 0; i < mtd->oobblock; i++) +- writeb ( this->data_poi[i] , this->IO_ADDR_W); +- this->calculate_ecc (NULL, &(ecc_code[0])); ++ this->enable_hwecc(mtd, NAND_ECC_WRITE); /* enable hardware ecc logic */ ++ this->write_buf(mtd, this->data_poi, mtd->oobblock); ++ this->calculate_ecc(mtd, NULL, &(ecc_code[0])); + for (i = 0; i < 6; i++) + oob_data[oob_config[i]] = ecc_code[i]; + break; +@@ -460,8 +502,7 @@ + } + + /* Write out OOB data */ +- for (i = 0; i < mtd->oobsize; i++) +- writeb ( oob_data[i] , this->IO_ADDR_W); ++ this->write_buf(mtd, oob_data, mtd->oobsize); + + /* Send command to actually program the data */ + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); +@@ -490,25 +531,21 @@ + */ + + /* Send command to read back the page */ +- this->cmdfunc (mtd, NAND_CMD_READ0, col, page); ++ this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); + /* Loop through and verify the data */ +- for (i = col; i < last; i++) { +- if (this->data_poi[i] != readb (this->IO_ADDR_R)) { ++ if (this->verify_buf(mtd, this->data_poi, mtd->oobblock)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + return -EIO; + } +- } + + /* check, if we have a fs-supplied oob-buffer */ + if (oob_buf) { +- for (i = 0; i < mtd->oobsize; i++) { +- if (oob_data[i] != readb (this->IO_ADDR_R)) { ++ if (this->verify_buf(mtd, oob_data, mtd->oobsize)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + return -EIO; + } +- } + } else { +- if (eccmode != NAND_ECC_NONE && !col && last == mtd->oobblock) { ++ if (eccmode != NAND_ECC_NONE) { + int ecc_bytes = 0; + + switch (this->eccmode) { +@@ -518,8 +555,7 @@ + case NAND_ECC_HW6_512: ecc_bytes = 6; break; + } + +- for (i = 0; i < mtd->oobsize; i++) +- oob_data[i] = readb (this->IO_ADDR_R); ++ this->read_buf(mtd, oob_data, mtd->oobsize); + + for (i = 0; i < ecc_bytes; i++) { + if (oob_data[oob_config[i]] != ecc_code[i]) { +@@ -531,6 +567,13 @@ + } + } + } ++ /* ++ * Terminate the read command. This is faster than sending a reset command or ++ * applying a 20us delay before issuing the next programm sequence. ++ * This is not a problem for all chips, but I have found a bunch of them. ++ */ ++ this->select_chip(mtd, -1); ++ this->select_chip(mtd, 0); + #endif + return 0; + } +@@ -540,7 +583,7 @@ + */ + static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) + { +- return (nand_read_ecc (mtd, from, len, retlen, buf, NULL, 0)); ++ return nand_read_ecc (mtd, from, len, retlen, buf, NULL, NULL); + } + + +@@ -548,7 +591,7 @@ + * NAND read with ECC + */ + static int nand_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, +- size_t * retlen, u_char * buf, u_char * oob_buf, int oobsel) ++ size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) + { + int j, col, page, end, ecc; + int erase_state = 0; +@@ -557,9 +600,15 @@ + u_char *data_poi, *oob_data = oob_buf; + u_char ecc_calc[6]; + u_char ecc_code[6]; +- int eccmode = oobsel ? this->eccmode : NAND_ECC_NONE; ++ int eccmode; ++ int *oob_config; ++ ++ // use chip default if zero ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; + +- int *oob_config = oobconfigs[oobsel]; ++ eccmode = oobsel->useecc ? this->eccmode : NAND_ECC_NONE; ++ oob_config = oobsel->eccpos; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); + +@@ -574,7 +623,7 @@ + nand_get_chip (this, mtd ,FL_READING, &erase_state); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* First we calculate the starting page */ + page = from >> this->page_shift; +@@ -596,7 +645,7 @@ + if (!this->dev_ready) + udelay (this->chip_delay); + else +- while (!this->dev_ready()); ++ while (!this->dev_ready(mtd)); + } + + /* +@@ -616,39 +665,40 @@ + + j = 0; + switch (eccmode) { +- case NAND_ECC_NONE: /* No ECC, Read in a page */ +- while (j < end) +- data_poi[j++] = readb (this->IO_ADDR_R); ++ case NAND_ECC_NONE: { /* No ECC, Read in a page */ ++ static unsigned long lastwhinge = 0; ++ if ((lastwhinge / HZ) != (jiffies / HZ)) { ++ printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); ++ lastwhinge = jiffies; ++ } ++ this->read_buf(mtd, data_poi, end); + break; ++ } + + case NAND_ECC_SOFT: /* Software ECC 3/256: Read in a page + oob data */ +- while (j < end) +- data_poi[j++] = readb (this->IO_ADDR_R); +- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); ++ this->read_buf(mtd, data_poi, end); ++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); + if (mtd->oobblock == 512) +- this->calculate_ecc (&data_poi[256], &ecc_calc[3]); ++ this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); + break; + + case NAND_ECC_HW3_256: /* Hardware ECC 3 byte /256 byte data: Read in first 256 byte, get ecc, */ +- this->enable_hwecc (NAND_ECC_READ); +- while (j < ecc) +- data_poi[j++] = readb (this->IO_ADDR_R); +- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */ ++ this->enable_hwecc(mtd, NAND_ECC_READ); ++ this->read_buf(mtd, data_poi, ecc); ++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */ + + if (mtd->oobblock == 512) { /* read second, if pagesize = 512 */ +- this->enable_hwecc (NAND_ECC_READ); +- while (j < end) +- data_poi[j++] = readb (this->IO_ADDR_R); +- this->calculate_ecc (&data_poi[256], &ecc_calc[3]); /* read from hardware */ ++ this->enable_hwecc(mtd, NAND_ECC_READ); ++ this->read_buf(mtd, &data_poi[ecc], end-ecc); ++ this->calculate_ecc(mtd, &data_poi[256], &ecc_calc[3]); /* read from hardware */ + } + break; + + case NAND_ECC_HW3_512: + case NAND_ECC_HW6_512: /* Hardware ECC 3/6 byte / 512 byte data : Read in a page */ +- this->enable_hwecc (NAND_ECC_READ); +- while (j < end) +- data_poi[j++] = readb (this->IO_ADDR_R); +- this->calculate_ecc (&data_poi[0], &ecc_calc[0]); /* read from hardware */ ++ this->enable_hwecc(mtd, NAND_ECC_READ); ++ this->read_buf(mtd, data_poi, end); ++ this->calculate_ecc(mtd, &data_poi[0], &ecc_calc[0]); /* read from hardware */ + break; + + default: +@@ -658,7 +708,7 @@ + + /* read oobdata */ + for (j = 0; j < mtd->oobsize; j++) +- oob_data[oob + j] = readb (this->IO_ADDR_R); ++ oob_data[oob + j] = this->read_byte(mtd); + + /* Skip ECC, if not active */ + if (eccmode == NAND_ECC_NONE) +@@ -669,7 +719,7 @@ + ecc_code[j] = oob_data[oob + oob_config[j]]; + + /* correct data, if neccecary */ +- ecc_status = this->correct_data (&data_poi[0], &ecc_code[0], &ecc_calc[0]); ++ ecc_status = this->correct_data(mtd, &data_poi[0], &ecc_code[0], &ecc_calc[0]); + /* check, if we have a fs supplied oob-buffer */ + if (oob_buf) { + oob += mtd->oobsize; +@@ -682,7 +732,7 @@ + } + + if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) { +- ecc_status = this->correct_data (&data_poi[256], &ecc_code[3], &ecc_calc[3]); ++ ecc_status = this->correct_data(mtd, &data_poi[256], &ecc_code[3], &ecc_calc[3]); + if (oob_buf) { + *((int *)&oob_data[oob]) = ecc_status; + oob += sizeof(int); +@@ -705,7 +755,7 @@ + } + + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -753,7 +803,7 @@ + nand_get_chip (this, mtd , FL_READING, &erase_state); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Send the read command */ + this->cmdfunc (mtd, NAND_CMD_READOOB, col, page); +@@ -761,13 +811,20 @@ + * Read the data, if we read more than one page + * oob data, let the device transfer the data ! + */ +- for (i = 0; i < len; i++) { +- buf[i] = readb (this->IO_ADDR_R); +- if ((col++ & (mtd->oobsize - 1)) == (mtd->oobsize - 1)) ++ i = 0; ++ while (i < len) { ++ int thislen = (mtd->oobsize - col) & (mtd->oobsize - 1); ++ if (!thislen) ++ thislen = mtd->oobsize; ++ thislen = min_t(int, thislen, len); ++ this->read_buf(mtd, &buf[i], thislen); ++ i += thislen; ++ col += thislen; ++ /* Delay between pages */ + udelay (this->chip_delay); + } + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -780,45 +837,54 @@ + return 0; + } + ++#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 ++ + /* + * Use NAND write ECC + */ + static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) + { +- return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, 0)); ++ return (nand_write_ecc (mtd, to, len, retlen, buf, NULL, NULL)); + } + /* + * NAND write with ECC + */ + static int nand_write_ecc (struct mtd_info *mtd, loff_t to, size_t len, +- size_t * retlen, const u_char * buf, u_char * eccbuf, int oobsel) ++ size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) + { +- int i, page, col, cnt, ret = 0, oob = 0, written = 0; ++ int page, ret = 0, oob = 0, written = 0; + struct nand_chip *this = mtd->priv; + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + + /* Do not allow write past end of device */ + if ((to + len) > mtd->size) { +- DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); ++ DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); ++ return -EINVAL; ++ } ++ ++ /* reject writes, which are not page aligned */ ++ if (NOTALIGNED (to) || NOTALIGNED(len)) { ++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); + return -EINVAL; + } + ++ // if oobsel is NULL, use chip defaults ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; ++ + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + +- /* Get the starting column */ +- col = to & (mtd->oobblock - 1); +- + /* Grab the lock and see if the device is available */ + nand_get_chip (this, mtd, FL_WRITING, NULL); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); +- if (!(readb (this->IO_ADDR_R) & 0x80)) { ++ if (!(this->read_byte(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n"); + ret = -EIO; + goto out; +@@ -826,42 +892,27 @@ + + /* Loop until all data is written */ + while (written < len) { +- /* +- * Check, if we have a full page write, then we can +- * use the given buffer, else we have to copy +- */ +- if (!col && (len - written) >= mtd->oobblock) { ++ int cnt = mtd->oobblock; + this->data_poi = (u_char*) &buf[written]; +- cnt = mtd->oobblock; +- } else { +- cnt = 0; +- for (i = col; i < len && i < mtd->oobblock; i++) { +- this->data_buf[i] = buf[written + i]; +- cnt++; +- } +- this->data_poi = this->data_buf; +- } +- /* We use the same function for write and writev !) */ ++ /* We use the same function for write and writev */ + if (eccbuf) { +- ret = nand_write_page (mtd, this, page, col, cnt ,&eccbuf[oob], oobsel); ++ ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel); + oob += mtd->oobsize; + } else +- ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel); ++ ret = nand_write_page (mtd, this, page, NULL, oobsel); + + if (ret) + goto out; + + /* Update written bytes count */ + written += cnt; +- /* Next write is aligned */ +- col = 0; + /* Increment page address */ + page++; + } + + out: + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -873,13 +924,21 @@ + return ret; + } + ++static u_char ffchars[] = { ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, ++ 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff ++}; ++ + /* + * NAND write out-of-band + */ + static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, size_t * retlen, const u_char * buf) + { +- int i, column, page, status, ret = 0; ++ int column, page, status, ret = 0; + struct nand_chip *this = mtd->priv; ++#ifdef CONFIG_MTD_NAND_VERIFY_WRITE ++ int i; ++#endif + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); + +@@ -902,27 +961,31 @@ + nand_get_chip (this, mtd, FL_WRITING, NULL); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); ++ ++ /* Reset the chip. Some chips (like the Toshiba TC5832DC found ++ in one of my DiskOnChip 2000 test units) will clear the whole ++ data page too if we don't do this. I have no clue why, but ++ I seem to have 'fixed' it in the doc2000 driver in ++ August 1999. dwmw2. */ ++ this->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); +- if (!(readb (this->IO_ADDR_R) & 0x80)) { ++ if (!(this->read_byte(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n"); + ret = -EIO; + goto out; + } +- + /* Write out desired data */ + this->cmdfunc (mtd, NAND_CMD_SEQIN, mtd->oobblock, page); ++ + /* prepad 0xff for partial programming */ +- for (i = 0; i < column; i++) +- writeb (0xff, this->IO_ADDR_W); ++ this->write_buf(mtd, ffchars, column); + /* write data */ +- for (i = 0; i < len; i++) +- writeb (buf[i], this->IO_ADDR_W); ++ this->write_buf(mtd, buf, len); + /* postpad 0xff for partial programming */ +- for (i = len + column; i < mtd->oobsize; i++) +- writeb (0xff, this->IO_ADDR_W); ++ this->write_buf(mtd, ffchars, mtd->oobsize - (len+column)); + + /* Send command to program the OOB data */ + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); +@@ -944,7 +1007,7 @@ + + /* Loop through and verify the data */ + for (i = 0; i < len; i++) { +- if (buf[i] != readb (this->IO_ADDR_R)) { ++ if (buf[i] != this->read_byte(mtd)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page); + ret = -EIO; + goto out; +@@ -954,7 +1017,7 @@ + + out: + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -976,9 +1039,9 @@ + } + + static int nand_writev_ecc (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, +- loff_t to, size_t * retlen, u_char *eccbuf, int oobsel) ++ loff_t to, size_t * retlen, u_char *eccbuf, struct nand_oobinfo *oobsel) + { +- int i, page, col, cnt, len, total_len, ret = 0, written = 0; ++ int i, page, len, total_len, ret = 0, written = 0; + struct nand_chip *this = mtd->priv; + + /* Calculate total length of data */ +@@ -995,39 +1058,42 @@ + return -EINVAL; + } + ++ /* reject writes, which are not page aligned */ ++ if (NOTALIGNED (to) || NOTALIGNED(total_len)) { ++ printk (KERN_NOTICE "nand_write_ecc: Attempt to write not page aligned data\n"); ++ return -EINVAL; ++ } ++ ++ // if oobsel is NULL, use chip defaults ++ if (oobsel == NULL) ++ oobsel = &mtd->oobinfo; ++ + /* Shift to get page */ + page = ((int) to) >> this->page_shift; + +- /* Get the starting column */ +- col = to & (mtd->oobblock - 1); +- + /* Grab the lock and see if the device is available */ + nand_get_chip (this, mtd, FL_WRITING, NULL); + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); +- if (!(readb (this->IO_ADDR_R) & 0x80)) { ++ if (!(this->read_byte(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Device is write protected!!!\n"); + ret = -EIO; + goto out; + } + + /* Loop until all iovecs' data has been written */ +- cnt = col; + len = 0; +- + while (count) { + /* +- * Check, if we write from offset 0 and if the tuple +- * gives us not enough data for a full page write. Then we +- * can use the iov direct, else we have to copy into +- * data_buf. ++ * Check, if the tuple gives us not enough data for a ++ * full page write. Then we can use the iov direct, ++ * else we have to copy into data_buf. + */ +- if (!cnt && (vecs->iov_len - len) >= mtd->oobblock) { +- cnt = mtd->oobblock; ++ if ((vecs->iov_len - len) >= mtd->oobblock) { + this->data_poi = (u_char *) vecs->iov_base; + this->data_poi += len; + len += mtd->oobblock; +@@ -1042,6 +1108,7 @@ + * Read data out of each tuple until we have a full page + * to write or we've read all the tuples. + */ ++ int cnt = 0; + while ((cnt < mtd->oobblock) && count) { + if (vecs->iov_base != NULL && vecs->iov_len) { + this->data_buf[cnt++] = ((u_char *) vecs->iov_base)[len++]; +@@ -1057,15 +1124,12 @@ + } + + /* We use the same function for write and writev !) */ +- ret = nand_write_page (mtd, this, page, col, cnt, NULL, oobsel); ++ ret = nand_write_page (mtd, this, page, NULL, oobsel); + if (ret) + goto out; + + /* Update written bytes count */ +- written += (cnt - col); +- +- /* Reset written byte counter and column */ +- col = cnt = 0; ++ written += mtd->oobblock;; + + /* Increment page address */ + page++; +@@ -1073,7 +1137,7 @@ + + out: + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + + /* Wake up anyone waiting on the device */ + spin_lock_bh (&this->chip_lock); +@@ -1125,11 +1189,11 @@ + pages_per_block = mtd->erasesize / mtd->oobblock; + + /* Select the NAND device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Check the WP bit */ + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); +- if (!(readb (this->IO_ADDR_R) & 0x80)) { ++ if (!(this->read_byte(mtd) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; +@@ -1142,8 +1206,7 @@ + + while (len) { + /* Check if we have a bad block, we do not erase bad blocks ! */ +- this->cmdfunc (mtd, NAND_CMD_READOOB, NAND_BADBLOCK_POS, page); +- if (readb (this->IO_ADDR_R) != 0xff) { ++ if (this->block_bad(mtd, page)) { + printk (KERN_WARNING "nand_erase: attempt to erase a bad block at page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; +@@ -1179,7 +1242,7 @@ + if (this->state == FL_ERASING || this->state == FL_READY) { + /* Select the NAND device again, if we were interrupted */ + this->state = FL_ERASING; +- nand_select (); ++ this->select_chip(mtd, 0); + continue; + } else { + set_current_state (TASK_UNINTERRUPTIBLE); +@@ -1194,7 +1257,7 @@ + + erase_exit: + /* De-select the NAND device */ +- nand_deselect (); ++ this->select_chip(mtd, -1); + spin_unlock_bh (&this->chip_lock); + + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;; +@@ -1205,6 +1268,7 @@ + /* The device is ready */ + spin_lock_bh (&this->chip_lock); + this->state = FL_READY; ++ wake_up (&this->wq); + spin_unlock_bh (&this->chip_lock); + + /* Return more or less happy */ +@@ -1259,7 +1323,7 @@ + /* + * Scan for the NAND device + */ +-int nand_scan (struct mtd_info *mtd) ++int nand_scan (struct mtd_info *mtd, int maxchips) + { + int i, nand_maf_id, nand_dev_id; + struct nand_chip *this = mtd->priv; +@@ -1276,23 +1340,38 @@ + if (this->waitfunc == NULL) + this->waitfunc = nand_wait; + ++ if (!this->block_bad) ++ this->block_bad = nand_block_bad; ++ if (!this->select_chip) ++ this->select_chip = nand_select_chip; ++ if (!this->write_byte) ++ this->write_byte = nand_write_byte; ++ if (!this->read_byte) ++ this->read_byte = nand_read_byte; ++ if (!this->write_buf) ++ this->write_buf = nand_write_buf; ++ if (!this->read_buf) ++ this->read_buf = nand_read_buf; ++ if (!this->verify_buf) ++ this->verify_buf = nand_verify_buf; ++ + /* Select the device */ +- nand_select (); ++ this->select_chip(mtd, 0); + + /* Send the command for reading device ID */ + this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); + + /* Read manufacturer and device IDs */ +- nand_maf_id = readb (this->IO_ADDR_R); +- nand_dev_id = readb (this->IO_ADDR_R); ++ nand_maf_id = this->read_byte(mtd); ++ nand_dev_id = this->read_byte(mtd); + + /* Print and store flash device information */ + for (i = 0; nand_flash_ids[i].name != NULL; i++) { + if (nand_dev_id == nand_flash_ids[i].id && !mtd->size) { + mtd->name = nand_flash_ids[i].name; + mtd->erasesize = nand_flash_ids[i].erasesize; +- mtd->size = (1 << nand_flash_ids[i].chipshift); + mtd->eccsize = 256; ++ this->chipshift = nand_flash_ids[i].chipshift; + if (nand_flash_ids[i].page256) { + mtd->oobblock = 256; + mtd->oobsize = 8; +@@ -1307,13 +1386,34 @@ + if (nand_manuf_ids[i].id == nand_maf_id) + break; + } +- printk (KERN_INFO "NAND device: Manufacture ID:" ++ printk (KERN_INFO "NAND device: Manufacturer ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[i].name , mtd->name); + break; + } + } + ++ if (!mtd->name) { ++ printk (KERN_WARNING "No NAND device found!!!\n"); ++ return 1; ++ } ++ ++ for (i=1; i < maxchips; i++) { ++ this->select_chip(mtd, i); ++ ++ /* Send the command for reading device ID */ ++ this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); ++ ++ /* Read manufacturer and device IDs */ ++ if (nand_maf_id != this->read_byte(mtd) || ++ nand_dev_id != this->read_byte(mtd)) ++ break; ++ } ++ if (i > 1) ++ printk(KERN_INFO "%d NAND chips detected\n", i); ++ ++ mtd->size = (1 << this->chipshift) /* * i when we fix the rest of the code */; ++ + /* + * check ECC mode, default to software + * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize +@@ -1324,6 +1424,7 @@ + switch (this->eccmode) { + + case NAND_ECC_HW3_512: ++ case NAND_ECC_HW6_512: + if (mtd->oobblock == 256) { + printk (KERN_WARNING "512 byte HW ECC not possible on 256 Byte pagesize, fallback to SW ECC \n"); + this->eccmode = NAND_ECC_SOFT; +@@ -1340,6 +1441,7 @@ + BUG(); + + case NAND_ECC_NONE: ++ printk (KERN_WARNING "NAND_ECC_NONE selected by board driver. This is not recommended !!\n"); + this->eccmode = NAND_ECC_NONE; + break; + +@@ -1359,18 +1461,11 @@ + spin_lock_init (&this->chip_lock); + + /* De-select the device */ +- nand_deselect (); +- +- /* Print warning message for no device */ +- if (!mtd->size) { +- printk (KERN_WARNING "No NAND device found!!!\n"); +- return 1; +- } ++ this->select_chip(mtd, -1); + + /* Fill in remaining MTD driver data */ + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH | MTD_ECC; +- mtd->module = THIS_MODULE; + mtd->ecctype = MTD_ECC_SW; + mtd->erase = nand_erase; + mtd->point = NULL; +@@ -1389,6 +1484,7 @@ + mtd->unlock = NULL; + mtd->suspend = NULL; + mtd->resume = NULL; ++ mtd->owner = THIS_MODULE; + + /* Return happy */ + return 0; +@@ -1397,5 +1493,5 @@ + EXPORT_SYMBOL (nand_scan); + + MODULE_LICENSE ("GPL"); +-MODULE_AUTHOR ("Steven J. Hill <sjhill@cotw.com>, Thomas Gleixner <tglx@linutronix.de>"); ++MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>"); + MODULE_DESCRIPTION ("Generic NAND flash driver code"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/nand_ecc.c linux/drivers/mtd/nand/nand_ecc.c +--- linux-mips-2.4.27/drivers/mtd/nand/nand_ecc.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/nand_ecc.c 2004-11-19 10:25:12.011182768 +0100 +@@ -1,10 +1,10 @@ + /* + * drivers/mtd/nand_ecc.c + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * Toshiba America Electronics Components, Inc. + * +- * $Id: nand_ecc.c,v 1.8 2002/09/16 09:19:53 dwmw2 Exp $ ++ * $Id: nand_ecc.c,v 1.10 2003/07/01 23:31:15 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License +@@ -17,6 +17,7 @@ + #include <linux/types.h> + #include <linux/kernel.h> + #include <linux/module.h> ++#include <linux/mtd/nand_ecc.h> + + /* + * Pre-calculated 256-way 1 byte column parity +@@ -84,7 +85,7 @@ + /* + * Calculate 3 byte ECC code for 256 byte block + */ +-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code) ++void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) + { + u_char idx, reg1, reg2, reg3; + int j; +@@ -119,7 +120,7 @@ + /* + * Detect and correct a 1 bit error for 256 byte block + */ +-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc) ++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc) + { + u_char a, b, c, d1, d2, d3, add, bit, i; + +@@ -209,5 +210,5 @@ + EXPORT_SYMBOL(nand_correct_data); + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com>"); ++MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>"); + MODULE_DESCRIPTION("Generic NAND ECC support"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/nand_ids.c linux/drivers/mtd/nand/nand_ids.c +--- linux-mips-2.4.27/drivers/mtd/nand/nand_ids.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/nand_ids.c 2004-11-19 10:25:12.013182464 +0100 +@@ -4,7 +4,7 @@ + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * + * +- * $Id: nand_ids.c,v 1.1 2002/12/02 22:06:04 gleixner Exp $ ++ * $Id: nand_ids.c,v 1.4 2003/05/21 15:15:08 dwmw2 Exp $ + * + * 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 +@@ -18,21 +18,21 @@ + * Chip ID list + */ + struct nand_flash_dev nand_flash_ids[] = { +- {"NAND 1MB 5V", 0x6e, 20, 0x1000, 1}, // 1Mb 5V +- {"NAND 2MB 5V", 0x64, 21, 0x1000, 1}, // 2Mb 5V +- {"NAND 4MB 5V", 0x6b, 22, 0x2000, 0}, // 4Mb 5V +- {"NAND 1MB 3,3V", 0xe8, 20, 0x1000, 1}, // 1Mb 3.3V +- {"NAND 1MB 3,3V", 0xec, 20, 0x1000, 1}, // 1Mb 3.3V +- {"NAND 2MB 3,3V", 0xea, 21, 0x1000, 1}, // 2Mb 3.3V +- {"NAND 4MB 3,3V", 0xd5, 22, 0x2000, 0}, // 4Mb 3.3V +- {"NAND 4MB 3,3V", 0xe3, 22, 0x2000, 0}, // 4Mb 3.3V +- {"NAND 4MB 3,3V", 0xe5, 22, 0x2000, 0}, // 4Mb 3.3V +- {"NAND 8MB 3,3V", 0xd6, 23, 0x2000, 0}, // 8Mb 3.3V +- {"NAND 8MB 3,3V", 0xe6, 23, 0x2000, 0}, // 8Mb 3.3V +- {"NAND 16MB 3,3V", 0x73, 24, 0x4000, 0},// 16Mb 3,3V +- {"NAND 32MB 3,3V", 0x75, 25, 0x4000, 0}, // 32Mb 3,3V +- {"NAND 64MB 3,3V", 0x76, 26, 0x4000, 0}, // 64Mb 3,3V +- {"NAND 128MB 3,3V", 0x79, 27, 0x4000, 0}, // 128Mb 3,3V ++ {"NAND 1MiB 5V", 0x6e, 20, 0x1000, 1}, ++ {"NAND 2MiB 5V", 0x64, 21, 0x1000, 1}, ++ {"NAND 4MiB 5V", 0x6b, 22, 0x2000, 0}, ++ {"NAND 1MiB 3,3V", 0xe8, 20, 0x1000, 1}, ++ {"NAND 1MiB 3,3V", 0xec, 20, 0x1000, 1}, ++ {"NAND 2MiB 3,3V", 0xea, 21, 0x1000, 1}, ++ {"NAND 4MiB 3,3V", 0xd5, 22, 0x2000, 0}, ++ {"NAND 4MiB 3,3V", 0xe3, 22, 0x2000, 0}, ++ {"NAND 4MiB 3,3V", 0xe5, 22, 0x2000, 0}, ++ {"NAND 8MiB 3,3V", 0xd6, 23, 0x2000, 0}, ++ {"NAND 8MiB 3,3V", 0xe6, 23, 0x2000, 0}, ++ {"NAND 16MiB 3,3V", 0x73, 24, 0x4000, 0}, ++ {"NAND 32MiB 3,3V", 0x75, 25, 0x4000, 0}, ++ {"NAND 64MiB 3,3V", 0x76, 26, 0x4000, 0}, ++ {"NAND 128MiB 3,3V", 0x79, 27, 0x4000, 0}, + {NULL,} + }; + +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/spia.c linux/drivers/mtd/nand/spia.c +--- linux-mips-2.4.27/drivers/mtd/nand/spia.c 2003-02-26 01:53:50.000000000 +0100 ++++ linux/drivers/mtd/nand/spia.c 2004-11-19 10:25:12.014182312 +0100 +@@ -1,14 +1,14 @@ + /* + * drivers/mtd/nand/spia.c + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * + * 10-29-2001 TG change to support hardwarespecific access + * to controllines (due to change in nand.c) + * page_cache added + * +- * $Id: spia.c,v 1.16 2002/03/05 13:50:47 dwmw2 Exp $ ++ * $Id: spia.c,v 1.21 2003/07/11 15:12:29 dwmw2 Exp $ + * + * 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 +@@ -20,6 +20,8 @@ + * a 64Mibit (8MiB x 8 bits) NAND flash device. + */ + ++#include <linux/kernel.h> ++#include <linux/init.h> + #include <linux/slab.h> + #include <linux/module.h> + #include <linux/mtd/mtd.h> +@@ -35,14 +37,14 @@ + /* + * Values specific to the SPIA board (used with EP7212 processor) + */ +-#define SPIA_IO_ADDR = 0xd0000000 /* Start of EP7212 IO address space */ +-#define SPIA_FIO_ADDR = 0xf0000000 /* Address where flash is mapped */ +-#define SPIA_PEDR = 0x0080 /* ++#define SPIA_IO_BASE 0xd0000000 /* Start of EP7212 IO address space */ ++#define SPIA_FIO_BASE 0xf0000000 /* Address where flash is mapped */ ++#define SPIA_PEDR 0x0080 /* + * IO offset to Port E data register + * where the CLE, ALE and NCE pins + * are wired to. + */ +-#define SPIA_PEDDR = 0x00c0 /* ++#define SPIA_PEDDR 0x00c0 /* + * IO offset to Port E data direction + * register so we can control the IO + * lines. +@@ -62,21 +64,20 @@ + MODULE_PARM(spia_pedr, "i"); + MODULE_PARM(spia_peddr, "i"); + +-__setup("spia_io_base=",spia_io_base); +-__setup("spia_fio_base=",spia_fio_base); +-__setup("spia_pedr=",spia_pedr); +-__setup("spia_peddr=",spia_peddr); +- + /* + * Define partitions for flash device + */ + const static struct mtd_partition partition_info[] = { +- { name: "SPIA flash partition 1", +- offset: 0, +- size: 2*1024*1024 }, +- { name: "SPIA flash partition 2", +- offset: 2*1024*1024, +- size: 6*1024*1024 } ++ { ++ .name = "SPIA flash partition 1", ++ .offset = 0, ++ .size = 2*1024*1024 ++ }, ++ { ++ .name = "SPIA flash partition 2", ++ .offset = 2*1024*1024, ++ .size = 6*1024*1024 ++ } + }; + #define NUM_PARTITIONS 2 + +@@ -84,7 +85,7 @@ + /* + * hardware specific access to control-lines + */ +-void spia_hwcontrol(int cmd){ ++static void spia_hwcontrol(struct mtd_info *mtd, int cmd){ + + switch(cmd){ + +@@ -139,7 +140,7 @@ + this->chip_delay = 15; + + /* Scan to find existence of the device */ +- if (nand_scan (spia_mtd)) { ++ if (nand_scan (spia_mtd, 1)) { + kfree (spia_mtd); + return -ENXIO; + } +@@ -152,16 +153,6 @@ + return -ENOMEM; + } + +- /* Allocate memory for internal data buffer */ +- this->data_cache = kmalloc (sizeof(u_char) * (spia_mtd->oobblock + spia_mtd->oobsize), GFP_KERNEL); +- if (!this->data_cache) { +- printk ("Unable to allocate NAND data cache for SPIA.\n"); +- kfree (this->data_buf); +- kfree (spia_mtd); +- return = -ENOMEM; +- } +- this->cache_page = -1; +- + /* Register the partitions */ + add_mtd_partitions(spia_mtd, partition_info, NUM_PARTITIONS); + +@@ -183,7 +174,6 @@ + + /* Free internal data buffer */ + kfree (this->data_buf); +- kfree (this->page_cache); + + /* Free the MTD device structure */ + kfree (spia_mtd); +@@ -192,5 +182,5 @@ + #endif + + MODULE_LICENSE("GPL"); +-MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com"); ++MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com"); + MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on SPIA board"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/toto.c linux/drivers/mtd/nand/toto.c +--- linux-mips-2.4.27/drivers/mtd/nand/toto.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/nand/toto.c 2004-11-19 10:25:12.016182008 +0100 +@@ -0,0 +1,221 @@ ++/* ++ * drivers/mtd/nand/toto.c ++ * ++ * Copyright (c) 2003 Texas Instruments ++ * ++ * Derived from drivers/mtd/autcpu12.c ++ * ++ * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de> ++ * ++ * This program is free software; you can redistribute it and/or modify ++ * it under the terms of the GNU General Public License version 2 as ++ * published by the Free Software Foundation. ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device found on the ++ * TI fido board. It supports 32MiB and 64MiB cards ++ * ++ * $Id: toto.c,v 1.2 2003/10/21 10:04:58 dwmw2 Exp $ ++ */ ++ ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/delay.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> ++#include <asm/io.h> ++#include <asm/arch/hardware.h> ++#include <asm/sizes.h> ++#include <asm/arch/toto.h> ++#include <asm/arch-omap1510/hardware.h> ++#include <asm/arch/gpio.h> ++ ++/* ++ * MTD structure for TOTO board ++ */ ++static struct mtd_info *toto_mtd = NULL; ++ ++static int toto_io_base = OMAP_FLASH_1_BASE; ++ ++#define CONFIG_NAND_WORKAROUND 1 ++ ++#define NAND_NCE 0x4000 ++#define NAND_CLE 0x1000 ++#define NAND_ALE 0x0002 ++#define NAND_MASK (NAND_CLE | NAND_ALE | NAND_NCE) ++ ++#define T_NAND_CTL_CLRALE(iob) gpiosetout(NAND_ALE, 0) ++#define T_NAND_CTL_SETALE(iob) gpiosetout(NAND_ALE, NAND_ALE) ++#ifdef CONFIG_NAND_WORKAROUND /* "some" dev boards busted, blue wired to rts2 :( */ ++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0); rts2setout(2, 2) ++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE); rts2setout(2, 0) ++#else ++#define T_NAND_CTL_CLRCLE(iob) gpiosetout(NAND_CLE, 0) ++#define T_NAND_CTL_SETCLE(iob) gpiosetout(NAND_CLE, NAND_CLE) ++#endif ++#define T_NAND_CTL_SETNCE(iob) gpiosetout(NAND_NCE, 0) ++#define T_NAND_CTL_CLRNCE(iob) gpiosetout(NAND_NCE, NAND_NCE) ++ ++/* ++ * Define partitions for flash devices ++ */ ++ ++static struct mtd_partition partition_info64M[] = { ++ { .name = "toto kernel partition 1", ++ .offset = 0, ++ .size = 2 * SZ_1M }, ++ { .name = "toto file sys partition 2", ++ .offset = 2 * SZ_1M, ++ .size = 14 * SZ_1M }, ++ { .name = "toto user partition 3", ++ .offset = 16 * SZ_1M, ++ .size = 16 * SZ_1M }, ++ { .name = "toto devboard extra partition 4", ++ .offset = 32 * SZ_1M, ++ .size = 32 * SZ_1M }, ++}; ++ ++static struct mtd_partition partition_info32M[] = { ++ { .name = "toto kernel partition 1", ++ .offset = 0, ++ .size = 2 * SZ_1M }, ++ { .name = "toto file sys partition 2", ++ .offset = 2 * SZ_1M, ++ .size = 14 * SZ_1M }, ++ { .name = "toto user partition 3", ++ .offset = 16 * SZ_1M, ++ .size = 16 * SZ_1M }, ++}; ++ ++#define NUM_PARTITIONS32M 3 ++#define NUM_PARTITIONS64M 4 ++/* ++ * hardware specific access to control-lines ++*/ ++ ++static void toto_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ ++ udelay(1); /* hopefully enough time for tc make proceding write to clear */ ++ switch(cmd){ ++ ++ case NAND_CTL_SETCLE: T_NAND_CTL_SETCLE(cmd); break; ++ case NAND_CTL_CLRCLE: T_NAND_CTL_CLRCLE(cmd); break; ++ ++ case NAND_CTL_SETALE: T_NAND_CTL_SETALE(cmd); break; ++ case NAND_CTL_CLRALE: T_NAND_CTL_CLRALE(cmd); break; ++ ++ case NAND_CTL_SETNCE: T_NAND_CTL_SETNCE(cmd); break; ++ case NAND_CTL_CLRNCE: T_NAND_CTL_CLRNCE(cmd); break; ++ } ++ udelay(1); /* allow time to ensure gpio state to over take memory write */ ++} ++ ++/* ++ * Main initialization routine ++ */ ++int __init toto_init (void) ++{ ++ struct nand_chip *this; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ toto_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!toto_mtd) { ++ printk (KERN_WARNING "Unable to allocate toto NAND MTD device structure.\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&toto_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) toto_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ toto_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = toto_io_base; ++ this->IO_ADDR_W = toto_io_base; ++ this->hwcontrol = toto_hwcontrol; ++ this->dev_ready = NULL; ++ /* 25 us command delay time */ ++ this->chip_delay = 30; ++ this->eccmode = NAND_ECC_SOFT; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (toto_mtd, 1)) { ++ err = -ENXIO; ++ goto out_mtd; ++ } ++ ++ /* Allocate memory for internal data buffer */ ++ this->data_buf = kmalloc (sizeof(u_char) * (toto_mtd->oobblock + toto_mtd->oobsize), GFP_KERNEL); ++ if (!this->data_buf) { ++ printk (KERN_WARNING "Unable to allocate NAND data buffer for toto.\n"); ++ err = -ENOMEM; ++ goto out_mtd; ++ } ++ ++ /* Register the partitions */ ++ switch(toto_mtd->size){ ++ case SZ_64M: add_mtd_partitions(toto_mtd, partition_info64M, NUM_PARTITIONS64M); break; ++ case SZ_32M: add_mtd_partitions(toto_mtd, partition_info32M, NUM_PARTITIONS32M); break; ++ default: { ++ printk (KERN_WARNING "Unsupported Nand device\n"); ++ err = -ENXIO; ++ goto out_buf; ++ } ++ } ++ ++ gpioreserve(NAND_MASK); /* claim our gpios */ ++ archflashwp(0,0); /* open up flash for writing */ ++ ++ goto out; ++ ++out_buf: ++ kfree (this->data_buf); ++out_mtd: ++ kfree (toto_mtd); ++out: ++ return err; ++} ++ ++module_init(toto_init); ++ ++/* ++ * Clean up routine ++ */ ++static void __exit toto_cleanup (void) ++{ ++ struct nand_chip *this = (struct nand_chip *) &toto_mtd[1]; ++ ++ /* Unregister partitions */ ++ del_mtd_partitions(toto_mtd); ++ ++ /* Unregister the device */ ++ del_mtd_device (toto_mtd); ++ ++ /* Free internal data buffers */ ++ kfree (this->data_buf); ++ ++ /* Free the MTD device structure */ ++ kfree (toto_mtd); ++ ++ /* stop flash writes */ ++ archflashwp(0,1); ++ ++ /* release gpios to system */ ++ gpiorelease(NAND_MASK); ++} ++module_exit(toto_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Richard Woodruff <r-woodruff2@ti.com>"); ++MODULE_DESCRIPTION("Glue layer for NAND flash on toto board"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/tx4925ndfmc.c linux/drivers/mtd/nand/tx4925ndfmc.c +--- linux-mips-2.4.27/drivers/mtd/nand/tx4925ndfmc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/nand/tx4925ndfmc.c 2004-11-19 10:25:12.017181856 +0100 +@@ -0,0 +1,442 @@ ++/* ++ * drivers/mtd/tx4925ndfmc.c ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device found on the ++ * Toshiba RBTX4925 reference board, which is a SmartMediaCard. It supports ++ * 16MiB, 32MiB and 64MiB cards. ++ * ++ * Author: MontaVista Software, Inc. source@mvista.com ++ * ++ * Derived from drivers/mtd/autcpu12.c ++ * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) ++ * ++ * $Id: tx4925ndfmc.c,v 1.1 2003/11/04 22:59:11 ahennessy Exp $ ++ * ++ * Copyright (C) 2001 Toshiba Corporation ++ * ++ * 2003 (c) MontaVista Software, Inc. This file is licensed under ++ * the terms of the GNU General Public License version 2. This program ++ * is licensed "as is" without any warranty of any kind, whether express ++ * or implied. ++ * ++ */ ++ ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/partitions.h> ++#include <linux/delay.h> ++#include <asm/io.h> ++#include <asm/tx4925/tx4925_nand.h> ++ ++extern struct nand_oobinfo jffs2_oobinfo; ++ ++/* ++ * MTD structure for RBTX4925 board ++ */ ++static struct mtd_info *tx4925ndfmc_mtd = NULL; ++ ++/* ++ * Module stuff ++ */ ++#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) ++#define tx4925ndfmc_init init_module ++#define tx4925ndfmc_cleanup cleanup_module ++#endif ++ ++/* ++ * Define partitions for flash devices ++ */ ++ ++static struct mtd_partition partition_info16k[] = { ++ { .name = "RBTX4925 flash partition 1", ++ .offset = 0, ++ .size = 8 * 0x00100000 }, ++ { .name = "RBTX4925 flash partition 2", ++ .offset = 8 * 0x00100000, ++ .size = 8 * 0x00100000 }, ++}; ++ ++static struct mtd_partition partition_info32k[] = { ++ { .name = "RBTX4925 flash partition 1", ++ .offset = 0, ++ .size = 8 * 0x00100000 }, ++ { .name = "RBTX4925 flash partition 2", ++ .offset = 8 * 0x00100000, ++ .size = 24 * 0x00100000 }, ++}; ++ ++static struct mtd_partition partition_info64k[] = { ++ { .name = "User FS", ++ .offset = 0, ++ .size = 16 * 0x00100000 }, ++ { .name = "RBTX4925 flash partition 2", ++ .offset = 16 * 0x00100000, ++ .size = 48 * 0x00100000}, ++}; ++ ++static struct mtd_partition partition_info128k[] = { ++ { .name = "Skip bad section", ++ .offset = 0, ++ .size = 16 * 0x00100000 }, ++ { .name = "User FS", ++ .offset = 16 * 0x00100000, ++ .size = 112 * 0x00100000 }, ++}; ++#define NUM_PARTITIONS16K 2 ++#define NUM_PARTITIONS32K 2 ++#define NUM_PARTITIONS64K 2 ++#define NUM_PARTITIONS128K 2 ++ ++/* ++ * hardware specific access to control-lines ++*/ ++static void tx4925ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ ++ switch(cmd){ ++ ++ case NAND_CTL_SETCLE: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CLE; ++ break; ++ case NAND_CTL_SETALE: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ALE; ++ break; ++ case NAND_CTL_SETNCE: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_CE; ++ break; ++ case NAND_CTL_CLRNCE: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_CE; ++ break; ++ case NAND_CTL_SETWP: ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_WE; ++ break; ++ case NAND_CTL_CLRWP: ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_WE; ++ break; ++ } ++} ++ ++/* ++* read device ready pin ++*/ ++static int tx4925ndfmc_device_ready(struct mtd_info *mtd) ++{ ++ int ready; ++ ready = (tx4925_ndfmcptr->sr & TX4925_NDSFR_BUSY) ? 0 : 1; ++ return ready; ++} ++void tx4925ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ /* reset first */ ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_MASK; ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_ENAB; ++} ++static void tx4925ndfmc_disable_ecc(void) ++{ ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; ++} ++static void tx4925ndfmc_enable_read_ecc(void) ++{ ++ tx4925_ndfmcptr->mcr &= ~TX4925_NDFMCR_ECC_CNTL_MASK; ++ tx4925_ndfmcptr->mcr |= TX4925_NDFMCR_ECC_CNTL_READ; ++} ++void tx4925ndfmc_readecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code){ ++ int i; ++ u_char *ecc = ecc_code; ++ tx4925ndfmc_enable_read_ecc(); ++ for (i = 0;i < 6;i++,ecc++) ++ *ecc = tx4925_read_nfmc(&(tx4925_ndfmcptr->dtr)); ++ tx4925ndfmc_disable_ecc(); ++} ++void tx4925ndfmc_device_setup(void) ++{ ++ ++ *(unsigned char *)0xbb005000 &= ~0x08; ++ ++ /* reset NDFMC */ ++ tx4925_ndfmcptr->rstr |= TX4925_NDFRSTR_RST; ++ while (tx4925_ndfmcptr->rstr & TX4925_NDFRSTR_RST); ++ ++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */ ++ tx4925_ndfmcptr->mcr = TX4925_BSPRT ? TX4925_NDFMCR_BSPRT : 0; ++ tx4925_ndfmcptr->spr = TX4925_HOLD << 4 | TX4925_SPW; ++} ++static u_char tx4925ndfmc_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return tx4925_read_nfmc(this->IO_ADDR_R); ++} ++ ++static void tx4925ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ tx4925_write_nfmc(byte, this->IO_ADDR_W); ++} ++ ++static void tx4925ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; i<len; i++) ++ tx4925_write_nfmc(buf[i], this->IO_ADDR_W); ++} ++ ++static void tx4925ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; i<len; i++) ++ buf[i] = tx4925_read_nfmc(this->IO_ADDR_R); ++} ++ ++static int tx4925ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; i<len; i++) ++ if (buf[i] != tx4925_read_nfmc(this->IO_ADDR_R)) ++ return i; ++ ++ return 0; ++} ++ ++/* ++ * Send command to NAND device ++ */ ++static void tx4925ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ register struct nand_chip *this = mtd->priv; ++ ++ /* Begin command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ this->write_byte(mtd, readcmd); ++ } ++ this->write_byte(mtd, command); ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ ++ if (column != -1 || page_addr != -1) { ++ this->hwcontrol(mtd, NAND_CTL_SETALE); ++ ++ /* Serially input address */ ++ if (column != -1) ++ this->write_byte(mtd, column); ++ if (page_addr != -1) { ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); ++ /* One more address cycle for higher density devices */ ++ if (mtd->size & 0x0c000000) ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); ++ } ++ /* Latch in address */ ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ /* Turn off WE */ ++ this->hwcontrol (mtd, NAND_CTL_CLRWP); ++ return; ++ ++ case NAND_CMD_SEQIN: ++ /* Turn on WE */ ++ this->hwcontrol (mtd, NAND_CTL_SETWP); ++ return; ++ ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & 0x40)); ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ ++ /* wait until command is processed */ ++ while (!this->dev_ready(mtd)); ++} ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partitio ++n **pparts, char *); ++#endif ++ ++/* ++ * Main initialization routine ++ */ ++extern int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); ++int __init tx4925ndfmc_init (void) ++{ ++ struct nand_chip *this; ++ int err = 0; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ tx4925ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!tx4925ndfmc_mtd) { ++ printk ("Unable to allocate RBTX4925 NAND MTD device structure.\n"); ++ err = -ENOMEM; ++ goto out; ++ } ++ ++ tx4925ndfmc_device_setup(); ++ ++ /* io is indirect via a register so don't need to ioremap address */ ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&tx4925ndfmc_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) tx4925ndfmc_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ tx4925ndfmc_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = (unsigned long)&(tx4925_ndfmcptr->dtr); ++ this->IO_ADDR_W = (unsigned long)&(tx4925_ndfmcptr->dtr); ++ this->hwcontrol = tx4925ndfmc_hwcontrol; ++ this->enable_hwecc = tx4925ndfmc_enable_hwecc; ++ this->calculate_ecc = tx4925ndfmc_readecc; ++ this->correct_data = nand_correct_data; ++ this->eccmode = NAND_ECC_HW6_512; ++ this->dev_ready = tx4925ndfmc_device_ready; ++ /* 20 us command delay time */ ++ this->chip_delay = 20; ++ this->read_byte = tx4925ndfmc_nand_read_byte; ++ this->write_byte = tx4925ndfmc_nand_write_byte; ++ this->cmdfunc = tx4925ndfmc_nand_command; ++ this->write_buf = tx4925ndfmc_nand_write_buf; ++ this->read_buf = tx4925ndfmc_nand_read_buf; ++ this->verify_buf = tx4925ndfmc_nand_verify_buf; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (tx4925ndfmc_mtd, 1)) { ++ err = -ENXIO; ++ goto out_ior; ++ } ++ ++ /* Allocate memory for internal data buffer */ ++ this->data_buf = kmalloc (sizeof(u_char) * (tx4925ndfmc_mtd->oobblock + tx4925ndfmc_mtd->oobsize), GFP_KERNEL); ++ if (!this->data_buf) { ++ printk ("Unable to allocate NAND data buffer for RBTX4925.\n"); ++ err = -ENOMEM; ++ goto out_ior; ++ } ++ ++ /* Register the partitions */ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ { ++ int mtd_parts_nb = 0; ++ struct mtd_partition *mtd_parts = 0; ++ mtd_parts_nb = parse_cmdline_partitions(tx4925ndfmc_mtd, &mtd_parts, "tx4925ndfmc"); ++ if (mtd_parts_nb > 0) ++ add_mtd_partitions(tx4925ndfmc_mtd, mtd_parts, mtd_parts_nb); ++ else ++ add_mtd_device(tx4925ndfmc_mtd); ++ } ++#else /* ifdef CONFIG_MTD_CMDLINE_PARTS */ ++ switch(tx4925ndfmc_mtd->size){ ++ case 0x01000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info16k, NUM_PARTITIONS16K); break; ++ case 0x02000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info32k, NUM_PARTITIONS32K); break; ++ case 0x04000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info64k, NUM_PARTITIONS64K); break; ++ case 0x08000000: add_mtd_partitions(tx4925ndfmc_mtd, partition_info128k, NUM_PARTITIONS128K); break; ++ default: { ++ printk ("Unsupported SmartMedia device\n"); ++ err = -ENXIO; ++ goto out_buf; ++ } ++ } ++#endif /* ifdef CONFIG_MTD_CMDLINE_PARTS */ ++ goto out; ++ ++out_buf: ++ kfree (this->data_buf); ++out_ior: ++out: ++ return err; ++} ++ ++module_init(tx4925ndfmc_init); ++ ++/* ++ * Clean up routine ++ */ ++#ifdef MODULE ++static void __exit tx4925ndfmc_cleanup (void) ++{ ++ struct nand_chip *this = (struct nand_chip *) &tx4925ndfmc_mtd[1]; ++ ++ /* Unregister partitions */ ++ del_mtd_partitions(tx4925ndfmc_mtd); ++ ++ /* Unregister the device */ ++ del_mtd_device (tx4925ndfmc_mtd); ++ ++ /* Free internal data buffers */ ++ kfree (this->data_buf); ++ ++ /* Free the MTD device structure */ ++ kfree (tx4925ndfmc_mtd); ++} ++module_exit(tx4925ndfmc_cleanup); ++#endif ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>"); ++MODULE_DESCRIPTION("Glue layer for SmartMediaCard on Toshiba RBTX4925"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nand/tx4938ndfmc.c linux/drivers/mtd/nand/tx4938ndfmc.c +--- linux-mips-2.4.27/drivers/mtd/nand/tx4938ndfmc.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/drivers/mtd/nand/tx4938ndfmc.c 2004-11-19 10:25:12.019181552 +0100 +@@ -0,0 +1,422 @@ ++/* ++ * drivers/mtd/nand/tx4938ndfmc.c ++ * ++ * Overview: ++ * This is a device driver for the NAND flash device connected to ++ * TX4938 internal NAND Memory Controller. ++ * TX4938 NDFMC is almost same as TX4925 NDFMC, but register size are 64 bit. ++ * ++ * Author: source@mvista.com ++ * ++ * Based on spia.c by Steven J. Hill ++ * ++ * $Id: tx4938ndfmc.c,v 1.1 2003/11/04 22:59:11 ahennessy Exp $ ++ * ++ * Copyright (C) 2000-2001 Toshiba Corporation ++ * ++ * 2003 (c) MontaVista Software, Inc. This file is licensed under the ++ * terms of the GNU General Public License version 2. This program is ++ * licensed "as is" without any warranty of any kind, whether express ++ * or implied. ++ */ ++#include <linux/config.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/module.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nand.h> ++#include <linux/mtd/nand_ecc.h> ++#include <linux/mtd/partitions.h> ++#include <asm/io.h> ++#include <asm/bootinfo.h> ++#include <linux/delay.h> ++#include <asm/tx4938/rbtx4938.h> ++ ++extern struct nand_oobinfo jffs2_oobinfo; ++ ++/* ++ * MTD structure for TX4938 NDFMC ++ */ ++static struct mtd_info *tx4938ndfmc_mtd; ++ ++/* ++ * Define partitions for flash device ++ */ ++#define flush_wb() (void)tx4938_ndfmcptr->mcr; ++ ++#define NUM_PARTITIONS 3 ++#define NUMBER_OF_CIS_BLOCKS 24 ++#define SIZE_OF_BLOCK 0x00004000 ++#define NUMBER_OF_BLOCK_PER_ZONE 1024 ++#define SIZE_OF_ZONE (NUMBER_OF_BLOCK_PER_ZONE * SIZE_OF_BLOCK) ++#ifndef CONFIG_MTD_CMDLINE_PARTS ++/* ++ * You can use the following sample of MTD partitions ++ * on the NAND Flash Memory 32MB or more. ++ * ++ * The following figure shows the image of the sample partition on ++ * the 32MB NAND Flash Memory. ++ * ++ * Block No. ++ * 0 +-----------------------------+ ------ ++ * | CIS | ^ ++ * 24 +-----------------------------+ | ++ * | kernel image | | Zone 0 ++ * | | | ++ * +-----------------------------+ | ++ * 1023 | unused area | v ++ * +-----------------------------+ ------ ++ * 1024 | JFFS2 | ^ ++ * | | | ++ * | | | Zone 1 ++ * | | | ++ * | | | ++ * | | v ++ * 2047 +-----------------------------+ ------ ++ * ++ */ ++static struct mtd_partition partition_info[NUM_PARTITIONS] = { ++ { ++ .name = "RBTX4938 CIS Area", ++ .offset = 0, ++ .size = (NUMBER_OF_CIS_BLOCKS * SIZE_OF_BLOCK), ++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ ++ }, ++ { ++ .name = "RBTX4938 kernel image", ++ .offset = MTDPART_OFS_APPEND, ++ .size = 8 * 0x00100000, /* 8MB (Depends on size of kernel image) */ ++ .mask_flags = MTD_WRITEABLE /* This partition is NOT writable */ ++ }, ++ { ++ .name = "Root FS (JFFS2)", ++ .offset = (0 + SIZE_OF_ZONE), /* start address of next zone */ ++ .size = MTDPART_SIZ_FULL ++ }, ++}; ++#endif ++ ++static void tx4938ndfmc_hwcontrol(struct mtd_info *mtd, int cmd) ++{ ++ switch (cmd) { ++ case NAND_CTL_SETCLE: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CLE; ++ break; ++ case NAND_CTL_CLRCLE: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CLE; ++ break; ++ case NAND_CTL_SETALE: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_ALE; ++ break; ++ case NAND_CTL_CLRALE: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_ALE; ++ break; ++ /* TX4938_NDFMCR_CE bit is 0:high 1:low */ ++ case NAND_CTL_SETNCE: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_CE; ++ break; ++ case NAND_CTL_CLRNCE: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_CE; ++ break; ++ case NAND_CTL_SETWP: ++ tx4938_ndfmcptr->mcr |= TX4938_NDFMCR_WE; ++ break; ++ case NAND_CTL_CLRWP: ++ tx4938_ndfmcptr->mcr &= ~TX4938_NDFMCR_WE; ++ break; ++ } ++} ++static int tx4938ndfmc_dev_ready(struct mtd_info *mtd) ++{ ++ flush_wb(); ++ return !(tx4938_ndfmcptr->sr & TX4938_NDFSR_BUSY); ++} ++static void tx4938ndfmc_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code) ++{ ++ u32 mcr = tx4938_ndfmcptr->mcr; ++ mcr &= ~TX4938_NDFMCR_ECC_ALL; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_READ; ++ ecc_code[1] = tx4938_ndfmcptr->dtr; ++ ecc_code[0] = tx4938_ndfmcptr->dtr; ++ ecc_code[2] = tx4938_ndfmcptr->dtr; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; ++} ++static void tx4938ndfmc_enable_hwecc(struct mtd_info *mtd, int mode) ++{ ++ u32 mcr = tx4938_ndfmcptr->mcr; ++ mcr &= ~TX4938_NDFMCR_ECC_ALL; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_RESET; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_OFF; ++ tx4938_ndfmcptr->mcr = mcr | TX4938_NDFMCR_ECC_ON; ++} ++ ++static u_char tx4938ndfmc_nand_read_byte(struct mtd_info *mtd) ++{ ++ struct nand_chip *this = mtd->priv; ++ return tx4938_read_nfmc(this->IO_ADDR_R); ++} ++ ++static void tx4938ndfmc_nand_write_byte(struct mtd_info *mtd, u_char byte) ++{ ++ struct nand_chip *this = mtd->priv; ++ tx4938_write_nfmc(byte, this->IO_ADDR_W); ++} ++ ++static void tx4938ndfmc_nand_write_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; i<len; i++) ++ tx4938_write_nfmc(buf[i], this->IO_ADDR_W); ++} ++ ++static void tx4938ndfmc_nand_read_buf(struct mtd_info *mtd, u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; i<len; i++) ++ buf[i] = tx4938_read_nfmc(this->IO_ADDR_R); ++} ++ ++static int tx4938ndfmc_nand_verify_buf(struct mtd_info *mtd, const u_char *buf, int len) ++{ ++ int i; ++ struct nand_chip *this = mtd->priv; ++ ++ for (i=0; i<len; i++) ++ if (buf[i] != tx4938_read_nfmc(this->IO_ADDR_R)) ++ return i; ++ ++ return 0; ++} ++ ++/* ++ * Send command to NAND device ++ */ ++static void tx4938ndfmc_nand_command (struct mtd_info *mtd, unsigned command, int column, int page_addr) ++{ ++ register struct nand_chip *this = mtd->priv; ++ ++ /* Begin command latch cycle */ ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ /* ++ * Write out the command to the device. ++ */ ++ if (command == NAND_CMD_SEQIN) { ++ int readcmd; ++ ++ if (column >= mtd->oobblock) { ++ /* OOB area */ ++ column -= mtd->oobblock; ++ readcmd = NAND_CMD_READOOB; ++ } else if (column < 256) { ++ /* First 256 bytes --> READ0 */ ++ readcmd = NAND_CMD_READ0; ++ } else { ++ column -= 256; ++ readcmd = NAND_CMD_READ1; ++ } ++ this->write_byte(mtd, readcmd); ++ } ++ this->write_byte(mtd, command); ++ ++ /* Set ALE and clear CLE to start address cycle */ ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ ++ if (column != -1 || page_addr != -1) { ++ this->hwcontrol(mtd, NAND_CTL_SETALE); ++ ++ /* Serially input address */ ++ if (column != -1) ++ this->write_byte(mtd, column); ++ if (page_addr != -1) { ++ this->write_byte(mtd, (unsigned char) (page_addr & 0xff)); ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 8) & 0xff)); ++ /* One more address cycle for higher density devices */ ++ if (mtd->size & 0x0c000000) ++ this->write_byte(mtd, (unsigned char) ((page_addr >> 16) & 0x0f)); ++ } ++ /* Latch in address */ ++ this->hwcontrol(mtd, NAND_CTL_CLRALE); ++ } ++ ++ /* ++ * program and erase have their own busy handlers ++ * status and sequential in needs no delay ++ */ ++ switch (command) { ++ ++ case NAND_CMD_PAGEPROG: ++ /* Turn off WE */ ++ this->hwcontrol (mtd, NAND_CTL_CLRWP); ++ return; ++ ++ case NAND_CMD_SEQIN: ++ /* Turn on WE */ ++ this->hwcontrol (mtd, NAND_CTL_SETWP); ++ return; ++ ++ case NAND_CMD_ERASE1: ++ case NAND_CMD_ERASE2: ++ case NAND_CMD_STATUS: ++ return; ++ ++ case NAND_CMD_RESET: ++ if (this->dev_ready) ++ break; ++ this->hwcontrol(mtd, NAND_CTL_SETCLE); ++ this->write_byte(mtd, NAND_CMD_STATUS); ++ this->hwcontrol(mtd, NAND_CTL_CLRCLE); ++ while ( !(this->read_byte(mtd) & 0x40)); ++ return; ++ ++ /* This applies to read commands */ ++ default: ++ /* ++ * If we don't have access to the busy pin, we apply the given ++ * command delay ++ */ ++ if (!this->dev_ready) { ++ udelay (this->chip_delay); ++ return; ++ } ++ } ++ ++ /* wait until command is processed */ ++ while (!this->dev_ready(mtd)); ++} ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++extern int parse_cmdline_partitions(struct mtd_info *master, struct mtd_partition **pparts, char *); ++#endif ++/* ++ * Main initialization routine ++ */ ++int __init tx4938ndfmc_init (void) ++{ ++ struct nand_chip *this; ++ int bsprt = 0, hold = 0xf, spw = 0xf; ++ int protected = 0; ++ ++ if ((*rbtx4938_piosel_ptr & 0x0c) != 0x08) { ++ printk("TX4938 NDFMC: disabled by IOC PIOSEL\n"); ++ return -ENODEV; ++ } ++ bsprt = 1; ++ hold = 2; ++ spw = 9 - 1; /* 8 GBUSCLK = 80ns (@ GBUSCLK 100MHz) */ ++ ++ if ((tx4938_ccfgptr->pcfg & ++ (TX4938_PCFG_ATA_SEL|TX4938_PCFG_ISA_SEL|TX4938_PCFG_NDF_SEL)) ++ != TX4938_PCFG_NDF_SEL) { ++ printk("TX4938 NDFMC: disabled by PCFG.\n"); ++ return -ENODEV; ++ } ++ ++ /* reset NDFMC */ ++ tx4938_ndfmcptr->rstr |= TX4938_NDFRSTR_RST; ++ while (tx4938_ndfmcptr->rstr & TX4938_NDFRSTR_RST) ++ ; ++ /* setup BusSeparete, Hold Time, Strobe Pulse Width */ ++ tx4938_ndfmcptr->mcr = bsprt ? TX4938_NDFMCR_BSPRT : 0; ++ tx4938_ndfmcptr->spr = hold << 4 | spw; ++ ++ /* Allocate memory for MTD device structure and private data */ ++ tx4938ndfmc_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), ++ GFP_KERNEL); ++ if (!tx4938ndfmc_mtd) { ++ printk ("Unable to allocate TX4938 NDFMC MTD device structure.\n"); ++ return -ENOMEM; ++ } ++ ++ /* Get pointer to private data */ ++ this = (struct nand_chip *) (&tx4938ndfmc_mtd[1]); ++ ++ /* Initialize structures */ ++ memset((char *) tx4938ndfmc_mtd, 0, sizeof(struct mtd_info)); ++ memset((char *) this, 0, sizeof(struct nand_chip)); ++ ++ /* Link the private data with the MTD structure */ ++ tx4938ndfmc_mtd->priv = this; ++ ++ /* Set address of NAND IO lines */ ++ this->IO_ADDR_R = (unsigned long)&tx4938_ndfmcptr->dtr; ++ this->IO_ADDR_W = (unsigned long)&tx4938_ndfmcptr->dtr; ++ this->hwcontrol = tx4938ndfmc_hwcontrol; ++ this->dev_ready = tx4938ndfmc_dev_ready; ++ this->calculate_ecc = tx4938ndfmc_calculate_ecc; ++ this->correct_data = nand_correct_data; ++ this->enable_hwecc = tx4938ndfmc_enable_hwecc; ++ this->eccmode = NAND_ECC_HW3_256; ++ this->chip_delay = 100; ++ this->read_byte = tx4938ndfmc_nand_read_byte; ++ this->write_byte = tx4938ndfmc_nand_write_byte; ++ this->cmdfunc = tx4938ndfmc_nand_command; ++ this->write_buf = tx4938ndfmc_nand_write_buf; ++ this->read_buf = tx4938ndfmc_nand_read_buf; ++ this->verify_buf = tx4938ndfmc_nand_verify_buf; ++ ++ /* Scan to find existance of the device */ ++ if (nand_scan (tx4938ndfmc_mtd, 1)) { ++ kfree (tx4938ndfmc_mtd); ++ return -ENXIO; ++ } ++ ++ /* Allocate memory for internal data buffer */ ++ this->data_buf = kmalloc (sizeof(u_char) * (tx4938ndfmc_mtd->oobblock + tx4938ndfmc_mtd->oobsize), GFP_KERNEL); ++ if (!this->data_buf) { ++ printk ("Unable to allocate NAND data buffer for TX4938.\n"); ++ kfree (tx4938ndfmc_mtd); ++ return -ENOMEM; ++ } ++ ++ if (protected) { ++ printk(KERN_INFO "TX4938 NDFMC: write protected.\n"); ++ tx4938ndfmc_mtd->flags &= ~(MTD_WRITEABLE | MTD_ERASEABLE); ++ } ++ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ { ++ int mtd_parts_nb = 0; ++ struct mtd_partition *mtd_parts = 0; ++ mtd_parts_nb = parse_cmdline_partitions(tx4938ndfmc_mtd, &mtd_parts, "tx4938ndfmc"); ++ if (mtd_parts_nb > 0) ++ add_mtd_partitions(tx4938ndfmc_mtd, mtd_parts, mtd_parts_nb); ++ else ++ add_mtd_device(tx4938ndfmc_mtd); ++ } ++#else ++ add_mtd_partitions(tx4938ndfmc_mtd, partition_info, NUM_PARTITIONS ); ++#endif ++ ++ return 0; ++} ++module_init(tx4938ndfmc_init); ++ ++/* ++ * Clean up routine ++ */ ++static void __exit tx4938ndfmc_cleanup (void) ++{ ++ struct nand_chip *this = (struct nand_chip *) tx4938ndfmc_mtd->priv; ++ ++ /* Unregister the device */ ++#ifdef CONFIG_MTD_CMDLINE_PARTS ++ del_mtd_partitions(tx4938ndfmc_mtd); ++#endif ++ del_mtd_device (tx4938ndfmc_mtd); ++ ++ /* Free the MTD device structure */ ++ kfree (tx4938ndfmc_mtd); ++ ++ /* Free internal data buffer */ ++ kfree (this->data_buf); ++} ++module_exit(tx4938ndfmc_cleanup); ++ ++MODULE_LICENSE("GPL"); ++MODULE_AUTHOR("Alice Hennessy <ahennessy@mvista.com>"); ++MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on TX4938 NDFMC"); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nftlcore.c linux/drivers/mtd/nftlcore.c +--- linux-mips-2.4.27/drivers/mtd/nftlcore.c 2003-02-26 01:53:49.000000000 +0100 ++++ linux/drivers/mtd/nftlcore.c 2004-11-19 10:25:11.653237184 +0100 +@@ -1,7 +1,7 @@ + /* Linux driver for NAND Flash Translation Layer */ + /* (c) 1999 Machine Vision Holdings, Inc. */ + /* Author: David Woodhouse <dwmw2@infradead.org> */ +-/* $Id: nftlcore.c,v 1.87 2002/09/13 14:35:33 dwmw2 Exp $ */ ++/* $Id: nftlcore.c,v 1.94 2003/06/23 12:00:08 dwmw2 Exp $ */ + + /* + The contents of this file are distributed under the GNU General +@@ -23,15 +23,13 @@ + #include <linux/slab.h> + #include <linux/sched.h> + #include <linux/init.h> +-#include <linux/blkpg.h> ++#include <linux/hdreg.h> + +-#ifdef CONFIG_KMOD + #include <linux/kmod.h> +-#endif + #include <linux/mtd/mtd.h> + #include <linux/mtd/nand.h> + #include <linux/mtd/nftl.h> +-#include <linux/mtd/compatmac.h> ++#include <linux/mtd/blktrans.h> + + /* maximum number of loops while examining next block, to have a + chance to detect consistency problems (they should never happen +@@ -39,187 +37,95 @@ + + #define MAX_LOOPS 10000 + +-/* NFTL block device stuff */ +-#define MAJOR_NR NFTL_MAJOR +-#define DEVICE_REQUEST nftl_request +-#define DEVICE_OFF(device) +- +- +-#include <linux/blk.h> +-#include <linux/hdreg.h> +- +-/* Linux-specific block device functions */ +- +-/* I _HATE_ the Linux block device setup more than anything else I've ever +- * encountered, except ... +- */ +- +-static int nftl_sizes[256]; +-static int nftl_blocksizes[256]; +- +-/* .. for the Linux partition table handling. */ +-struct hd_struct part_table[256]; +- +-#if LINUX_VERSION_CODE < 0x20328 +-static void dummy_init (struct gendisk *crap) +-{} +-#endif +- +-static struct gendisk nftl_gendisk = { +- major: MAJOR_NR, +- major_name: "nftl", +- minor_shift: NFTL_PARTN_BITS, /* Bits to shift to get real from partition */ +- max_p: (1<<NFTL_PARTN_BITS)-1, /* Number of partitions per real */ +-#if LINUX_VERSION_CODE < 0x20328 +- max_nr: MAX_NFTLS, /* maximum number of real */ +- init: dummy_init, /* init function */ +-#endif +- part: part_table, /* hd struct */ +- sizes: nftl_sizes, /* block sizes */ +-}; +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif +- +-struct NFTLrecord *NFTLs[MAX_NFTLS]; + +-static void NFTL_setup(struct mtd_info *mtd) ++static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) + { +- int i; + struct NFTLrecord *nftl; + unsigned long temp; +- int firstfree = -1; +- +- DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n"); + +- for (i = 0; i < MAX_NFTLS; i++) { +- if (!NFTLs[i] && firstfree == -1) +- firstfree = i; +- else if (NFTLs[i] && NFTLs[i]->mtd == mtd) { +- /* This is a Spare Media Header for an NFTL we've already found */ +- DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n"); ++ if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) + return; +- } +- } +- if (firstfree == -1) { +- printk(KERN_WARNING "No more NFTL slot available\n"); +- return; +- } ++ ++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); + + nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); ++ + if (!nftl) { +- printk(KERN_WARNING "Out of memory for NFTL data structures\n"); ++ printk(KERN_WARNING "NFTL: out of memory for data structures\n"); + return; + } ++ memset(nftl, 0, sizeof(*nftl)); + +- init_MUTEX(&nftl->mutex); +- +- nftl->mtd = mtd; ++ nftl->mbd.mtd = mtd; ++ nftl->mbd.devnum = -1; ++ nftl->mbd.blksize = 512; ++ nftl->mbd.tr = tr; + + if (NFTL_mount(nftl) < 0) { +- printk(KERN_WARNING "Could not mount NFTL device\n"); ++ printk(KERN_WARNING "NFTL: could not mount device\n"); + kfree(nftl); + return; + } + + /* OK, it's a new one. Set up all the data structures. */ +-#ifdef PSYCHO_DEBUG +- printk("Found new NFTL nftl%c\n", firstfree + 'a'); +-#endif + +- /* linux stuff */ +- nftl->usecount = 0; ++ /* Calculate geometry */ + nftl->cylinders = 1024; + nftl->heads = 16; + + temp = nftl->cylinders * nftl->heads; +- nftl->sectors = nftl->nr_sects / temp; +- if (nftl->nr_sects % temp) { ++ nftl->sectors = nftl->mbd.size / temp; ++ if (nftl->mbd.size % temp) { + nftl->sectors++; + temp = nftl->cylinders * nftl->sectors; +- nftl->heads = nftl->nr_sects / temp; ++ nftl->heads = nftl->mbd.size / temp; + +- if (nftl->nr_sects % temp) { ++ if (nftl->mbd.size % temp) { + nftl->heads++; + temp = nftl->heads * nftl->sectors; +- nftl->cylinders = nftl->nr_sects / temp; ++ nftl->cylinders = nftl->mbd.size / temp; + } + } + +- if (nftl->nr_sects != nftl->heads * nftl->cylinders * nftl->sectors) { +- printk(KERN_WARNING "Cannot calculate an NFTL geometry to " +- "match size of 0x%x.\n", nftl->nr_sects); +- printk(KERN_WARNING "Using C:%d H:%d S:%d (== 0x%lx sects)\n", ++ if (nftl->mbd.size != nftl->heads * nftl->cylinders * nftl->sectors) { ++ /* ++ Oh no we don't have ++ mbd.size == heads * cylinders * sectors ++ */ ++ printk(KERN_WARNING "NFTL: cannot calculate a geometry to " ++ "match size of 0x%lx.\n", nftl->mbd.size); ++ printk(KERN_WARNING "NFTL: using C:%d H:%d S:%d " ++ "(== 0x%lx sects)\n", + nftl->cylinders, nftl->heads , nftl->sectors, +- (long)nftl->cylinders * (long)nftl->heads * (long)nftl->sectors ); +- +- /* Oh no we don't have nftl->nr_sects = nftl->heads * nftl->cylinders * nftl->sectors; */ ++ (long)nftl->cylinders * (long)nftl->heads * ++ (long)nftl->sectors ); + } +- NFTLs[firstfree] = nftl; +- /* Finally, set up the block device sizes */ +- nftl_sizes[firstfree * 16] = nftl->nr_sects; +- //nftl_blocksizes[firstfree*16] = 512; +- part_table[firstfree * 16].nr_sects = nftl->nr_sects; +- +- nftl_gendisk.nr_real++; +- +- /* partition check ... */ +-#if LINUX_VERSION_CODE < 0x20328 +- resetup_one_dev(&nftl_gendisk, firstfree); +-#else +- grok_partitions(&nftl_gendisk, firstfree, 1<<NFTL_PARTN_BITS, nftl->nr_sects); +-#endif +-} +- +-static void NFTL_unsetup(int i) +-{ +- struct NFTLrecord *nftl = NFTLs[i]; +- +- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i); +- +- NFTLs[i] = NULL; + ++ if (add_mtd_blktrans_dev(&nftl->mbd)) { + if (nftl->ReplUnitTable) + kfree(nftl->ReplUnitTable); + if (nftl->EUNtable) + kfree(nftl->EUNtable); +- +- nftl_gendisk.nr_real--; + kfree(nftl); +-} +- +-/* Search the MTD device for NFTL partitions */ +-static void NFTL_notify_add(struct mtd_info *mtd) +-{ +- DEBUG(MTD_DEBUG_LEVEL1, "NFTL_notify_add for %s\n", mtd->name); +- +- if (mtd) { +- if (!mtd->read_oob) { +- /* If this MTD doesn't have out-of-band data, +- then there's no point continuing */ +- DEBUG(MTD_DEBUG_LEVEL1, "No OOB data, quitting\n"); + return; + } +- DEBUG(MTD_DEBUG_LEVEL3, "mtd->read = %p, size = %d, erasesize = %d\n", +- mtd->read, mtd->size, mtd->erasesize); +- +- NFTL_setup(mtd); +- } ++#ifdef PSYCHO_DEBUG ++ printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); ++#endif + } + +-static void NFTL_notify_remove(struct mtd_info *mtd) ++static void nftl_remove_dev(struct mtd_blktrans_dev *dev) + { +- int i; ++ struct NFTLrecord *nftl = (void *)dev; + +- for (i = 0; i < MAX_NFTLS; i++) { +- if (NFTLs[i] && NFTLs[i]->mtd == mtd) +- NFTL_unsetup(i); +- } ++ DEBUG(MTD_DEBUG_LEVEL1, "NFTL: remove_dev (i=%d)\n", dev->devnum); ++ ++ del_mtd_blktrans_dev(dev); ++ if (nftl->ReplUnitTable) ++ kfree(nftl->ReplUnitTable); ++ if (nftl->EUNtable) ++ kfree(nftl->EUNtable); ++ kfree(nftl); + } + + #ifdef CONFIG_NFTL_RW +@@ -303,7 +209,7 @@ + + targetEUN = thisEUN; + for (block = 0; block < nftl->EraseSize / 512; block ++) { +- MTD_READOOB(nftl->mtd, ++ MTD_READOOB(nftl->mbd.mtd, + (thisEUN * nftl->EraseSize) + (block * 512), + 16 , &retlen, (char *)&oob); + if (block == 2) { +@@ -420,7 +326,7 @@ + chain by selecting the longer one */ + oob.u.c.FoldMark = oob.u.c.FoldMark1 = cpu_to_le16(FOLD_MARK_IN_PROGRESS); + oob.u.c.unused = 0xffffffff; +- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 2 * 512 + 8, + 8, &retlen, (char *)&oob.u); + } + +@@ -444,16 +350,16 @@ + if (BlockMap[block] == BLOCK_NIL) + continue; + +- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), ++ ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + (block * 512), + 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + if (ret < 0) { +- ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) ++ ret = MTD_READECC(nftl->mbd.mtd, (nftl->EraseSize * BlockMap[block]) + + (block * 512), 512, &retlen, + movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + if (ret != -EIO) + printk("Error went away on retry.\n"); + } +- MTD_WRITEECC(nftl->mtd, (nftl->EraseSize * targetEUN) + (block * 512), ++ MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512), + 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); + } + +@@ -462,7 +368,7 @@ + = cpu_to_le16(thisVUC); + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum = 0xffff; + +- MTD_WRITEOOB(nftl->mtd, (nftl->EraseSize * targetEUN) + 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + 8, + 8, &retlen, (char *)&oob.u); + + /* OK. We've moved the whole lot into the new block. Now we have to free the original blocks. */ +@@ -582,7 +488,7 @@ + + lastEUN = writeEUN; + +- MTD_READOOB(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, ++ MTD_READOOB(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, + 8, &retlen, (char *)&bci); + + DEBUG(MTD_DEBUG_LEVEL2, "Status of block %d in EUN %d is %x\n", +@@ -670,12 +576,12 @@ + nftl->ReplUnitTable[writeEUN] = BLOCK_NIL; + + /* ... and on the flash itself */ +- MTD_READOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8, ++ MTD_READOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); + + oob.u.a.VirtUnitNum = oob.u.a.SpareVirtUnitNum = cpu_to_le16(thisVUC); + +- MTD_WRITEOOB(nftl->mtd, writeEUN * nftl->EraseSize + 8, 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, writeEUN * nftl->EraseSize + 8, 8, + &retlen, (char *)&oob.u); + + /* we link the new block to the chain only after the +@@ -685,13 +591,13 @@ + /* Both in our cache... */ + nftl->ReplUnitTable[lastEUN] = writeEUN; + /* ... and on the flash itself */ +- MTD_READOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8, ++ MTD_READOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); + + oob.u.a.ReplUnitNum = oob.u.a.SpareReplUnitNum + = cpu_to_le16(writeEUN); + +- MTD_WRITEOOB(nftl->mtd, (lastEUN * nftl->EraseSize) + 8, ++ MTD_WRITEOOB(nftl->mbd.mtd, (lastEUN * nftl->EraseSize) + 8, + 8, &retlen, (char *)&oob.u); + } + +@@ -704,8 +610,10 @@ + return 0xffff; + } + +-static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer) ++static int nftl_writeblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) + { ++ struct NFTLrecord *nftl = (void *)mbd; + u16 writeEUN; + unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); + size_t retlen; +@@ -720,7 +628,7 @@ + return 1; + } + +- MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, ++ MTD_WRITEECC(nftl->mbd.mtd, (writeEUN * nftl->EraseSize) + blockofs, + 512, &retlen, (char *)buffer, (char *)eccbuf, NAND_ECC_DISKONCHIP); + /* no need to write SECTOR_USED flags since they are written in mtd_writeecc */ + +@@ -728,8 +636,10 @@ + } + #endif /* CONFIG_NFTL_RW */ + +-static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer) ++static int nftl_readblock(struct mtd_blktrans_dev *mbd, unsigned long block, ++ char *buffer) + { ++ struct NFTLrecord *nftl = (void *)mbd; + u16 lastgoodEUN; + u16 thisEUN = nftl->EUNtable[block / (nftl->EraseSize / 512)]; + unsigned long blockofs = (block * 512) & (nftl->EraseSize - 1); +@@ -742,7 +652,7 @@ + + if (thisEUN != BLOCK_NIL) { + while (thisEUN < nftl->nb_blocks) { +- if (MTD_READOOB(nftl->mtd, (thisEUN * nftl->EraseSize) + blockofs, ++ if (MTD_READOOB(nftl->mbd.mtd, (thisEUN * nftl->EraseSize) + blockofs, + 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else +@@ -761,13 +671,13 @@ + case SECTOR_IGNORE: + break; + default: +- printk("Unknown status for block %d in EUN %d: %x\n", ++ printk("Unknown status for block %ld in EUN %d: %x\n", + block, thisEUN, status); + break; + } + + if (!silly--) { +- printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%x\n", ++ printk(KERN_WARNING "Infinite loop in Virtual Unit Chain 0x%lx\n", + block / (nftl->EraseSize / 512)); + return 1; + } +@@ -783,264 +693,22 @@ + loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; + size_t retlen; + u_char eccbuf[6]; +- if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP)) ++ if (MTD_READECC(nftl->mbd.mtd, ptr, 512, &retlen, buffer, eccbuf, NAND_ECC_DISKONCHIP)) + return -EIO; + } + return 0; + } + +-static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg) +-{ +- struct NFTLrecord *nftl; +- int p; +- +- nftl = NFTLs[MINOR(inode->i_rdev) >> NFTL_PARTN_BITS]; +- +- if (!nftl) return -EINVAL; +- +- switch (cmd) { +- case HDIO_GETGEO: { +- struct hd_geometry g; +- +- g.heads = nftl->heads; +- g.sectors = nftl->sectors; +- g.cylinders = nftl->cylinders; +- g.start = part_table[MINOR(inode->i_rdev)].start_sect; +- return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; +- } +- case BLKGETSIZE: /* Return device size */ +- return put_user(part_table[MINOR(inode->i_rdev)].nr_sects, +- (unsigned long *) arg); +- +-#ifdef BLKGETSIZE64 +- case BLKGETSIZE64: +- return put_user((u64)part_table[MINOR(inode->i_rdev)].nr_sects << 9, +- (u64 *)arg); +-#endif +- +- case BLKFLSBUF: +- if (!capable(CAP_SYS_ADMIN)) return -EACCES; +- fsync_dev(inode->i_rdev); +- invalidate_buffers(inode->i_rdev); +- if (nftl->mtd->sync) +- nftl->mtd->sync(nftl->mtd); +- return 0; +- +- case BLKRRPART: +- if (!capable(CAP_SYS_ADMIN)) return -EACCES; +- if (nftl->usecount > 1) return -EBUSY; +- /* +- * We have to flush all buffers and invalidate caches, +- * or we won't be able to re-use the partitions, +- * if there was a change and we don't want to reboot +- */ +- p = (1<<NFTL_PARTN_BITS) - 1; +- while (p-- > 0) { +- kdev_t devp = MKDEV(MAJOR(inode->i_dev), MINOR(inode->i_dev)+p); +- if (part_table[p].nr_sects > 0) +- invalidate_device (devp, 1); +- +- part_table[MINOR(inode->i_dev)+p].start_sect = 0; +- part_table[MINOR(inode->i_dev)+p].nr_sects = 0; +- } +- +-#if LINUX_VERSION_CODE < 0x20328 +- resetup_one_dev(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS); +-#else +- grok_partitions(&nftl_gendisk, MINOR(inode->i_rdev) >> NFTL_PARTN_BITS, +- 1<<NFTL_PARTN_BITS, nftl->nr_sects); +-#endif +- return 0; +- +-#if (LINUX_VERSION_CODE < 0x20303) +- RO_IOCTLS(inode->i_rdev, arg); /* ref. linux/blk.h */ +-#else +- case BLKROSET: +- case BLKROGET: +- case BLKSSZGET: +- return blk_ioctl(inode->i_rdev, cmd, arg); +-#endif +- +- default: +- return -EINVAL; +- } +-} +- +-void nftl_request(RQFUNC_ARG) +-{ +- unsigned int dev, block, nsect; +- struct NFTLrecord *nftl; +- char *buffer; +- struct request *req; +- int res; +- +- while (1) { +- INIT_REQUEST; /* blk.h */ +- req = CURRENT; +- +- /* We can do this because the generic code knows not to +- touch the request at the head of the queue */ +- spin_unlock_irq(&io_request_lock); +- +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n"); +- DEBUG(MTD_DEBUG_LEVEL3, "NFTL %s request, from sector 0x%04lx for 0x%04lx sectors\n", +- (req->cmd == READ) ? "Read " : "Write", +- req->sector, req->current_nr_sectors); +- +- dev = MINOR(req->rq_dev); +- block = req->sector; +- nsect = req->current_nr_sectors; +- buffer = req->buffer; +- res = 1; /* succeed */ +- +- if (dev >= MAX_NFTLS * (1<<NFTL_PARTN_BITS)) { +- /* there is no such partition */ +- printk("nftl: bad minor number: device = %s\n", +- kdevname(req->rq_dev)); +- res = 0; /* fail */ +- goto repeat; +- } +- +- nftl = NFTLs[dev / (1<<NFTL_PARTN_BITS)]; +- DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n"); +- down(&nftl->mutex); +- DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n"); +- +- if (block + nsect > part_table[dev].nr_sects) { +- /* access past the end of device */ +- printk("nftl%c%d: bad access: block = %d, count = %d\n", +- (MINOR(req->rq_dev)>>6)+'a', dev & 0xf, block, nsect); +- up(&nftl->mutex); +- res = 0; /* fail */ +- goto repeat; +- } +- +- block += part_table[dev].start_sect; +- +- if (req->cmd == READ) { +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request of 0x%x sectors @ %x " +- "(req->nr_sectors == %lx)\n", nsect, block, req->nr_sectors); +- +- for ( ; nsect > 0; nsect-- , block++, buffer += 512) { +- /* Read a single sector to req->buffer + (512 * i) */ +- if (NFTL_readblock(nftl, block, buffer)) { +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL read request failed\n"); +- up(&nftl->mutex); +- res = 0; +- goto repeat; +- } +- } +- +- DEBUG(MTD_DEBUG_LEVEL2,"NFTL read request completed OK\n"); +- up(&nftl->mutex); +- goto repeat; +- } else if (req->cmd == WRITE) { +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL write request of 0x%x sectors @ %x " +- "(req->nr_sectors == %lx)\n", nsect, block, +- req->nr_sectors); +-#ifdef CONFIG_NFTL_RW +- for ( ; nsect > 0; nsect-- , block++, buffer += 512) { +- /* Read a single sector to req->buffer + (512 * i) */ +- if (NFTL_writeblock(nftl, block, buffer)) { +- DEBUG(MTD_DEBUG_LEVEL1,"NFTL write request failed\n"); +- up(&nftl->mutex); +- res = 0; +- goto repeat; +- } +- } +- DEBUG(MTD_DEBUG_LEVEL2,"NFTL write request completed OK\n"); +-#else +- res = 0; /* Writes always fail */ +-#endif /* CONFIG_NFTL_RW */ +- up(&nftl->mutex); +- goto repeat; +- } else { +- DEBUG(MTD_DEBUG_LEVEL0, "NFTL unknown request\n"); +- up(&nftl->mutex); +- res = 0; +- goto repeat; +- } +- repeat: +- DEBUG(MTD_DEBUG_LEVEL3, "end_request(%d)\n", res); +- spin_lock_irq(&io_request_lock); +- end_request(res); +- } +-} +- +-static int nftl_open(struct inode *ip, struct file *fp) +-{ +- int nftlnum = MINOR(ip->i_rdev) >> NFTL_PARTN_BITS; +- struct NFTLrecord *thisNFTL; +- thisNFTL = NFTLs[nftlnum]; +- +- DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n"); +- +-#ifdef CONFIG_KMOD +- if (!thisNFTL && nftlnum == 0) { +- request_module("docprobe"); +- thisNFTL = NFTLs[nftlnum]; +- } +-#endif +- if (!thisNFTL) { +- DEBUG(MTD_DEBUG_LEVEL2,"ENODEV: thisNFTL = %d, minor = %d, ip = %p, fp = %p\n", +- nftlnum, ip->i_rdev, ip, fp); +- return -ENODEV; +- } +- +-#ifndef CONFIG_NFTL_RW +- if (fp->f_mode & FMODE_WRITE) +- return -EROFS; +-#endif /* !CONFIG_NFTL_RW */ +- +- thisNFTL->usecount++; +- BLK_INC_USE_COUNT; +- if (!get_mtd_device(thisNFTL->mtd, -1)) { +- BLK_DEC_USE_COUNT; +- return -ENXIO; +- } +- +- return 0; +-} +- +-static int nftl_release(struct inode *inode, struct file *fp) ++static int nftl_getgeo(struct mtd_blktrans_dev *dev, struct hd_geometry *geo) + { +- struct NFTLrecord *thisNFTL; +- +- thisNFTL = NFTLs[MINOR(inode->i_rdev) / 16]; +- +- DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n"); +- +- if (thisNFTL->mtd->sync) +- thisNFTL->mtd->sync(thisNFTL->mtd); +- thisNFTL->usecount--; +- BLK_DEC_USE_COUNT; ++ struct NFTLrecord *nftl = (void *)dev; + +- put_mtd_device(thisNFTL->mtd); ++ geo->heads = nftl->heads; ++ geo->sectors = nftl->sectors; ++ geo->cylinders = nftl->cylinders; + + return 0; + } +-#if LINUX_VERSION_CODE < 0x20326 +-static struct file_operations nftl_fops = { +- read: block_read, +- write: block_write, +- ioctl: nftl_ioctl, +- open: nftl_open, +- release: nftl_release, +- fsync: block_fsync, +-}; +-#else +-static struct block_device_operations nftl_fops = +-{ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,4,14) +- owner: THIS_MODULE, +-#endif +- open: nftl_open, +- release: nftl_release, +- ioctl: nftl_ioctl +-}; +-#endif +- +- + + /**************************************************************************** + * +@@ -1048,49 +716,33 @@ + * + ****************************************************************************/ + +-static struct mtd_notifier nftl_notifier = { +- add: NFTL_notify_add, +- remove: NFTL_notify_remove ++ ++struct mtd_blktrans_ops nftl_tr = { ++ .name = "nftl", ++ .major = NFTL_MAJOR, ++ .part_bits = NFTL_PARTN_BITS, ++ .getgeo = nftl_getgeo, ++ .readsect = nftl_readblock, ++#ifdef CONFIG_NFTL_RW ++ .writesect = nftl_writeblock, ++#endif ++ .add_mtd = nftl_add_mtd, ++ .remove_dev = nftl_remove_dev, ++ .owner = THIS_MODULE, + }; + + extern char nftlmountrev[]; + + int __init init_nftl(void) + { +- int i; +- +-#ifdef PRERELEASE +- printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.87 $, nftlmount.c %s\n", nftlmountrev); +-#endif +- +- if (register_blkdev(MAJOR_NR, "nftl", &nftl_fops)){ +- printk("unable to register NFTL block device on major %d\n", MAJOR_NR); +- return -EBUSY; +- } else { +- blk_init_queue(BLK_DEFAULT_QUEUE(MAJOR_NR), &nftl_request); ++ printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.94 $, nftlmount.c %s\n", nftlmountrev); + +- /* set block size to 1kB each */ +- for (i = 0; i < 256; i++) { +- nftl_blocksizes[i] = 1024; +- } +- blksize_size[MAJOR_NR] = nftl_blocksizes; +- +- add_gendisk(&nftl_gendisk); +- } +- +- register_mtd_user(&nftl_notifier); +- +- return 0; ++ return register_mtd_blktrans(&nftl_tr); + } + + static void __exit cleanup_nftl(void) + { +- unregister_mtd_user(&nftl_notifier); +- unregister_blkdev(MAJOR_NR, "nftl"); +- +- blk_cleanup_queue(BLK_DEFAULT_QUEUE(MAJOR_NR)); +- +- del_gendisk(&nftl_gendisk); ++ deregister_mtd_blktrans(&nftl_tr); + } + + module_init(init_nftl); +diff -Nurb linux-mips-2.4.27/drivers/mtd/nftlmount.c linux/drivers/mtd/nftlmount.c +--- linux-mips-2.4.27/drivers/mtd/nftlmount.c 2003-07-05 05:23:38.000000000 +0200 ++++ linux/drivers/mtd/nftlmount.c 2004-11-19 10:25:11.655236880 +0100 +@@ -4,7 +4,7 @@ + * Author: Fabrice Bellard (fabrice.bellard@netgem.com) + * Copyright (C) 2000 Netgem S.A. + * +- * $Id: nftlmount.c,v 1.31 2002/11/15 16:34:43 dwmw2 Exp $ ++ * $Id: nftlmount.c,v 1.34 2003/05/21 10:54:10 dwmw2 Exp $ + * + * 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 +@@ -21,26 +21,17 @@ + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +-#define __NO_VERSION__ + #include <linux/kernel.h> +-#include <linux/module.h> + #include <asm/errno.h> +-#include <asm/io.h> +-#include <asm/uaccess.h> +-#include <linux/miscdevice.h> +-#include <linux/pci.h> + #include <linux/delay.h> + #include <linux/slab.h> +-#include <linux/sched.h> +-#include <linux/init.h> + #include <linux/mtd/mtd.h> + #include <linux/mtd/nand.h> + #include <linux/mtd/nftl.h> +-#include <linux/mtd/compatmac.h> + + #define SECTORSIZE 512 + +-char nftlmountrev[]="$Revision: 1.31 $"; ++char nftlmountrev[]="$Revision: 1.34 $"; + + /* find_boot_record: Find the NFTL Media Header and its Spare copy which contains the + * various device information of the NFTL partition and Bad Unit Table. Update +@@ -59,8 +50,8 @@ + + /* Assume logical EraseSize == physical erasesize for starting the scan. + We'll sort it out later if we find a MediaHeader which says otherwise */ +- nftl->EraseSize = nftl->mtd->erasesize; +- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize; ++ nftl->EraseSize = nftl->mbd.mtd->erasesize; ++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; + + nftl->MediaUnit = BLOCK_NIL; + nftl->SpareMediaUnit = BLOCK_NIL; +@@ -71,12 +62,12 @@ + + /* Check for ANAND header first. Then can whinge if it's found but later + checks fail */ +- if ((ret = MTD_READ(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) { ++ if ((ret = MTD_READ(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, &retlen, buf))) { + static int warncount = 5; + + if (warncount) { + printk(KERN_WARNING "Block read at 0x%x of mtd%d failed: %d\n", +- block * nftl->EraseSize, nftl->mtd->index, ret); ++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret); + if (!--warncount) + printk(KERN_WARNING "Further failures for this block will not be printed\n"); + } +@@ -87,16 +78,16 @@ + /* ANAND\0 not found. Continue */ + #if 0 + printk(KERN_DEBUG "ANAND header not found at 0x%x in mtd%d\n", +- block * nftl->EraseSize, nftl->mtd->index); ++ block * nftl->EraseSize, nftl->mbd.mtd->index); + #endif + continue; + } + + /* To be safer with BIOS, also use erase mark as discriminant */ +- if ((ret = MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, +- 8, &retlen, (char *)&h1)) < 0) { ++ if ((ret = MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, ++ 8, &retlen, (char *)&h1) < 0)) { + printk(KERN_WARNING "ANAND header found at 0x%x in mtd%d, but OOB data read failed (err %d)\n", +- block * nftl->EraseSize, nftl->mtd->index, ret); ++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret); + continue; + } + +@@ -106,23 +97,23 @@ + */ + if (le16_to_cpu(h1.EraseMark | h1.EraseMark1) != ERASE_MARK) { + printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but erase mark not present (0x%04x,0x%04x instead)\n", +- block * nftl->EraseSize, nftl->mtd->index, ++ block * nftl->EraseSize, nftl->mbd.mtd->index, + le16_to_cpu(h1.EraseMark), le16_to_cpu(h1.EraseMark1)); + continue; + } + + /* Finally reread to check ECC */ +- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize, SECTORSIZE, +- &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { ++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize, SECTORSIZE, ++ &retlen, buf, (char *)&oob, NAND_ECC_DISKONCHIP) < 0)) { + printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but ECC read failed (err %d)\n", +- block * nftl->EraseSize, nftl->mtd->index, ret); ++ block * nftl->EraseSize, nftl->mbd.mtd->index, ret); + continue; + } + + /* Paranoia. Check the ANAND header is still there after the ECC read */ + if (memcmp(buf, "ANAND", 6)) { + printk(KERN_NOTICE "ANAND header found at 0x%x in mtd%d, but went away on reread!\n", +- block * nftl->EraseSize, nftl->mtd->index); ++ block * nftl->EraseSize, nftl->mbd.mtd->index); + printk(KERN_NOTICE "New data are: %02x %02x %02x %02x %02x %02x\n", + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5]); + continue; +@@ -137,8 +128,12 @@ + printk(KERN_NOTICE "NFTL Media Headers at 0x%x and 0x%x disagree.\n", + nftl->MediaUnit * nftl->EraseSize, block * nftl->EraseSize); + /* if (debug) Print both side by side */ ++ if (boot_record_count < 2) { ++ /* We haven't yet seen two real ones */ + return -1; + } ++ continue; ++ } + if (boot_record_count == 1) + nftl->SpareMediaUnit = block; + +@@ -163,8 +158,8 @@ + } else if (mh->UnitSizeFactor != 0xff) { + printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n", + mh->UnitSizeFactor); +- nftl->EraseSize = nftl->mtd->erasesize << (0xff - mh->UnitSizeFactor); +- nftl->nb_blocks = nftl->mtd->size / nftl->EraseSize; ++ nftl->EraseSize = nftl->mbd.mtd->erasesize << (0xff - mh->UnitSizeFactor); ++ nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; + } + nftl->nb_boot_blocks = le16_to_cpu(mh->FirstPhysicalEUN); + if ((nftl->nb_boot_blocks + 2) >= nftl->nb_blocks) { +@@ -182,7 +177,7 @@ + return -1; + } + +- nftl->nr_sects = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); ++ nftl->mbd.size = nftl->numvunits * (nftl->EraseSize / SECTORSIZE); + + /* If we're not using the last sectors in the device for some reason, + reduce nb_blocks accordingly so we forget they're there */ +@@ -220,7 +215,7 @@ + for (i = 0; i < nftl->nb_blocks; i++) { + if ((i & (SECTORSIZE - 1)) == 0) { + /* read one sector for every SECTORSIZE of blocks */ +- if ((ret = MTD_READECC(nftl->mtd, block * nftl->EraseSize + ++ if ((ret = MTD_READECC(nftl->mbd.mtd, block * nftl->EraseSize + + i + SECTORSIZE, SECTORSIZE, &retlen, buf, + (char *)&oob, NAND_ECC_DISKONCHIP)) < 0) { + printk(KERN_NOTICE "Read of bad sector table failed (err %d)\n", +@@ -263,16 +258,16 @@ + for (i = 0; i < len; i += SECTORSIZE) { + /* we want to read the sector without ECC check here since a free + sector does not have ECC syndrome on it yet */ +- if (MTD_READ(nftl->mtd, address, SECTORSIZE, &retlen, buf) < 0) ++ if (MTD_READ(nftl->mbd.mtd, address, SECTORSIZE, &retlen, buf) < 0) + return -1; + if (memcmpb(buf, 0xff, SECTORSIZE) != 0) + return -1; + + if (check_oob) { +- if (MTD_READOOB(nftl->mtd, address, nftl->mtd->oobsize, ++ if (MTD_READOOB(nftl->mbd.mtd, address, nftl->mbd.mtd->oobsize, + &retlen, buf) < 0) + return -1; +- if (memcmpb(buf, 0xff, nftl->mtd->oobsize) != 0) ++ if (memcmpb(buf, 0xff, nftl->mbd.mtd->oobsize) != 0) + return -1; + } + address += SECTORSIZE; +@@ -297,7 +292,7 @@ + struct erase_info *instr = &nftl->instr; + + /* Read the Unit Control Information #1 for Wear-Leveling */ +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) + goto default_uci1; + +@@ -314,7 +309,7 @@ + /* XXX: use async erase interface, XXX: test return code */ + instr->addr = block * nftl->EraseSize; + instr->len = nftl->EraseSize; +- MTD_ERASE(nftl->mtd, instr); ++ MTD_ERASE(nftl->mbd.mtd, instr); + + if (instr->state == MTD_ERASE_FAILED) { + /* could not format, FixMe: We should update the BadUnitTable +@@ -337,7 +332,7 @@ + return -1; + + uci.WearInfo = le32_to_cpu(nb_erases); +- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, ++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&uci) < 0) + return -1; + return 0; +@@ -363,7 +358,7 @@ + block = first_block; + for (;;) { + for (i = 0; i < sectors_per_block; i++) { +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i * SECTORSIZE, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i * SECTORSIZE, + 8, &retlen, (char *)&bci) < 0) + status = SECTOR_IGNORE; + else +@@ -383,7 +378,7 @@ + /* sector not free actually : mark it as SECTOR_IGNORE */ + bci.Status = SECTOR_IGNORE; + bci.Status1 = SECTOR_IGNORE; +- MTD_WRITEOOB(nftl->mtd, ++ MTD_WRITEOOB(nftl->mbd.mtd, + block * nftl->EraseSize + i * SECTORSIZE, + 8, &retlen, (char *)&bci); + } +@@ -476,7 +471,7 @@ + size_t retlen; + + /* check erase mark. */ +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) + return -1; + +@@ -491,7 +486,7 @@ + h1.EraseMark = cpu_to_le16(ERASE_MARK); + h1.EraseMark1 = cpu_to_le16(ERASE_MARK); + h1.WearInfo = cpu_to_le32(0); +- if (MTD_WRITEOOB(nftl->mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, ++ if (MTD_WRITEOOB(nftl->mbd.mtd, block * nftl->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) + return -1; + } else { +@@ -503,7 +498,7 @@ + SECTORSIZE, 0) != 0) + return -1; + +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + i, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + i, + 16, &retlen, buf) < 0) + return -1; + if (i == SECTORSIZE) { +@@ -533,7 +528,7 @@ + struct nftl_uci2 uci; + size_t retlen; + +- if (MTD_READOOB(nftl->mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, ++ if (MTD_READOOB(nftl->mbd.mtd, block * nftl->EraseSize + 2 * SECTORSIZE + 8, + 8, &retlen, (char *)&uci) < 0) + return 0; + +@@ -572,9 +567,9 @@ + + for (;;) { + /* read the block header. If error, we format the chain */ +- if (MTD_READOOB(s->mtd, block * s->EraseSize + 8, 8, ++ if (MTD_READOOB(s->mbd.mtd, block * s->EraseSize + 8, 8, + &retlen, (char *)&h0) < 0 || +- MTD_READOOB(s->mtd, block * s->EraseSize + SECTORSIZE + 8, 8, ++ MTD_READOOB(s->mbd.mtd, block * s->EraseSize + SECTORSIZE + 8, 8, + &retlen, (char *)&h1) < 0) { + s->ReplUnitTable[block] = BLOCK_NIL; + do_format_chain = 1; +diff -Nurb linux-mips-2.4.27/drivers/mtd/redboot.c linux/drivers/mtd/redboot.c +--- linux-mips-2.4.27/drivers/mtd/redboot.c 2001-12-02 12:34:42.000000000 +0100 ++++ linux/drivers/mtd/redboot.c 2004-11-19 10:25:11.656236728 +0100 +@@ -1,5 +1,5 @@ + /* +- * $Id: redboot.c,v 1.6 2001/10/25 09:16:06 dwmw2 Exp $ ++ * $Id: redboot.c,v 1.12 2003/06/25 16:08:10 dwmw2 Exp $ + * + * Parse RedBoot-style Flash Image System (FIS) tables and + * produce a Linux partition array to match. +@@ -7,6 +7,7 @@ + + #include <linux/kernel.h> + #include <linux/slab.h> ++#include <linux/init.h> + + #include <linux/mtd/mtd.h> + #include <linux/mtd/partitions.h> +@@ -34,7 +35,9 @@ + return 1; + } + +-int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts) ++static int parse_redboot_partitions(struct mtd_info *master, ++ struct mtd_partition **pparts, ++ unsigned long fis_origin) + { + int nrparts = 0; + struct fis_image_desc *buf; +@@ -43,7 +46,9 @@ + int ret, i; + size_t retlen; + char *names; ++ char *nullname; + int namelen = 0; ++ static char nullstring[] = "unallocated"; + + buf = kmalloc(PAGE_SIZE, GFP_KERNEL); + +@@ -90,7 +95,11 @@ + goto out; + } + new_fl->img = &buf[i]; ++ if (fis_origin) { ++ buf[i].flash_base -= fis_origin; ++ } else { + buf[i].flash_base &= master->size-1; ++ } + + /* I'm sure the JFFS2 code has done me permanent damage. + * I now think the following is _normal_ +@@ -110,18 +119,24 @@ + if (tmp_fl->img->flash_base + tmp_fl->img->size + master->erasesize < tmp_fl->next->img->flash_base) + nrparts++; + } +- parts = kmalloc(sizeof(*parts)*nrparts + namelen, GFP_KERNEL); ++ parts = kmalloc(sizeof(*parts)*nrparts + sizeof(nullstring) + namelen, GFP_KERNEL); + + if (!parts) { + ret = -ENOMEM; + goto out; + } +- names = (char *)&parts[nrparts]; ++ + memset(parts, 0, sizeof(*parts)*nrparts + namelen); ++ ++ /* FIXME: Include nullname only if it's used */ ++ nullname = (char *)&parts[nrparts]; ++ sprintf(nullname, nullstring); ++ names = nullname + sizeof(nullstring); ++ + i=0; + + if (fl->img->flash_base) { +- parts[0].name = "unallocated space"; ++ parts[0].name = nullname; + parts[0].size = fl->img->flash_base; + parts[0].offset = 0; + } +@@ -133,11 +148,11 @@ + strcpy(names, fl->img->name); + names += strlen(names)+1; + +- if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize < fl->next->img->flash_base) { ++ if(fl->next && fl->img->flash_base + fl->img->size + master->erasesize <= fl->next->img->flash_base) { + i++; + parts[i].offset = parts[i-1].size + parts[i-1].offset; + parts[i].size = fl->next->img->flash_base - parts[i].offset; +- parts[i].name = "unallocated space"; ++ parts[i].name = nullname; + } + tmp_fl = fl; + fl = fl->next; +@@ -155,7 +170,24 @@ + return ret; + } + +-EXPORT_SYMBOL(parse_redboot_partitions); ++static struct mtd_part_parser redboot_parser = { ++ .owner = THIS_MODULE, ++ .parse_fn = parse_redboot_partitions, ++ .name = "RedBoot", ++}; ++ ++static int __init redboot_parser_init(void) ++{ ++ return register_mtd_parser(&redboot_parser); ++} ++ ++static void __exit redboot_parser_exit(void) ++{ ++ deregister_mtd_parser(&redboot_parser); ++} ++ ++module_init(redboot_parser_init); ++module_exit(redboot_parser_exit); + + MODULE_LICENSE("GPL"); + MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>"); +diff -Nurb linux-mips-2.4.27/fs/Config.in linux/fs/Config.in +--- linux-mips-2.4.27/fs/Config.in 2004-02-20 02:22:19.000000000 +0100 ++++ linux/fs/Config.in 2004-11-19 10:25:12.229149632 +0100 +@@ -49,6 +49,7 @@ + dep_tristate 'Journalling Flash File System v2 (JFFS2) support' CONFIG_JFFS2_FS $CONFIG_MTD + if [ "$CONFIG_JFFS2_FS" = "y" -o "$CONFIG_JFFS2_FS" = "m" ] ; then + int 'JFFS2 debugging verbosity (0 = quiet, 2 = noisy)' CONFIG_JFFS2_FS_DEBUG 0 ++ bool 'JFFS2 support for NAND chips' CONFIG_JFFS2_FS_NAND + fi + tristate 'Compressed ROM file system support' CONFIG_CRAMFS + bool 'Virtual memory file system support (former shm fs)' CONFIG_TMPFS +diff -Nurb linux-mips-2.4.27/fs/jffs2/Makefile linux/fs/jffs2/Makefile +--- linux-mips-2.4.27/fs/jffs2/Makefile 2003-08-13 19:19:25.000000000 +0200 ++++ linux/fs/jffs2/Makefile 2004-11-19 10:25:12.071173648 +0100 +@@ -1,7 +1,7 @@ + # + # Makefile for the linux Journalling Flash FileSystem (JFFS) routines. + # +-# $Id: Makefile,v 1.25.2.1 2002/10/11 09:04:44 dwmw2 Exp $ ++# $Id: Makefile.common,v 1.1 2003/05/27 09:34:41 dwmw2 Exp $ + # + # Note! Dependencies are done automagically by 'make dep', which also + # removes any old dependencies. DON'T put your own dependencies here +@@ -10,16 +10,31 @@ + # Note 2! The CFLAGS definitions are now in the main makefile... + + +-COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o pushpull.o \ +- compr_zlib.o ++obj-$(CONFIG_JFFS2_FS) := jffs2.o ++ ++COMPR_OBJS := compr.o compr_rubin.o compr_rtime.o compr_zlib.o + JFFS2_OBJS := dir.o file.o ioctl.o nodelist.o malloc.o \ +- read.o nodemgmt.o readinode.o super.o write.o scan.o gc.o \ +- symlink.o build.o erase.o background.o ++ read.o nodemgmt.o readinode.o write.o scan.o gc.o \ ++ symlink.o build.o erase.o background.o fs.o writev.o + +-O_TARGET := jffs2.o ++BELOW25 := $(shell echo $(PATCHLEVEL) | sed s/[1234]/y/) ++ ++ifeq ($(BELOW25),y) ++LINUX_OBJS := super-v24.o crc32.o rbtree.o ++else ++LINUX_OBJS := super.o ++endif + +-obj-y := $(COMPR_OBJS) $(JFFS2_OBJS) +-obj-m := $(O_TARGET) ++NAND_OBJS-$(CONFIG_JFFS2_FS_NAND) := wbuf.o + ++jffs2-objs := $(COMPR_OBJS) $(JFFS2_OBJS) $(VERS_OBJS) $(NAND_OBJS-y) \ ++ $(LINUX_OBJS) ++ ++ ++# 2.4 build compatibility ++ifeq ($(BELOW25),y) ++obj-y := $(jffs2-objs) ++O_TARGET := jffs2.o + include $(TOPDIR)/Rules.make ++endif + +diff -Nurb linux-mips-2.4.27/fs/jffs2/background.c linux/fs/jffs2/background.c +--- linux-mips-2.4.27/fs/jffs2/background.c 2001-11-06 08:56:10.000000000 +0100 ++++ linux/fs/jffs2/background.c 2004-11-19 10:25:12.072173496 +0100 +@@ -1,61 +1,36 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: background.c,v 1.16 2001/10/08 09:22:38 dwmw2 Exp $ ++ * $Id: background.c,v 1.47 2003/11/26 15:30:58 dwmw2 Exp $ + * + */ + + #define __KERNEL_SYSCALLS__ + + #include <linux/kernel.h> +-#include <linux/sched.h> +-#include <linux/unistd.h> + #include <linux/jffs2.h> + #include <linux/mtd/mtd.h> +-#include <linux/interrupt.h> + #include <linux/completion.h> ++#include <linux/sched.h> ++#include <linux/unistd.h> ++#include <linux/suspend.h> + #include "nodelist.h" + + + static int jffs2_garbage_collect_thread(void *); +-static int thread_should_wake(struct jffs2_sb_info *c); + + void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c) + { +- spin_lock_bh(&c->erase_completion_lock); +- if (c->gc_task && thread_should_wake(c)) ++ spin_lock(&c->erase_completion_lock); ++ if (c->gc_task && jffs2_thread_should_wake(c)) + send_sig(SIGHUP, c->gc_task, 1); +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + } + + /* This must only ever be called when no GC thread is currently running */ +@@ -86,12 +61,12 @@ + + void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) + { +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + if (c->gc_task) { + D1(printk(KERN_DEBUG "jffs2: Killing GC task %d\n", c->gc_task->pid)); + send_sig(SIGKILL, c->gc_task, 1); + } +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + wait_for_completion(&c->gc_thread_exit); + } + +@@ -99,34 +74,37 @@ + { + struct jffs2_sb_info *c = _c; + +- daemonize(); +- current->tty = NULL; ++ daemonize("jffs2_gcd_mtd%d", c->mtd->index); ++ allow_signal(SIGKILL); ++ allow_signal(SIGSTOP); ++ allow_signal(SIGCONT); ++ + c->gc_task = current; + up(&c->gc_thread_start); + +- sprintf(current->comm, "jffs2_gcd_mtd%d", c->mtd->index); +- +- /* FIXME in the 2.2 backport */ +- current->nice = 10; ++ set_user_nice(current, 10); + + for (;;) { +- spin_lock_irq(¤t->sigmask_lock); +- siginitsetinv (¤t->blocked, sigmask(SIGHUP) | sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); +- recalc_sigpending(current); +- spin_unlock_irq(¤t->sigmask_lock); ++ allow_signal(SIGHUP); + +- if (!thread_should_wake(c)) { ++ if (!jffs2_thread_should_wake(c)) { + set_current_state (TASK_INTERRUPTIBLE); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread sleeping...\n")); +- /* Yes, there's a race here; we checked thread_should_wake() before +- setting current->state to TASK_INTERRUPTIBLE. But it doesn't ++ /* Yes, there's a race here; we checked jffs2_thread_should_wake() ++ before setting current->state to TASK_INTERRUPTIBLE. But it doesn't + matter - We don't care if we miss a wakeup, because the GC thread + is only an optimisation anyway. */ + schedule(); + } + +- if (current->need_resched) +- schedule(); ++ if (current->flags & PF_FREEZE) { ++ refrigerator(0); ++ /* refrigerator() should recalc sigpending for us ++ but doesn't. No matter - allow_signal() will. */ ++ continue; ++ } ++ ++ cond_resched(); + + /* Put_super will send a SIGKILL and then wait on the sem. + */ +@@ -134,9 +112,7 @@ + siginfo_t info; + unsigned long signr; + +- spin_lock_irq(¤t->sigmask_lock); +- signr = dequeue_signal(¤t->blocked, &info); +- spin_unlock_irq(¤t->sigmask_lock); ++ signr = dequeue_signal_lock(current, ¤t->blocked, &info); + + switch(signr) { + case SIGSTOP: +@@ -147,9 +123,10 @@ + + case SIGKILL: + D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); +- spin_lock_bh(&c->erase_completion_lock); ++ die: ++ spin_lock(&c->erase_completion_lock); + c->gc_task = NULL; +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + complete_and_exit(&c->gc_thread_exit, 0); + + case SIGHUP: +@@ -157,27 +134,15 @@ + break; + default: + D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): signal %ld received\n", signr)); +- + } + } + /* We don't want SIGHUP to interrupt us. STOP and KILL are OK though. */ +- spin_lock_irq(¤t->sigmask_lock); +- siginitsetinv (¤t->blocked, sigmask(SIGKILL) | sigmask(SIGSTOP) | sigmask(SIGCONT)); +- recalc_sigpending(current); +- spin_unlock_irq(¤t->sigmask_lock); ++ disallow_signal(SIGHUP); + + D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): pass\n")); +- jffs2_garbage_collect_pass(c); ++ if (jffs2_garbage_collect_pass(c) == -ENOSPC) { ++ printk(KERN_NOTICE "No space for garbage collection. Aborting GC thread\n"); ++ goto die; ++ } + } +-} +- +-static int thread_should_wake(struct jffs2_sb_info *c) +-{ +- D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x\n", +- c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size)); +- if (c->nr_free_blocks + c->nr_erasing_blocks < JFFS2_RESERVED_BLOCKS_GCTRIGGER && +- c->dirty_size > c->sector_size) +- return 1; +- else +- return 0; + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/build.c linux/fs/jffs2/build.c +--- linux-mips-2.4.27/fs/jffs2/build.c 2003-07-05 05:23:44.000000000 +0200 ++++ linux/fs/jffs2/build.c 2004-11-19 10:25:12.073173344 +0100 +@@ -1,47 +1,22 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: build.c,v 1.16.2.3 2003/04/30 09:43:32 dwmw2 Exp $ ++ * $Id: build.c,v 1.55 2003/10/28 17:02:44 dwmw2 Exp $ + * + */ + + #include <linux/kernel.h> +-#include <linux/jffs2.h> ++#include <linux/sched.h> + #include <linux/slab.h> + #include "nodelist.h" + +-int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *); +-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *); ++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *, struct jffs2_full_dirent **); + + static inline struct jffs2_inode_cache * + first_inode_chain(int *i, struct jffs2_sb_info *c) +@@ -68,16 +43,52 @@ + ic; \ + ic = next_inode(&i, ic, (c))) + ++ ++static inline void jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) ++{ ++ struct jffs2_full_dirent *fd; ++ ++ D1(printk(KERN_DEBUG "jffs2_build_inode building directory inode #%u\n", ic->ino)); ++ ++ /* For each child, increase nlink */ ++ for(fd = ic->scan_dents; fd; fd = fd->next) { ++ struct jffs2_inode_cache *child_ic; ++ if (!fd->ino) ++ continue; ++ ++ /* XXX: Can get high latency here with huge directories */ ++ ++ child_ic = jffs2_get_ino_cache(c, fd->ino); ++ if (!child_ic) { ++ printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", ++ fd->name, fd->ino, ic->ino); ++ continue; ++ } ++ ++ if (child_ic->nlink++ && fd->type == DT_DIR) { ++ printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); ++ if (fd->ino == 1 && ic->ino == 1) { ++ printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n"); ++ printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n"); ++ } ++ /* What do we do about it? */ ++ } ++ D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino)); ++ /* Can't free them. We might need them in pass 2 */ ++ } ++} ++ + /* Scan plan: + - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go + - Scan directory tree from top down, setting nlink in inocaches + - Scan inocaches for inodes with nlink==0 + */ +-int jffs2_build_filesystem(struct jffs2_sb_info *c) ++static int jffs2_build_filesystem(struct jffs2_sb_info *c) + { + int ret; + int i; + struct jffs2_inode_cache *ic; ++ struct jffs2_full_dirent *dead_fds = NULL; + + /* First, scan the medium and build all the inode caches with + lists of physical nodes */ +@@ -90,14 +101,17 @@ + return ret; + + D1(printk(KERN_DEBUG "Scanned flash completely\n")); +- /* Now build the data map for each inode, marking obsoleted nodes +- as such, and also increase nlink of any children. */ ++ D1(jffs2_dump_block_lists(c)); ++ ++ /* Now scan the directory tree, increasing nlink according to every dirent found. */ + for_each_inode(i, c, ic) { + D1(printk(KERN_DEBUG "Pass 1: ino #%u\n", ic->ino)); +- ret = jffs2_build_inode_pass1(c, ic); +- if (ret) { +- D1(printk(KERN_WARNING "Eep. jffs2_build_inode_pass1 for ino %d returned %d\n", ic->ino, ret)); +- return ret; ++ ++ D1(BUG_ON(ic->ino > c->highest_ino)); ++ ++ if (ic->scan_dents) { ++ jffs2_build_inode_pass1(c, ic); ++ cond_resched(); + } + } + D1(printk(KERN_DEBUG "Pass 1 complete\n")); +@@ -107,181 +121,226 @@ + children too, and repeat the scan. As that's going to be + a fairly uncommon occurrence, it's not so evil to do it this + way. Recursion bad. */ +- do { +- D1(printk(KERN_DEBUG "Pass 2 (re)starting\n")); +- ret = 0; ++ D1(printk(KERN_DEBUG "Pass 2 starting\n")); ++ + for_each_inode(i, c, ic) { + D1(printk(KERN_DEBUG "Pass 2: ino #%u, nlink %d, ic %p, nodes %p\n", ic->ino, ic->nlink, ic, ic->nodes)); + if (ic->nlink) + continue; + +- ret = jffs2_build_remove_unlinked_inode(c, ic); +- if (ret) +- break; +- /* -EAGAIN means the inode's nlink was zero, so we deleted it, +- and furthermore that it had children and their nlink has now +- gone to zero too. So we have to restart the scan. */ ++ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); ++ cond_resched(); ++ } ++ ++ D1(printk(KERN_DEBUG "Pass 2a starting\n")); ++ ++ while (dead_fds) { ++ struct jffs2_inode_cache *ic; ++ struct jffs2_full_dirent *fd = dead_fds; ++ ++ dead_fds = fd->next; ++ ++ ic = jffs2_get_ino_cache(c, fd->ino); ++ D1(printk(KERN_DEBUG "Removing dead_fd ino #%u (\"%s\"), ic at %p\n", fd->ino, fd->name, ic)); ++ ++ if (ic) ++ jffs2_build_remove_unlinked_inode(c, ic, &dead_fds); ++ jffs2_free_full_dirent(fd); + } +- } while(ret == -EAGAIN); + + D1(printk(KERN_DEBUG "Pass 2 complete\n")); + +- /* Finally, we can scan again and free the dirent nodes and scan_info structs */ ++ /* Finally, we can scan again and free the dirent structs */ + for_each_inode(i, c, ic) { +- struct jffs2_scan_info *scan = ic->scan; + struct jffs2_full_dirent *fd; + D1(printk(KERN_DEBUG "Pass 3: ino #%u, ic %p, nodes %p\n", ic->ino, ic, ic->nodes)); +- if (!scan) { +- if (ic->nlink) { +- D1(printk(KERN_WARNING "Why no scan struct for ino #%u which has nlink %d?\n", ic->ino, ic->nlink)); +- } +- continue; +- } +- ic->scan = NULL; +- while(scan->dents) { +- fd = scan->dents; +- scan->dents = fd->next; ++ ++ while(ic->scan_dents) { ++ fd = ic->scan_dents; ++ ic->scan_dents = fd->next; + jffs2_free_full_dirent(fd); + } +- kfree(scan); ++ ic->scan_dents = NULL; ++ cond_resched(); + } + D1(printk(KERN_DEBUG "Pass 3 complete\n")); ++ D1(jffs2_dump_block_lists(c)); ++ ++ /* Rotate the lists by some number to ensure wear levelling */ ++ jffs2_rotate_lists(c); + + return ret; + } + +-int jffs2_build_inode_pass1(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) ++static void jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, struct jffs2_full_dirent **dead_fds) + { +- struct jffs2_tmp_dnode_info *tn; ++ struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd; +- struct jffs2_node_frag *fraglist = NULL; +- struct jffs2_tmp_dnode_info *metadata = NULL; +- +- D1(printk(KERN_DEBUG "jffs2_build_inode building inode #%u\n", ic->ino)); +- if (ic->ino > c->highest_ino) +- c->highest_ino = ic->ino; + +- if (!ic->scan->tmpnodes && ic->ino != 1) { +- D1(printk(KERN_DEBUG "jffs2_build_inode: ino #%u has no data nodes!\n", ic->ino)); +- } +- /* Build the list to make sure any obsolete nodes are marked as such */ +- while(ic->scan->tmpnodes) { +- tn = ic->scan->tmpnodes; +- ic->scan->tmpnodes = tn->next; +- +- if (metadata && tn->version > metadata->version) { +- D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring old metadata at 0x%08x\n", +- metadata->fn->raw->flash_offset &~3)); ++ D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); + +- jffs2_free_full_dnode(metadata->fn); +- jffs2_free_tmp_dnode_info(metadata); +- metadata = NULL; ++ for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) { ++ D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", ref_offset(raw))); ++ jffs2_mark_node_obsolete(c, raw); + } + +- if (tn->fn->size) { +- jffs2_add_full_dnode_to_fraglist (c, &fraglist, tn->fn); +- jffs2_free_tmp_dnode_info(tn); +- } else { +- if (!metadata) { +- metadata = tn; +- } else { +- D1(printk(KERN_DEBUG "jffs2_build_inode_pass1 ignoring new metadata at 0x%08x\n", +- tn->fn->raw->flash_offset &~3)); +- +- jffs2_free_full_dnode(tn->fn); +- jffs2_free_tmp_dnode_info(tn); +- } +- } +- } ++ if (ic->scan_dents) { ++ int whinged = 0; ++ D1(printk(KERN_DEBUG "Inode #%u was a directory which may have children...\n", ic->ino)); + +- /* OK. Now clear up */ +- if (metadata) { +- jffs2_free_full_dnode(metadata->fn); +- jffs2_free_tmp_dnode_info(metadata); +- } +- metadata = NULL; ++ while(ic->scan_dents) { ++ struct jffs2_inode_cache *child_ic; + +- while (fraglist) { +- struct jffs2_node_frag *frag; +- frag = fraglist; +- fraglist = fraglist->next; ++ fd = ic->scan_dents; ++ ic->scan_dents = fd->next; + +- if (frag->node && !(--frag->node->frags)) { +- jffs2_free_full_dnode(frag->node); ++ if (!fd->ino) { ++ /* It's a deletion dirent. Ignore it */ ++ D1(printk(KERN_DEBUG "Child \"%s\" is a deletion dirent, skipping...\n", fd->name)); ++ jffs2_free_full_dirent(fd); ++ continue; + } +- jffs2_free_node_frag(frag); ++ if (!whinged) { ++ whinged = 1; ++ printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino); + } + +- /* Now for each child, increase nlink */ +- for(fd=ic->scan->dents; fd; fd = fd->next) { +- struct jffs2_inode_cache *child_ic; +- if (!fd->ino) +- continue; ++ D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n", ++ fd->name, fd->ino)); + + child_ic = jffs2_get_ino_cache(c, fd->ino); + if (!child_ic) { +- printk(KERN_NOTICE "Eep. Child \"%s\" (ino #%u) of dir ino #%u doesn't exist!\n", +- fd->name, fd->ino, ic->ino); ++ printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino); ++ jffs2_free_full_dirent(fd); + continue; + } + +- if (child_ic->nlink++ && fd->type == DT_DIR) { +- printk(KERN_NOTICE "Child dir \"%s\" (ino #%u) of dir ino #%u appears to be a hard link\n", fd->name, fd->ino, ic->ino); +- if (fd->ino == 1 && ic->ino == 1) { +- printk(KERN_NOTICE "This is mostly harmless, and probably caused by creating a JFFS2 image\n"); +- printk(KERN_NOTICE "using a buggy version of mkfs.jffs2. Use at least v1.17.\n"); ++ /* Reduce nlink of the child. If it's now zero, stick it on the ++ dead_fds list to be cleaned up later. Else just free the fd */ ++ ++ child_ic->nlink--; ++ ++ if (!child_ic->nlink) { ++ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got zero nlink. Adding to dead_fds list.\n", ++ fd->ino, fd->name)); ++ fd->next = *dead_fds; ++ *dead_fds = fd; ++ } else { ++ D1(printk(KERN_DEBUG "Inode #%u (\"%s\") has now got nlink %d. Ignoring.\n", ++ fd->ino, fd->name, child_ic->nlink)); ++ jffs2_free_full_dirent(fd); + } +- /* What do we do about it? */ + } +- D1(printk(KERN_DEBUG "Increased nlink for child \"%s\" (ino #%u)\n", fd->name, fd->ino)); +- /* Can't free them. We might need them in pass 2 */ + } +- return 0; ++ ++ /* ++ We don't delete the inocache from the hash list and free it yet. ++ The erase code will do that, when all the nodes are completely gone. ++ */ + } + +-int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) ++static void jffs2_calc_trigger_levels(struct jffs2_sb_info *c) + { +- struct jffs2_raw_node_ref *raw; +- struct jffs2_full_dirent *fd; +- int ret = 0; ++ uint32_t size; + +- if(!ic->scan) { +- D1(printk(KERN_DEBUG "ino #%u was already removed\n", ic->ino)); +- return 0; +- } ++ /* Deletion should almost _always_ be allowed. We're fairly ++ buggered once we stop allowing people to delete stuff ++ because there's not enough free space... */ ++ c->resv_blocks_deletion = 2; ++ ++ /* Be conservative about how much space we need before we allow writes. ++ On top of that which is required for deletia, require an extra 2% ++ of the medium to be available, for overhead caused by nodes being ++ split across blocks, etc. */ ++ ++ size = c->flash_size / 50; /* 2% of flash size */ ++ size += c->nr_blocks * 100; /* And 100 bytes per eraseblock */ ++ size += c->sector_size - 1; /* ... and round up */ ++ ++ c->resv_blocks_write = c->resv_blocks_deletion + (size / c->sector_size); ++ ++ /* When do we let the GC thread run in the background */ ++ ++ c->resv_blocks_gctrigger = c->resv_blocks_write + 1; ++ ++ /* When do we allow garbage collection to merge nodes to make ++ long-term progress at the expense of short-term space exhaustion? */ ++ c->resv_blocks_gcmerge = c->resv_blocks_deletion + 1; ++ ++ /* When do we allow garbage collection to eat from bad blocks rather ++ than actually making progress? */ ++ c->resv_blocks_gcbad = 0;//c->resv_blocks_deletion + 2; ++ ++ /* If there's less than this amount of dirty space, don't bother ++ trying to GC to make more space. It'll be a fruitless task */ ++ c->nospc_dirty_size = c->sector_size + (c->flash_size / 100); ++ ++ D1(printk(KERN_DEBUG "JFFS2 trigger levels (size %d KiB, block size %d KiB, %d blocks)\n", ++ c->flash_size / 1024, c->sector_size / 1024, c->nr_blocks)); ++ D1(printk(KERN_DEBUG "Blocks required to allow deletion: %d (%d KiB)\n", ++ c->resv_blocks_deletion, c->resv_blocks_deletion*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Blocks required to allow writes: %d (%d KiB)\n", ++ c->resv_blocks_write, c->resv_blocks_write*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Blocks required to quiesce GC thread: %d (%d KiB)\n", ++ c->resv_blocks_gctrigger, c->resv_blocks_gctrigger*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Blocks required to allow GC merges: %d (%d KiB)\n", ++ c->resv_blocks_gcmerge, c->resv_blocks_gcmerge*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Blocks required to GC bad blocks: %d (%d KiB)\n", ++ c->resv_blocks_gcbad, c->resv_blocks_gcbad*c->sector_size/1024)); ++ D1(printk(KERN_DEBUG "Amount of dirty space required to GC: %d bytes\n", ++ c->nospc_dirty_size)); ++} + +- D1(printk(KERN_DEBUG "JFFS2: Removing ino #%u with nlink == zero.\n", ic->ino)); ++int jffs2_do_mount_fs(struct jffs2_sb_info *c) ++{ ++ int i; + +- for (raw = ic->nodes; raw != (void *)ic; raw = raw->next_in_ino) { +- D1(printk(KERN_DEBUG "obsoleting node at 0x%08x\n", raw->flash_offset&~3)); +- jffs2_mark_node_obsolete(c, raw); ++ c->free_size = c->flash_size; ++ c->nr_blocks = c->flash_size / c->sector_size; ++ c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); ++ if (!c->blocks) ++ return -ENOMEM; ++ for (i=0; i<c->nr_blocks; i++) { ++ INIT_LIST_HEAD(&c->blocks[i].list); ++ c->blocks[i].offset = i * c->sector_size; ++ c->blocks[i].free_size = c->sector_size; ++ c->blocks[i].dirty_size = 0; ++ c->blocks[i].wasted_size = 0; ++ c->blocks[i].unchecked_size = 0; ++ c->blocks[i].used_size = 0; ++ c->blocks[i].first_node = NULL; ++ c->blocks[i].last_node = NULL; ++ } ++ ++ init_MUTEX(&c->alloc_sem); ++ init_MUTEX(&c->erase_free_sem); ++ init_waitqueue_head(&c->erase_wait); ++ init_waitqueue_head(&c->inocache_wq); ++ spin_lock_init(&c->erase_completion_lock); ++ spin_lock_init(&c->inocache_lock); ++ ++ INIT_LIST_HEAD(&c->clean_list); ++ INIT_LIST_HEAD(&c->very_dirty_list); ++ INIT_LIST_HEAD(&c->dirty_list); ++ INIT_LIST_HEAD(&c->erasable_list); ++ INIT_LIST_HEAD(&c->erasing_list); ++ INIT_LIST_HEAD(&c->erase_pending_list); ++ INIT_LIST_HEAD(&c->erasable_pending_wbuf_list); ++ INIT_LIST_HEAD(&c->erase_complete_list); ++ INIT_LIST_HEAD(&c->free_list); ++ INIT_LIST_HEAD(&c->bad_list); ++ INIT_LIST_HEAD(&c->bad_used_list); ++ c->highest_ino = 1; ++ ++ if (jffs2_build_filesystem(c)) { ++ D1(printk(KERN_DEBUG "build_fs failed\n")); ++ jffs2_free_ino_caches(c); ++ jffs2_free_raw_node_refs(c); ++ kfree(c->blocks); ++ return -EIO; + } + +- if (ic->scan->dents) { +- printk(KERN_NOTICE "Inode #%u was a directory with children - removing those too...\n", ic->ino); +- +- while(ic->scan->dents) { +- struct jffs2_inode_cache *child_ic; ++ jffs2_calc_trigger_levels(c); + +- fd = ic->scan->dents; +- ic->scan->dents = fd->next; +- +- D1(printk(KERN_DEBUG "Removing child \"%s\", ino #%u\n", +- fd->name, fd->ino)); +- +- child_ic = jffs2_get_ino_cache(c, fd->ino); +- if (!child_ic) { +- printk(KERN_NOTICE "Cannot remove child \"%s\", ino #%u, because it doesn't exist\n", fd->name, fd->ino); +- continue; +- } +- jffs2_free_full_dirent(fd); +- child_ic->nlink--; +- } +- ret = -EAGAIN; +- } +- kfree(ic->scan); +- ic->scan = NULL; +- // jffs2_del_ino_cache(c, ic); +- // jffs2_free_inode_cache(ic); +- return ret; ++ return 0; + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr.c linux/fs/jffs2/compr.c +--- linux-mips-2.4.27/fs/jffs2/compr.c 2001-11-05 21:16:18.000000000 +0100 ++++ linux/fs/jffs2/compr.c 2004-11-19 10:25:12.085171520 +0100 +@@ -1,59 +1,37 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by Arjan van de Ven <arjanv@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: compr.c,v 1.17 2001/09/23 09:56:46 dwmw2 Exp $ ++ * $Id: compr.c,v 1.33 2003/11/28 17:22:54 dwmw2 Exp $ + * + */ + + #include <linux/kernel.h> + #include <linux/string.h> +-#include <linux/types.h> + #include <linux/errno.h> ++#include <linux/types.h> ++#include <linux/slab.h> + #include <linux/jffs2.h> ++#include "nodelist.h" + +-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen); +-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen); +-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen); +-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen); +-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen); +-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen); +-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen); +-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, __u32 srclen, __u32 destlen); ++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); ++void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); ++int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); ++void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); ++int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); ++void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); ++int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen); ++void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, uint32_t srclen, uint32_t destlen); + + + /* jffs2_compress: + * @data: Pointer to uncompressed data +- * @cdata: Pointer to buffer for compressed data ++ * @cdata: Pointer to returned pointer to buffer for compressed data + * @datalen: On entry, holds the amount of data available for compression. + * On exit, expected to hold the amount of data actually compressed. + * @cdatalen: On entry, holds the amount of space available for compressed +@@ -68,47 +46,59 @@ + * jffs2_compress should compress as much as will fit, and should set + * *datalen accordingly to show the amount of data which were compressed. + */ +-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *datalen, __u32 *cdatalen) ++unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, ++ uint32_t *datalen, uint32_t *cdatalen) + { ++#ifdef JFFS2_COMPRESSION + int ret; + +- ret = zlib_compress(data_in, cpage_out, datalen, cdatalen); ++ *cpage_out = kmalloc(*cdatalen, GFP_KERNEL); ++ if (!*cpage_out) { ++ printk(KERN_WARNING "No memory for compressor allocation. Compression failed\n"); ++ goto out; ++ } ++ ++#ifdef JFFS2_USE_ZLIB ++ ret = jffs2_zlib_compress(data_in, *cpage_out, datalen, cdatalen); + if (!ret) { + return JFFS2_COMPR_ZLIB; + } +-#if 0 /* Disabled 23/9/1. With zlib it hardly ever gets a look in */ +- ret = dynrubin_compress(data_in, cpage_out, datalen, cdatalen); ++#endif ++#ifdef JFFS2_USE_DYNRUBIN ++ ret = jffs2_dynrubin_compress(data_in, *cpage_out, datalen, cdatalen); + if (!ret) { + return JFFS2_COMPR_DYNRUBIN; + } + #endif +-#if 0 /* Disabled 26/2/1. Obsoleted by dynrubin */ +- ret = rubinmips_compress(data_in, cpage_out, datalen, cdatalen); ++#ifdef JFFS2_USE_RUBINMIPS ++ ret = jffs2_rubinmips_compress(data_in, *cpage_out, datalen, cdatalen); + if (!ret) { + return JFFS2_COMPR_RUBINMIPS; + } + #endif ++#ifdef JFFS2_USE_RTIME + /* rtime does manage to recompress already-compressed data */ +- ret = rtime_compress(data_in, cpage_out, datalen, cdatalen); ++ ret = jffs2_rtime_compress(data_in, *cpage_out, datalen, cdatalen); + if (!ret) { + return JFFS2_COMPR_RTIME; + } +-#if 0 +- /* We don't need to copy. Let the caller special-case the COMPR_NONE case. */ +- /* If we get here, no compression is going to work */ +- /* But we might want to use the fragmentation part -- Arjan */ +- memcpy(cpage_out,data_in,min(*datalen,*cdatalen)); +- if (*datalen > *cdatalen) +- *datalen = *cdatalen; + #endif ++ kfree(*cpage_out); ++#endif /* Compression */ ++ out: ++ *cpage_out = data_in; ++ *datalen = *cdatalen; + return JFFS2_COMPR_NONE; /* We failed to compress */ +- + } + ++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig) ++{ ++ if (orig != comprbuf) ++ kfree(comprbuf); ++} + + int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, +- unsigned char *data_out, __u32 cdatalen, __u32 datalen) ++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen) + { + switch (comprtype) { + case JFFS2_COMPR_NONE: +@@ -119,30 +109,27 @@ + case JFFS2_COMPR_ZERO: + memset(data_out, 0, datalen); + break; +- ++#ifdef JFFS2_USE_ZLIB + case JFFS2_COMPR_ZLIB: +- zlib_decompress(cdata_in, data_out, cdatalen, datalen); ++ jffs2_zlib_decompress(cdata_in, data_out, cdatalen, datalen); + break; +- ++#endif ++#ifdef JFFS2_USE_RTIME + case JFFS2_COMPR_RTIME: +- rtime_decompress(cdata_in, data_out, cdatalen, datalen); ++ jffs2_rtime_decompress(cdata_in, data_out, cdatalen, datalen); + break; +- +- case JFFS2_COMPR_RUBINMIPS: +-#if 0 /* Disabled 23/9/1 */ +- rubinmips_decompress(cdata_in, data_out, cdatalen, datalen); +-#else +- printk(KERN_WARNING "JFFS2: Rubinmips compression encountered but support not compiled in!\n"); + #endif ++#ifdef JFFS2_USE_RUBINMIPS ++ case JFFS2_COMPR_RUBINMIPS: ++ jffs2_rubinmips_decompress(cdata_in, data_out, cdatalen, datalen); + break; +- case JFFS2_COMPR_DYNRUBIN: +-#if 1 /* Phase this one out */ +- dynrubin_decompress(cdata_in, data_out, cdatalen, datalen); +-#else +- printk(KERN_WARNING "JFFS2: Dynrubin compression encountered but support not compiled in!\n"); + #endif +- break; ++#ifdef JFFS2_USE_DYNRUBIN ++ case JFFS2_COMPR_DYNRUBIN: + ++ jffs2_dynrubin_decompress(cdata_in, data_out, cdatalen, datalen); ++ break; ++#endif + default: + printk(KERN_NOTICE "Unknown JFFS2 compression type 0x%02x\n", comprtype); + return -EIO; +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_rtime.c linux/fs/jffs2/compr_rtime.c +--- linux-mips-2.4.27/fs/jffs2/compr_rtime.c 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/compr_rtime.c 2004-11-19 10:25:12.087171216 +0100 +@@ -1,43 +1,19 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * + * Created by Arjan van de Ven <arjanv@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: compr_rtime.c,v 1.5 2001/03/15 15:38:23 dwmw2 Exp $ ++ * $Id: compr_rtime.c,v 1.11 2003/10/04 08:33:06 dwmw2 Exp $ + * + * + * Very simple lz77-ish encoder. + * + * Theory of operation: Both encoder and decoder have a list of "last +- * occurances" for every possible source-value; after sending the ++ * occurrences" for every possible source-value; after sending the + * first source-byte, the second byte indicated the "run" length of + * matches + * +@@ -51,10 +27,10 @@ + #include <linux/string.h> + + /* _compress returns the compressed size, -1 if bigger */ +-int rtime_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *sourcelen, __u32 *dstlen) ++int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) + { +- int positions[256]; ++ short positions[256]; + int outpos = 0; + int pos=0; + +@@ -91,10 +67,10 @@ + } + + +-void rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 srclen, __u32 destlen) ++void jffs2_rtime_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen) + { +- int positions[256]; ++ short positions[256]; + int outpos = 0; + int pos=0; + +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_rubin.c linux/fs/jffs2/compr_rubin.c +--- linux-mips-2.4.27/fs/jffs2/compr_rubin.c 2001-11-05 21:16:18.000000000 +0100 ++++ linux/fs/jffs2/compr_rubin.c 2004-11-19 10:25:12.088171064 +0100 +@@ -1,37 +1,13 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001, 2002 Red Hat, Inc. + * + * Created by Arjan van de Ven <arjanv@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: compr_rubin.c,v 1.13 2001/09/23 10:06:05 rmk Exp $ ++ * $Id: compr_rubin.c,v 1.17 2002/05/20 14:56:37 dwmw2 Exp $ + * + */ + +@@ -43,7 +19,7 @@ + + + +-void init_rubin(struct rubin_state *rs, int div, int *bits) ++static void init_rubin(struct rubin_state *rs, int div, int *bits) + { + int c; + +@@ -56,7 +32,7 @@ + } + + +-int encode(struct rubin_state *rs, long A, long B, int symbol) ++static int encode(struct rubin_state *rs, long A, long B, int symbol) + { + + long i0, i1; +@@ -91,7 +67,7 @@ + } + + +-void end_rubin(struct rubin_state *rs) ++static void end_rubin(struct rubin_state *rs) + { + + int i; +@@ -104,7 +80,7 @@ + } + + +-void init_decode(struct rubin_state *rs, int div, int *bits) ++static void init_decode(struct rubin_state *rs, int div, int *bits) + { + init_rubin(rs, div, bits); + +@@ -151,7 +127,7 @@ + rs->rec_q = rec_q; + } + +-int decode(struct rubin_state *rs, long A, long B) ++static int decode(struct rubin_state *rs, long A, long B) + { + unsigned long p = rs->p, q = rs->q; + long i0, threshold; +@@ -212,8 +188,8 @@ + + + +-int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, +- unsigned char *cpage_out, __u32 *sourcelen, __u32 *dstlen) ++static int rubin_do_compress(int bit_divider, int *bits, unsigned char *data_in, ++ unsigned char *cpage_out, uint32_t *sourcelen, uint32_t *dstlen) + { + int outpos = 0; + int pos=0; +@@ -246,20 +222,20 @@ + } + #if 0 + /* _compress returns the compressed size, -1 if bigger */ +-int rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *sourcelen, __u32 *dstlen) ++int jffs2_rubinmips_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) + { + return rubin_do_compress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); + } + #endif +-int dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *sourcelen, __u32 *dstlen) ++int jffs2_dynrubin_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) + { + int bits[8]; + unsigned char histo[256]; + int i; + int ret; +- __u32 mysrclen, mydstlen; ++ uint32_t mysrclen, mydstlen; + + mysrclen = *sourcelen; + mydstlen = *dstlen - 8; +@@ -315,8 +291,8 @@ + return 0; + } + +-void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, +- unsigned char *page_out, __u32 srclen, __u32 destlen) ++static void rubin_do_decompress(int bit_divider, int *bits, unsigned char *cdata_in, ++ unsigned char *page_out, uint32_t srclen, uint32_t destlen) + { + int outpos = 0; + struct rubin_state rs; +@@ -330,14 +306,14 @@ + } + + +-void rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 sourcelen, __u32 dstlen) ++void jffs2_rubinmips_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t sourcelen, uint32_t dstlen) + { + rubin_do_decompress(BIT_DIVIDER_MIPS, bits_mips, data_in, cpage_out, sourcelen, dstlen); + } + +-void dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 sourcelen, __u32 dstlen) ++void jffs2_dynrubin_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t sourcelen, uint32_t dstlen) + { + int bits[8]; + int c; +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_rubin.h linux/fs/jffs2/compr_rubin.h +--- linux-mips-2.4.27/fs/jffs2/compr_rubin.h 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/compr_rubin.h 2004-11-19 10:25:12.090170760 +0100 +@@ -1,7 +1,7 @@ + /* Rubin encoder/decoder header */ + /* work started at : aug 3, 1994 */ + /* last modification : aug 15, 1994 */ +-/* $Id: compr_rubin.h,v 1.5 2001/02/26 13:50:01 dwmw2 Exp $ */ ++/* $Id: compr_rubin.h,v 1.6 2002/01/25 01:49:26 dwmw2 Exp $ */ + + #include "pushpull.h" + +@@ -19,10 +19,3 @@ + int bit_divider; + int bits[8]; + }; +- +- +-void init_rubin (struct rubin_state *rs, int div, int *bits); +-int encode (struct rubin_state *, long, long, int); +-void end_rubin (struct rubin_state *); +-void init_decode (struct rubin_state *, int div, int *bits); +-int decode (struct rubin_state *, long, long); +diff -Nurb linux-mips-2.4.27/fs/jffs2/compr_zlib.c linux/fs/jffs2/compr_zlib.c +--- linux-mips-2.4.27/fs/jffs2/compr_zlib.c 2003-01-11 18:53:17.000000000 +0100 ++++ linux/fs/jffs2/compr_zlib.c 2004-11-19 10:25:12.091170608 +0100 +@@ -1,50 +1,26 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001, 2002 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: compr_zlib.c,v 1.8.2.1 2002/10/11 09:04:44 dwmw2 Exp $ ++ * $Id: compr_zlib.c,v 1.25 2003/12/03 09:25:43 dwmw2 Exp $ + * + */ + +-#ifndef __KERNEL__ ++#if !defined(__KERNEL__) && !defined(__ECOS) + #error "The userspace support got too messy and was removed. Update your mkfs.jffs2" + #endif + + #include <linux/config.h> + #include <linux/kernel.h> +-#include <linux/mtd/compatmac.h> /* for min() */ + #include <linux/slab.h> +-#include <linux/jffs2.h> + #include <linux/zlib.h> ++#include <linux/zutil.h> ++#include <asm/semaphore.h> + #include "nodelist.h" + + /* Plan: call deflate() with avail_in == *sourcelen, +@@ -58,21 +34,24 @@ + + static DECLARE_MUTEX(deflate_sem); + static DECLARE_MUTEX(inflate_sem); +-static void *deflate_workspace; +-static void *inflate_workspace; ++static z_stream inf_strm, def_strm; ++ ++#ifdef __KERNEL__ /* Linux-only */ ++#include <linux/vmalloc.h> ++#include <linux/init.h> + + int __init jffs2_zlib_init(void) + { +- deflate_workspace = vmalloc(zlib_deflate_workspacesize()); +- if (!deflate_workspace) { ++ def_strm.workspace = vmalloc(zlib_deflate_workspacesize()); ++ if (!def_strm.workspace) { + printk(KERN_WARNING "Failed to allocate %d bytes for deflate workspace\n", zlib_deflate_workspacesize()); + return -ENOMEM; + } + D1(printk(KERN_DEBUG "Allocated %d bytes for deflate workspace\n", zlib_deflate_workspacesize())); +- inflate_workspace = vmalloc(zlib_inflate_workspacesize()); +- if (!inflate_workspace) { ++ inf_strm.workspace = vmalloc(zlib_inflate_workspacesize()); ++ if (!inf_strm.workspace) { + printk(KERN_WARNING "Failed to allocate %d bytes for inflate workspace\n", zlib_inflate_workspacesize()); +- vfree(deflate_workspace); ++ vfree(def_strm.workspace); + return -ENOMEM; + } + D1(printk(KERN_DEBUG "Allocated %d bytes for inflate workspace\n", zlib_inflate_workspacesize())); +@@ -81,97 +60,120 @@ + + void jffs2_zlib_exit(void) + { +- vfree(deflate_workspace); +- vfree(inflate_workspace); ++ vfree(def_strm.workspace); ++ vfree(inf_strm.workspace); + } ++#endif /* __KERNEL__ */ + +-int zlib_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *sourcelen, __u32 *dstlen) ++int jffs2_zlib_compress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t *sourcelen, uint32_t *dstlen) + { +- z_stream strm; + int ret; + + if (*dstlen <= STREAM_END_SPACE) + return -1; + + down(&deflate_sem); +- strm.workspace = deflate_workspace; + +- if (Z_OK != zlib_deflateInit(&strm, 3)) { ++ if (Z_OK != zlib_deflateInit(&def_strm, 3)) { + printk(KERN_WARNING "deflateInit failed\n"); + up(&deflate_sem); + return -1; + } + +- strm.next_in = data_in; +- strm.total_in = 0; ++ def_strm.next_in = data_in; ++ def_strm.total_in = 0; + +- strm.next_out = cpage_out; +- strm.total_out = 0; ++ def_strm.next_out = cpage_out; ++ def_strm.total_out = 0; + +- while (strm.total_out < *dstlen - STREAM_END_SPACE && strm.total_in < *sourcelen) { +- strm.avail_out = *dstlen - (strm.total_out + STREAM_END_SPACE); +- strm.avail_in = min((unsigned)(*sourcelen-strm.total_in), strm.avail_out); ++ while (def_strm.total_out < *dstlen - STREAM_END_SPACE && def_strm.total_in < *sourcelen) { ++ def_strm.avail_out = *dstlen - (def_strm.total_out + STREAM_END_SPACE); ++ def_strm.avail_in = min((unsigned)(*sourcelen-def_strm.total_in), def_strm.avail_out); + D1(printk(KERN_DEBUG "calling deflate with avail_in %d, avail_out %d\n", +- strm.avail_in, strm.avail_out)); +- ret = zlib_deflate(&strm, Z_PARTIAL_FLUSH); ++ def_strm.avail_in, def_strm.avail_out)); ++ ret = zlib_deflate(&def_strm, Z_PARTIAL_FLUSH); + D1(printk(KERN_DEBUG "deflate returned with avail_in %d, avail_out %d, total_in %ld, total_out %ld\n", +- strm.avail_in, strm.avail_out, strm.total_in, strm.total_out)); ++ def_strm.avail_in, def_strm.avail_out, def_strm.total_in, def_strm.total_out)); + if (ret != Z_OK) { + D1(printk(KERN_DEBUG "deflate in loop returned %d\n", ret)); +- zlib_deflateEnd(&strm); ++ zlib_deflateEnd(&def_strm); + up(&deflate_sem); + return -1; + } + } +- strm.avail_out += STREAM_END_SPACE; +- strm.avail_in = 0; +- ret = zlib_deflate(&strm, Z_FINISH); +- zlib_deflateEnd(&strm); +- up(&deflate_sem); ++ def_strm.avail_out += STREAM_END_SPACE; ++ def_strm.avail_in = 0; ++ ret = zlib_deflate(&def_strm, Z_FINISH); ++ zlib_deflateEnd(&def_strm); ++ + if (ret != Z_STREAM_END) { + D1(printk(KERN_DEBUG "final deflate returned %d\n", ret)); +- return -1; ++ ret = -1; ++ goto out; + } + +- D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", +- strm.total_in, strm.total_out)); ++ if (def_strm.total_out >= def_strm.total_in) { ++ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld; failing\n", ++ def_strm.total_in, def_strm.total_out)); ++ ret = -1; ++ goto out; ++ } + +- if (strm.total_out >= strm.total_in) +- return -1; ++ D1(printk(KERN_DEBUG "zlib compressed %ld bytes into %ld\n", ++ def_strm.total_in, def_strm.total_out)); + +- *dstlen = strm.total_out; +- *sourcelen = strm.total_in; +- return 0; ++ *dstlen = def_strm.total_out; ++ *sourcelen = def_strm.total_in; ++ ret = 0; ++ out: ++ up(&deflate_sem); ++ return ret; + } + +-void zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 srclen, __u32 destlen) ++void jffs2_zlib_decompress(unsigned char *data_in, unsigned char *cpage_out, ++ uint32_t srclen, uint32_t destlen) + { +- z_stream strm; + int ret; ++ int wbits = MAX_WBITS; + + down(&inflate_sem); +- strm.workspace = inflate_workspace; + +- if (Z_OK != zlib_inflateInit(&strm)) { ++ inf_strm.next_in = data_in; ++ inf_strm.avail_in = srclen; ++ inf_strm.total_in = 0; ++ ++ inf_strm.next_out = cpage_out; ++ inf_strm.avail_out = destlen; ++ inf_strm.total_out = 0; ++ ++ /* If it's deflate, and it's got no preset dictionary, then ++ we can tell zlib to skip the adler32 check. */ ++ if (srclen > 2 && !(data_in[1] & PRESET_DICT) && ++ ((data_in[0] & 0x0f) == Z_DEFLATED) && ++ !(((data_in[0]<<8) + data_in[1]) % 31)) { ++ ++ D2(printk(KERN_DEBUG "inflate skipping adler32\n")); ++ wbits = -((data_in[0] >> 4) + 8); ++ inf_strm.next_in += 2; ++ inf_strm.avail_in -= 2; ++ } else { ++ /* Let this remain D1 for now -- it should never happen */ ++ D1(printk(KERN_DEBUG "inflate not skipping adler32\n")); ++ } ++ ++ ++ if (Z_OK != zlib_inflateInit2(&inf_strm, wbits)) { + printk(KERN_WARNING "inflateInit failed\n"); + up(&inflate_sem); + return; + } +- strm.next_in = data_in; +- strm.avail_in = srclen; +- strm.total_in = 0; +- +- strm.next_out = cpage_out; +- strm.avail_out = destlen; +- strm.total_out = 0; + +- while((ret = zlib_inflate(&strm, Z_FINISH)) == Z_OK) ++ while((ret = zlib_inflate(&inf_strm, Z_FINISH)) == Z_OK) + ; + if (ret != Z_STREAM_END) { + printk(KERN_NOTICE "inflate returned %d\n", ret); + } +- zlib_inflateEnd(&strm); ++ zlib_inflateEnd(&inf_strm); + up(&inflate_sem); + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/comprtest.c linux/fs/jffs2/comprtest.c +--- linux-mips-2.4.27/fs/jffs2/comprtest.c 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/comprtest.c 2004-11-19 10:25:12.093170304 +0100 +@@ -1,4 +1,4 @@ +-/* $Id: comprtest.c,v 1.4 2001/02/21 14:03:20 dwmw2 Exp $ */ ++/* $Id: comprtest.c,v 1.5 2002/01/03 15:20:44 dwmw2 Exp $ */ + + #include <linux/kernel.h> + #include <linux/string.h> +@@ -266,13 +266,13 @@ + static unsigned char decomprbuf[TESTDATA_LEN]; + + int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, +- unsigned char *data_out, __u32 cdatalen, __u32 datalen); ++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); + unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *datalen, __u32 *cdatalen); ++ uint32_t *datalen, uint32_t *cdatalen); + + int init_module(void ) { + unsigned char comprtype; +- __u32 c, d; ++ uint32_t c, d; + int ret; + + printk("Original data: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", +diff -Nurb linux-mips-2.4.27/fs/jffs2/crc32.c linux/fs/jffs2/crc32.c +--- linux-mips-2.4.27/fs/jffs2/crc32.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/crc32.c 2004-11-19 10:25:12.095170000 +0100 +@@ -0,0 +1,97 @@ ++/* ++ * COPYRIGHT (C) 1986 Gary S. Brown. You may use this program, or ++ * code or tables extracted from it, as desired without restriction. ++ * ++ * First, the polynomial itself and its table of feedback terms. The ++ * polynomial is ++ * X^32+X^26+X^23+X^22+X^16+X^12+X^11+X^10+X^8+X^7+X^5+X^4+X^2+X^1+X^0 ++ * ++ * Note that we take it "backwards" and put the highest-order term in ++ * the lowest-order bit. The X^32 term is "implied"; the LSB is the ++ * X^31 term, etc. The X^0 term (usually shown as "+1") results in ++ * the MSB being 1 ++ * ++ * Note that the usual hardware shift register implementation, which ++ * is what we're using (we're merely optimizing it by doing eight-bit ++ * chunks at a time) shifts bits into the lowest-order term. In our ++ * implementation, that means shifting towards the right. Why do we ++ * do it this way? Because the calculated CRC must be transmitted in ++ * order from highest-order term to lowest-order term. UARTs transmit ++ * characters in order from LSB to MSB. By storing the CRC this way ++ * we hand it to the UART in the order low-byte to high-byte; the UART ++ * sends each low-bit to hight-bit; and the result is transmission bit ++ * by bit from highest- to lowest-order term without requiring any bit ++ * shuffling on our part. Reception works similarly ++ * ++ * The feedback terms table consists of 256, 32-bit entries. Notes ++ * ++ * The table can be generated at runtime if desired; code to do so ++ * is shown later. It might not be obvious, but the feedback ++ * terms simply represent the results of eight shift/xor opera ++ * tions for all combinations of data and CRC register values ++ * ++ * The values must be right-shifted by eight bits by the "updcrc ++ * logic; the shift must be unsigned (bring in zeroes). On some ++ * hardware you could probably optimize the shift in assembler by ++ * using byte-swap instructions ++ * polynomial $edb88320 ++ */ ++ ++/* $Id: crc32.c,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */ ++ ++#include "crc32.h" ++ ++const uint32_t crc32_table[256] = { ++ 0x00000000L, 0x77073096L, 0xee0e612cL, 0x990951baL, 0x076dc419L, ++ 0x706af48fL, 0xe963a535L, 0x9e6495a3L, 0x0edb8832L, 0x79dcb8a4L, ++ 0xe0d5e91eL, 0x97d2d988L, 0x09b64c2bL, 0x7eb17cbdL, 0xe7b82d07L, ++ 0x90bf1d91L, 0x1db71064L, 0x6ab020f2L, 0xf3b97148L, 0x84be41deL, ++ 0x1adad47dL, 0x6ddde4ebL, 0xf4d4b551L, 0x83d385c7L, 0x136c9856L, ++ 0x646ba8c0L, 0xfd62f97aL, 0x8a65c9ecL, 0x14015c4fL, 0x63066cd9L, ++ 0xfa0f3d63L, 0x8d080df5L, 0x3b6e20c8L, 0x4c69105eL, 0xd56041e4L, ++ 0xa2677172L, 0x3c03e4d1L, 0x4b04d447L, 0xd20d85fdL, 0xa50ab56bL, ++ 0x35b5a8faL, 0x42b2986cL, 0xdbbbc9d6L, 0xacbcf940L, 0x32d86ce3L, ++ 0x45df5c75L, 0xdcd60dcfL, 0xabd13d59L, 0x26d930acL, 0x51de003aL, ++ 0xc8d75180L, 0xbfd06116L, 0x21b4f4b5L, 0x56b3c423L, 0xcfba9599L, ++ 0xb8bda50fL, 0x2802b89eL, 0x5f058808L, 0xc60cd9b2L, 0xb10be924L, ++ 0x2f6f7c87L, 0x58684c11L, 0xc1611dabL, 0xb6662d3dL, 0x76dc4190L, ++ 0x01db7106L, 0x98d220bcL, 0xefd5102aL, 0x71b18589L, 0x06b6b51fL, ++ 0x9fbfe4a5L, 0xe8b8d433L, 0x7807c9a2L, 0x0f00f934L, 0x9609a88eL, ++ 0xe10e9818L, 0x7f6a0dbbL, 0x086d3d2dL, 0x91646c97L, 0xe6635c01L, ++ 0x6b6b51f4L, 0x1c6c6162L, 0x856530d8L, 0xf262004eL, 0x6c0695edL, ++ 0x1b01a57bL, 0x8208f4c1L, 0xf50fc457L, 0x65b0d9c6L, 0x12b7e950L, ++ 0x8bbeb8eaL, 0xfcb9887cL, 0x62dd1ddfL, 0x15da2d49L, 0x8cd37cf3L, ++ 0xfbd44c65L, 0x4db26158L, 0x3ab551ceL, 0xa3bc0074L, 0xd4bb30e2L, ++ 0x4adfa541L, 0x3dd895d7L, 0xa4d1c46dL, 0xd3d6f4fbL, 0x4369e96aL, ++ 0x346ed9fcL, 0xad678846L, 0xda60b8d0L, 0x44042d73L, 0x33031de5L, ++ 0xaa0a4c5fL, 0xdd0d7cc9L, 0x5005713cL, 0x270241aaL, 0xbe0b1010L, ++ 0xc90c2086L, 0x5768b525L, 0x206f85b3L, 0xb966d409L, 0xce61e49fL, ++ 0x5edef90eL, 0x29d9c998L, 0xb0d09822L, 0xc7d7a8b4L, 0x59b33d17L, ++ 0x2eb40d81L, 0xb7bd5c3bL, 0xc0ba6cadL, 0xedb88320L, 0x9abfb3b6L, ++ 0x03b6e20cL, 0x74b1d29aL, 0xead54739L, 0x9dd277afL, 0x04db2615L, ++ 0x73dc1683L, 0xe3630b12L, 0x94643b84L, 0x0d6d6a3eL, 0x7a6a5aa8L, ++ 0xe40ecf0bL, 0x9309ff9dL, 0x0a00ae27L, 0x7d079eb1L, 0xf00f9344L, ++ 0x8708a3d2L, 0x1e01f268L, 0x6906c2feL, 0xf762575dL, 0x806567cbL, ++ 0x196c3671L, 0x6e6b06e7L, 0xfed41b76L, 0x89d32be0L, 0x10da7a5aL, ++ 0x67dd4accL, 0xf9b9df6fL, 0x8ebeeff9L, 0x17b7be43L, 0x60b08ed5L, ++ 0xd6d6a3e8L, 0xa1d1937eL, 0x38d8c2c4L, 0x4fdff252L, 0xd1bb67f1L, ++ 0xa6bc5767L, 0x3fb506ddL, 0x48b2364bL, 0xd80d2bdaL, 0xaf0a1b4cL, ++ 0x36034af6L, 0x41047a60L, 0xdf60efc3L, 0xa867df55L, 0x316e8eefL, ++ 0x4669be79L, 0xcb61b38cL, 0xbc66831aL, 0x256fd2a0L, 0x5268e236L, ++ 0xcc0c7795L, 0xbb0b4703L, 0x220216b9L, 0x5505262fL, 0xc5ba3bbeL, ++ 0xb2bd0b28L, 0x2bb45a92L, 0x5cb36a04L, 0xc2d7ffa7L, 0xb5d0cf31L, ++ 0x2cd99e8bL, 0x5bdeae1dL, 0x9b64c2b0L, 0xec63f226L, 0x756aa39cL, ++ 0x026d930aL, 0x9c0906a9L, 0xeb0e363fL, 0x72076785L, 0x05005713L, ++ 0x95bf4a82L, 0xe2b87a14L, 0x7bb12baeL, 0x0cb61b38L, 0x92d28e9bL, ++ 0xe5d5be0dL, 0x7cdcefb7L, 0x0bdbdf21L, 0x86d3d2d4L, 0xf1d4e242L, ++ 0x68ddb3f8L, 0x1fda836eL, 0x81be16cdL, 0xf6b9265bL, 0x6fb077e1L, ++ 0x18b74777L, 0x88085ae6L, 0xff0f6a70L, 0x66063bcaL, 0x11010b5cL, ++ 0x8f659effL, 0xf862ae69L, 0x616bffd3L, 0x166ccf45L, 0xa00ae278L, ++ 0xd70dd2eeL, 0x4e048354L, 0x3903b3c2L, 0xa7672661L, 0xd06016f7L, ++ 0x4969474dL, 0x3e6e77dbL, 0xaed16a4aL, 0xd9d65adcL, 0x40df0b66L, ++ 0x37d83bf0L, 0xa9bcae53L, 0xdebb9ec5L, 0x47b2cf7fL, 0x30b5ffe9L, ++ 0xbdbdf21cL, 0xcabac28aL, 0x53b39330L, 0x24b4a3a6L, 0xbad03605L, ++ 0xcdd70693L, 0x54de5729L, 0x23d967bfL, 0xb3667a2eL, 0xc4614ab8L, ++ 0x5d681b02L, 0x2a6f2b94L, 0xb40bbe37L, 0xc30c8ea1L, 0x5a05df1bL, ++ 0x2d02ef8dL ++}; +diff -Nurb linux-mips-2.4.27/fs/jffs2/crc32.h linux/fs/jffs2/crc32.h +--- linux-mips-2.4.27/fs/jffs2/crc32.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/crc32.h 2004-11-19 10:25:12.096169848 +0100 +@@ -0,0 +1,21 @@ ++#ifndef CRC32_H ++#define CRC32_H ++ ++/* $Id: crc32.h,v 1.4 2002/01/03 15:20:44 dwmw2 Exp $ */ ++ ++#include <linux/types.h> ++ ++extern const uint32_t crc32_table[256]; ++ ++/* Return a 32-bit CRC of the contents of the buffer. */ ++ ++static inline uint32_t ++crc32(uint32_t val, const void *ss, int len) ++{ ++ const unsigned char *s = ss; ++ while (--len >= 0) ++ val = crc32_table[(val ^ *s++) & 0xff] ^ (val >> 8); ++ return val; ++} ++ ++#endif +diff -Nurb linux-mips-2.4.27/fs/jffs2/dir.c linux/fs/jffs2/dir.c +--- linux-mips-2.4.27/fs/jffs2/dir.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/dir.c 2004-11-19 10:25:12.097169696 +0100 +@@ -1,84 +1,73 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: dir.c,v 1.45.2.8 2003/11/02 13:51:17 dwmw2 Exp $ ++ * $Id: dir.c,v 1.82 2003/10/11 11:47:23 dwmw2 Exp $ + * + */ + + #include <linux/kernel.h> + #include <linux/slab.h> ++#include <linux/sched.h> + #include <linux/fs.h> +-#include <linux/mtd/compatmac.h> /* For completion */ ++#include <linux/crc32.h> + #include <linux/jffs2.h> + #include <linux/jffs2_fs_i.h> + #include <linux/jffs2_fs_sb.h> ++#include <linux/time.h> + #include "nodelist.h" +-#include <linux/crc32.h> ++ ++/* Urgh. Please tell me there's a nicer way of doing these. */ ++#include <linux/version.h> ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) ++typedef int mknod_arg_t; ++#define NAMEI_COMPAT(x) ((void *)x) ++#else ++typedef dev_t mknod_arg_t; ++#define NAMEI_COMPAT(x) (x) ++#endif + + static int jffs2_readdir (struct file *, void *, filldir_t); + +-static int jffs2_create (struct inode *,struct dentry *,int); +-static struct dentry *jffs2_lookup (struct inode *,struct dentry *); ++static int jffs2_create (struct inode *,struct dentry *,int, ++ struct nameidata *); ++static struct dentry *jffs2_lookup (struct inode *,struct dentry *, ++ struct nameidata *); + static int jffs2_link (struct dentry *,struct inode *,struct dentry *); + static int jffs2_unlink (struct inode *,struct dentry *); + static int jffs2_symlink (struct inode *,struct dentry *,const char *); + static int jffs2_mkdir (struct inode *,struct dentry *,int); + static int jffs2_rmdir (struct inode *,struct dentry *); +-static int jffs2_mknod (struct inode *,struct dentry *,int,int); ++static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t); + static int jffs2_rename (struct inode *, struct dentry *, + struct inode *, struct dentry *); + + struct file_operations jffs2_dir_operations = + { +- read: generic_read_dir, +- readdir: jffs2_readdir, +- ioctl: jffs2_ioctl, +- fsync: jffs2_null_fsync ++ .read = generic_read_dir, ++ .readdir = jffs2_readdir, ++ .ioctl = jffs2_ioctl, ++ .fsync = jffs2_fsync + }; + + + struct inode_operations jffs2_dir_inode_operations = + { +- create: jffs2_create, +- lookup: jffs2_lookup, +- link: jffs2_link, +- unlink: jffs2_unlink, +- symlink: jffs2_symlink, +- mkdir: jffs2_mkdir, +- rmdir: jffs2_rmdir, +- mknod: jffs2_mknod, +- rename: jffs2_rename, +- setattr: jffs2_setattr, ++ .create = NAMEI_COMPAT(jffs2_create), ++ .lookup = NAMEI_COMPAT(jffs2_lookup), ++ .link = jffs2_link, ++ .unlink = jffs2_unlink, ++ .symlink = jffs2_symlink, ++ .mkdir = jffs2_mkdir, ++ .rmdir = jffs2_rmdir, ++ .mknod = jffs2_mknod, ++ .rename = jffs2_rename, ++ .setattr = jffs2_setattr, + }; + + /***********************************************************************/ +@@ -88,12 +77,13 @@ + and we use the same hash function as the dentries. Makes this + nice and simple + */ +-static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target) ++static struct dentry *jffs2_lookup(struct inode *dir_i, struct dentry *target, ++ struct nameidata *nd) + { + struct jffs2_inode_info *dir_f; + struct jffs2_sb_info *c; + struct jffs2_full_dirent *fd = NULL, *fd_list; +- __u32 ino = 0; ++ uint32_t ino = 0; + struct inode *inode = NULL; + + D1(printk(KERN_DEBUG "jffs2_lookup()\n")); +@@ -153,8 +143,9 @@ + offset++; + } + if (offset == 1) { +- D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", filp->f_dentry->d_parent->d_inode->i_ino)); +- if (filldir(dirent, "..", 2, 1, filp->f_dentry->d_parent->d_inode->i_ino, DT_DIR) < 0) ++ unsigned long pino = parent_ino(filp->f_dentry); ++ D1(printk(KERN_DEBUG "Dirent 1: \"..\", ino #%lu\n", pino)); ++ if (filldir(dirent, "..", 2, 1, pino, DT_DIR) < 0) + goto out; + offset++; + } +@@ -188,18 +179,14 @@ + + /***********************************************************************/ + +-static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode) ++ ++static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode, ++ struct nameidata *nd) + { ++ struct jffs2_raw_inode *ri; + struct jffs2_inode_info *f, *dir_f; + struct jffs2_sb_info *c; + struct inode *inode; +- struct jffs2_raw_inode *ri; +- struct jffs2_raw_dirent *rd; +- struct jffs2_full_dnode *fn; +- struct jffs2_full_dirent *fd; +- int namelen; +- __u32 alloclen, phys_ofs; +- __u32 writtenlen; + int ret; + + ri = jffs2_alloc_raw_inode(); +@@ -210,23 +197,11 @@ + + D1(printk(KERN_DEBUG "jffs2_create()\n")); + +- /* Try to reserve enough space for both node and dirent. +- * Just the node will do for now, though +- */ +- namelen = dentry->d_name.len; +- ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL); +- D1(printk(KERN_DEBUG "jffs2_create(): reserved 0x%x bytes\n", alloclen)); +- if (ret) { +- jffs2_free_raw_inode(ri); +- return ret; +- } +- + inode = jffs2_new_inode(dir_i, mode, ri); + + if (IS_ERR(inode)) { + D1(printk(KERN_DEBUG "jffs2_new_inode() failed\n")); + jffs2_free_raw_inode(ri); +- jffs2_complete_reservation(c); + return PTR_ERR(inode); + } + +@@ -236,93 +211,22 @@ + inode->i_mapping->nrpages = 0; + + f = JFFS2_INODE_INFO(inode); ++ dir_f = JFFS2_INODE_INFO(dir_i); + +- ri->data_crc = 0; +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); +- +- fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen); +- D1(printk(KERN_DEBUG "jffs2_create created file with mode 0x%x\n", ri->mode)); +- jffs2_free_raw_inode(ri); +- +- if (IS_ERR(fn)) { +- D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); +- /* Eeek. Wave bye bye */ +- up(&f->sem); +- jffs2_complete_reservation(c); +- jffs2_clear_inode(inode); +- return PTR_ERR(fn); +- } +- /* No data here. Only a metadata node, which will be +- obsoleted by the first data write +- */ +- f->metadata = fn; +- +- /* Work out where to put the dirent node now. */ +- writtenlen = PAD(writtenlen); +- phys_ofs += writtenlen; +- alloclen -= writtenlen; +- up(&f->sem); +- +- if (alloclen < sizeof(*rd)+namelen) { +- /* Not enough space left in this chunk. Get some more */ +- jffs2_complete_reservation(c); +- ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ ret = jffs2_do_create(c, dir_f, f, ri, ++ dentry->d_name.name, dentry->d_name.len); + + if (ret) { +- /* Eep. */ +- D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); + jffs2_clear_inode(inode); ++ make_bad_inode(inode); ++ iput(inode); ++ jffs2_free_raw_inode(ri); + return ret; + } +- } + +- rd = jffs2_alloc_raw_dirent(); +- if (!rd) { +- /* Argh. Now we treat it like a normal delete */ +- jffs2_complete_reservation(c); +- jffs2_clear_inode(inode); +- return -ENOMEM; +- } +- +- dir_f = JFFS2_INODE_INFO(dir_i); +- down(&dir_f->sem); +- +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + namelen; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = inode->i_ino; +- rd->mctime = CURRENT_TIME; +- rd->nsize = namelen; +- rd->type = DT_REG; +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, namelen); +- +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); +- +- jffs2_complete_reservation(c); +- +- if (IS_ERR(fd)) { +- /* dirent failed to write. Delete the inode normally +- as if it were the final unlink() */ +- jffs2_free_raw_dirent(rd); +- up(&dir_f->sem); +- jffs2_clear_inode(inode); +- return PTR_ERR(fd); +- } +- +- dir_i->i_mtime = dir_i->i_ctime = rd->mctime; +- +- jffs2_free_raw_dirent(rd); +- +- /* Link the fd into the inode's list, obsoleting an old +- one if necessary. */ +- jffs2_add_fd_to_list(c, fd, &dir_f->dents); +- up(&dir_f->sem); ++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime)); + ++ jffs2_free_raw_inode(ri); + d_instantiate(dentry, inode); + + D1(printk(KERN_DEBUG "jffs2_create: Created ino #%lu with mode %o, nlink %d(%d). nrpages %ld\n", +@@ -332,173 +236,48 @@ + + /***********************************************************************/ + +-static int jffs2_do_unlink(struct inode *dir_i, struct dentry *dentry, int rename) +-{ +- struct jffs2_inode_info *dir_f, *f; +- struct jffs2_sb_info *c; +- struct jffs2_raw_dirent *rd; +- struct jffs2_full_dirent *fd; +- __u32 alloclen, phys_ofs; +- int ret; +- +- c = JFFS2_SB_INFO(dir_i->i_sb); +- +- rd = jffs2_alloc_raw_dirent(); +- if (!rd) +- return -ENOMEM; +- +- ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_DELETION); +- if (ret) { +- jffs2_free_raw_dirent(rd); +- return ret; +- } +- +- dir_f = JFFS2_INODE_INFO(dir_i); +- down(&dir_f->sem); +- +- /* Build a deletion node */ +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + dentry->d_name.len; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = 0; +- rd->mctime = CURRENT_TIME; +- rd->nsize = dentry->d_name.len; +- rd->type = DT_UNKNOWN; +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len); +- +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL); +- +- jffs2_complete_reservation(c); +- jffs2_free_raw_dirent(rd); +- +- if (IS_ERR(fd)) { +- up(&dir_f->sem); +- return PTR_ERR(fd); +- } +- +- /* File it. This will mark the old one obsolete. */ +- jffs2_add_fd_to_list(c, fd, &dir_f->dents); +- up(&dir_f->sem); +- +- if (!rename) { +- f = JFFS2_INODE_INFO(dentry->d_inode); +- down(&f->sem); +- +- while (f->dents) { +- /* There can be only deleted ones */ +- fd = f->dents; +- +- f->dents = fd->next; +- +- if (fd->ino) { +- printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", +- f->inocache->ino, fd->name, fd->ino); +- } else { +- D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, f->inocache->ino)); +- } +- jffs2_mark_node_obsolete(c, fd->raw); +- jffs2_free_full_dirent(fd); +- } +- /* Don't oops on unlinking a bad inode */ +- if (f->inocache) +- f->inocache->nlink--; +- dentry->d_inode->i_nlink--; +- up(&f->sem); +- } +- +- return 0; +-} + + static int jffs2_unlink(struct inode *dir_i, struct dentry *dentry) + { +- return jffs2_do_unlink(dir_i, dentry, 0); +-} +-/***********************************************************************/ +- +-static int jffs2_do_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry, int rename) +-{ +- struct jffs2_inode_info *dir_f, *f; +- struct jffs2_sb_info *c; +- struct jffs2_raw_dirent *rd; +- struct jffs2_full_dirent *fd; +- __u32 alloclen, phys_ofs; ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(dir_i->i_sb); ++ struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); ++ struct jffs2_inode_info *dead_f = JFFS2_INODE_INFO(dentry->d_inode); + int ret; + +- c = JFFS2_SB_INFO(dir_i->i_sb); +- +- rd = jffs2_alloc_raw_dirent(); +- if (!rd) +- return -ENOMEM; +- +- ret = jffs2_reserve_space(c, sizeof(*rd)+dentry->d_name.len, &phys_ofs, &alloclen, ALLOC_NORMAL); +- if (ret) { +- jffs2_free_raw_dirent(rd); ++ ret = jffs2_do_unlink(c, dir_f, dentry->d_name.name, ++ dentry->d_name.len, dead_f); ++ if (dead_f->inocache) ++ dentry->d_inode->i_nlink = dead_f->inocache->nlink; + return ret; +- } +- +- dir_f = JFFS2_INODE_INFO(dir_i); +- down(&dir_f->sem); +- +- /* Build a deletion node */ +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + dentry->d_name.len; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = old_dentry->d_inode->i_ino; +- rd->mctime = CURRENT_TIME; +- rd->nsize = dentry->d_name.len; +- +- /* XXX: This is ugly. */ +- rd->type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; +- if (!rd->type) rd->type = DT_REG; +- +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, dentry->d_name.len); +- +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, dentry->d_name.len, phys_ofs, NULL); +- +- jffs2_complete_reservation(c); +- jffs2_free_raw_dirent(rd); +- +- if (IS_ERR(fd)) { +- up(&dir_f->sem); +- return PTR_ERR(fd); +- } +- +- /* File it. This will mark the old one obsolete. */ +- jffs2_add_fd_to_list(c, fd, &dir_f->dents); +- up(&dir_f->sem); +- +- if (!rename) { +- f = JFFS2_INODE_INFO(old_dentry->d_inode); +- down(&f->sem); +- old_dentry->d_inode->i_nlink = ++f->inocache->nlink; +- up(&f->sem); +- } +- return 0; + } ++/***********************************************************************/ ++ + + static int jffs2_link (struct dentry *old_dentry, struct inode *dir_i, struct dentry *dentry) + { ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dentry->d_inode->i_sb); ++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); ++ struct jffs2_inode_info *dir_f = JFFS2_INODE_INFO(dir_i); + int ret; ++ uint8_t type; + +- /* Can't link a bad inode. */ +- if (!JFFS2_INODE_INFO(old_dentry->d_inode)->inocache) ++ /* Don't let people make hard links to bad inodes. */ ++ if (!f->inocache) + return -EIO; + + if (S_ISDIR(old_dentry->d_inode->i_mode)) + return -EPERM; + +- ret = jffs2_do_link(old_dentry, dir_i, dentry, 0); ++ /* XXX: This is ugly */ ++ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; ++ if (!type) type = DT_REG; ++ ++ ret = jffs2_do_link(c, dir_f, f->inocache->ino, type, dentry->d_name.name, dentry->d_name.len); ++ + if (!ret) { ++ down(&f->sem); ++ old_dentry->d_inode->i_nlink = ++f->inocache->nlink; ++ up(&f->sem); + d_instantiate(dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); + } +@@ -517,8 +296,7 @@ + struct jffs2_full_dnode *fn; + struct jffs2_full_dirent *fd; + int namelen; +- __u32 alloclen, phys_ofs; +- __u32 writtenlen; ++ uint32_t alloclen, phys_ofs; + int ret; + + /* FIXME: If you care. We'd need to use frags for the target +@@ -556,15 +334,16 @@ + + f = JFFS2_INODE_INFO(inode); + +- inode->i_size = ri->isize = ri->dsize = ri->csize = strlen(target); +- ri->totlen = sizeof(*ri) + ri->dsize; +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); ++ inode->i_size = strlen(target); ++ ri->isize = ri->dsize = ri->csize = cpu_to_je32(inode->i_size); ++ ri->totlen = cpu_to_je32(sizeof(*ri) + inode->i_size); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); + + ri->compr = JFFS2_COMPR_NONE; +- ri->data_crc = crc32(0, target, strlen(target)); +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); ++ ri->data_crc = cpu_to_je32(crc32(0, target, strlen(target))); ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + +- fn = jffs2_write_dnode(inode, ri, target, strlen(target), phys_ofs, &writtenlen); ++ fn = jffs2_write_dnode(c, f, ri, target, strlen(target), phys_ofs, ALLOC_NORMAL); + + jffs2_free_raw_inode(ri); + +@@ -581,13 +360,6 @@ + f->metadata = fn; + up(&f->sem); + +- /* Work out where to put the dirent node now. */ +- writtenlen = (writtenlen+3)&~3; +- phys_ofs += writtenlen; +- alloclen -= writtenlen; +- +- if (alloclen < sizeof(*rd)+namelen) { +- /* Not enough space left in this chunk. Get some more */ + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { +@@ -595,7 +367,6 @@ + jffs2_clear_inode(inode); + return ret; + } +- } + + rd = jffs2_alloc_raw_dirent(); + if (!rd) { +@@ -608,41 +379,42 @@ + dir_f = JFFS2_INODE_INFO(dir_i); + down(&dir_f->sem); + +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + namelen; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = inode->i_ino; +- rd->mctime = CURRENT_TIME; ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_i->i_ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(inode->i_ino); ++ rd->mctime = cpu_to_je32(get_seconds()); + rd->nsize = namelen; + rd->type = DT_LNK; +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, namelen); ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); + +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); +- +- jffs2_complete_reservation(c); ++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); + + if (IS_ERR(fd)) { + /* dirent failed to write. Delete the inode normally + as if it were the final unlink() */ ++ jffs2_complete_reservation(c); + jffs2_free_raw_dirent(rd); + up(&dir_f->sem); + jffs2_clear_inode(inode); + return PTR_ERR(fd); + } + +- dir_i->i_mtime = dir_i->i_ctime = rd->mctime; ++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); + + jffs2_free_raw_dirent(rd); + + /* Link the fd into the inode's list, obsoleting an old + one if necessary. */ + jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ + up(&dir_f->sem); ++ jffs2_complete_reservation(c); + + d_instantiate(dentry, inode); + return 0; +@@ -659,8 +431,7 @@ + struct jffs2_full_dnode *fn; + struct jffs2_full_dirent *fd; + int namelen; +- __u32 alloclen, phys_ofs; +- __u32 writtenlen; ++ uint32_t alloclen, phys_ofs; + int ret; + + mode |= S_IFDIR; +@@ -692,13 +463,15 @@ + + inode->i_op = &jffs2_dir_inode_operations; + inode->i_fop = &jffs2_dir_operations; ++ /* Directories get nlink 2 at start */ ++ inode->i_nlink = 2; + + f = JFFS2_INODE_INFO(inode); + +- ri->data_crc = 0; +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); ++ ri->data_crc = cpu_to_je32(0); ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + +- fn = jffs2_write_dnode(inode, ri, NULL, 0, phys_ofs, &writtenlen); ++ fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); + + jffs2_free_raw_inode(ri); + +@@ -715,13 +488,6 @@ + f->metadata = fn; + up(&f->sem); + +- /* Work out where to put the dirent node now. */ +- writtenlen = PAD(writtenlen); +- phys_ofs += writtenlen; +- alloclen -= writtenlen; +- +- if (alloclen < sizeof(*rd)+namelen) { +- /* Not enough space left in this chunk. Get some more */ + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { +@@ -729,7 +495,6 @@ + jffs2_clear_inode(inode); + return ret; + } +- } + + rd = jffs2_alloc_raw_dirent(); + if (!rd) { +@@ -742,41 +507,43 @@ + dir_f = JFFS2_INODE_INFO(dir_i); + down(&dir_f->sem); + +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + namelen; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = inode->i_ino; +- rd->mctime = CURRENT_TIME; ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_i->i_ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(inode->i_ino); ++ rd->mctime = cpu_to_je32(get_seconds()); + rd->nsize = namelen; + rd->type = DT_DIR; +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, namelen); ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); + +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); +- +- jffs2_complete_reservation(c); ++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); + + if (IS_ERR(fd)) { + /* dirent failed to write. Delete the inode normally + as if it were the final unlink() */ ++ jffs2_complete_reservation(c); + jffs2_free_raw_dirent(rd); + up(&dir_f->sem); + jffs2_clear_inode(inode); + return PTR_ERR(fd); + } + +- dir_i->i_mtime = dir_i->i_ctime = rd->mctime; ++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); ++ dir_i->i_nlink++; + + jffs2_free_raw_dirent(rd); + + /* Link the fd into the inode's list, obsoleting an old + one if necessary. */ + jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ + up(&dir_f->sem); ++ jffs2_complete_reservation(c); + + d_instantiate(dentry, inode); + return 0; +@@ -786,15 +553,19 @@ + { + struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); + struct jffs2_full_dirent *fd; ++ int ret; + + for (fd = f->dents ; fd; fd = fd->next) { + if (fd->ino) + return -ENOTEMPTY; + } +- return jffs2_unlink(dir_i, dentry); ++ ret = jffs2_unlink(dir_i, dentry); ++ if (!ret) ++ dir_i->i_nlink--; ++ return ret; + } + +-static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, int rdev) ++static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, mknod_arg_t rdev) + { + struct jffs2_inode_info *f, *dir_f; + struct jffs2_sb_info *c; +@@ -804,12 +575,14 @@ + struct jffs2_full_dnode *fn; + struct jffs2_full_dirent *fd; + int namelen; +- unsigned short dev; ++ jint16_t dev; + int devlen = 0; +- __u32 alloclen, phys_ofs; +- __u32 writtenlen; ++ uint32_t alloclen, phys_ofs; + int ret; + ++ if (!old_valid_dev(rdev)) ++ return -EINVAL; ++ + ri = jffs2_alloc_raw_inode(); + if (!ri) + return -ENOMEM; +@@ -817,7 +590,7 @@ + c = JFFS2_SB_INFO(dir_i->i_sb); + + if (S_ISBLK(mode) || S_ISCHR(mode)) { +- dev = (MAJOR(to_kdev_t(rdev)) << 8) | MINOR(to_kdev_t(rdev)); ++ dev = cpu_to_je16(old_encode_dev(rdev)); + devlen = sizeof(dev); + } + +@@ -844,15 +617,15 @@ + + f = JFFS2_INODE_INFO(inode); + +- ri->dsize = ri->csize = devlen; +- ri->totlen = sizeof(*ri) + ri->csize; +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); ++ ri->dsize = ri->csize = cpu_to_je32(devlen); ++ ri->totlen = cpu_to_je32(sizeof(*ri) + devlen); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); + + ri->compr = JFFS2_COMPR_NONE; +- ri->data_crc = crc32(0, &dev, devlen); +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); ++ ri->data_crc = cpu_to_je32(crc32(0, &dev, devlen)); ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); + +- fn = jffs2_write_dnode(inode, ri, (char *)&dev, devlen, phys_ofs, &writtenlen); ++ fn = jffs2_write_dnode(c, f, ri, (char *)&dev, devlen, phys_ofs, ALLOC_NORMAL); + + jffs2_free_raw_inode(ri); + +@@ -869,13 +642,6 @@ + f->metadata = fn; + up(&f->sem); + +- /* Work out where to put the dirent node now. */ +- writtenlen = (writtenlen+3)&~3; +- phys_ofs += writtenlen; +- alloclen -= writtenlen; +- +- if (alloclen < sizeof(*rd)+namelen) { +- /* Not enough space left in this chunk. Get some more */ + jffs2_complete_reservation(c); + ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); + if (ret) { +@@ -883,7 +649,6 @@ + jffs2_clear_inode(inode); + return ret; + } +- } + + rd = jffs2_alloc_raw_dirent(); + if (!rd) { +@@ -896,44 +661,45 @@ + dir_f = JFFS2_INODE_INFO(dir_i); + down(&dir_f->sem); + +- rd->magic = JFFS2_MAGIC_BITMASK; +- rd->nodetype = JFFS2_NODETYPE_DIRENT; +- rd->totlen = sizeof(*rd) + namelen; +- rd->hdr_crc = crc32(0, rd, sizeof(struct jffs2_unknown_node)-4); +- +- rd->pino = dir_i->i_ino; +- rd->version = ++dir_f->highest_version; +- rd->ino = inode->i_ino; +- rd->mctime = CURRENT_TIME; ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_i->i_ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(inode->i_ino); ++ rd->mctime = cpu_to_je32(get_seconds()); + rd->nsize = namelen; + + /* XXX: This is ugly. */ + rd->type = (mode & S_IFMT) >> 12; + +- rd->node_crc = crc32(0, rd, sizeof(*rd)-8); +- rd->name_crc = crc32(0, dentry->d_name.name, namelen); +- +- fd = jffs2_write_dirent(dir_i, rd, dentry->d_name.name, namelen, phys_ofs, &writtenlen); ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, dentry->d_name.name, namelen)); + +- jffs2_complete_reservation(c); ++ fd = jffs2_write_dirent(c, dir_f, rd, dentry->d_name.name, namelen, phys_ofs, ALLOC_NORMAL); + + if (IS_ERR(fd)) { + /* dirent failed to write. Delete the inode normally + as if it were the final unlink() */ ++ jffs2_complete_reservation(c); + jffs2_free_raw_dirent(rd); + up(&dir_f->sem); + jffs2_clear_inode(inode); + return PTR_ERR(fd); + } + +- dir_i->i_mtime = dir_i->i_ctime = rd->mctime; ++ dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); + + jffs2_free_raw_dirent(rd); + + /* Link the fd into the inode's list, obsoleting an old + one if necessary. */ + jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ + up(&dir_f->sem); ++ jffs2_complete_reservation(c); + + d_instantiate(dentry, inode); + +@@ -944,7 +710,9 @@ + struct inode *new_dir_i, struct dentry *new_dentry) + { + int ret; ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(old_dir_i->i_sb); + struct jffs2_inode_info *victim_f = NULL; ++ uint8_t type; + + /* The VFS will check for us and prevent trying to rename a + * file over a directory and vice versa, but if it's a directory, +@@ -973,7 +741,15 @@ + */ + + /* Make a hard link */ +- ret = jffs2_do_link(old_dentry, new_dir_i, new_dentry, 1); ++ ++ /* XXX: This is ugly */ ++ type = (old_dentry->d_inode->i_mode & S_IFMT) >> 12; ++ if (!type) type = DT_REG; ++ ++ ret = jffs2_do_link(c, JFFS2_INODE_INFO(new_dir_i), ++ old_dentry->d_inode->i_ino, type, ++ new_dentry->d_name.name, new_dentry->d_name.len); ++ + if (ret) + return ret; + +@@ -989,22 +765,36 @@ + } + } + ++ /* If it was a directory we moved, and there was no victim, ++ increase i_nlink on its new parent */ ++ if (S_ISDIR(old_dentry->d_inode->i_mode) && !victim_f) ++ new_dir_i->i_nlink++; ++ + /* Unlink the original */ +- ret = jffs2_do_unlink(old_dir_i, old_dentry, 1); ++ ret = jffs2_do_unlink(c, JFFS2_INODE_INFO(old_dir_i), ++ old_dentry->d_name.name, old_dentry->d_name.len, NULL); ++ ++ /* We don't touch inode->i_nlink */ + + if (ret) { + /* Oh shit. We really ought to make a single node which can do both atomically */ + struct jffs2_inode_info *f = JFFS2_INODE_INFO(old_dentry->d_inode); + down(&f->sem); ++ old_dentry->d_inode->i_nlink++; + if (f->inocache) +- old_dentry->d_inode->i_nlink = f->inocache->nlink++; ++ f->inocache->nlink++; + up(&f->sem); + + printk(KERN_NOTICE "jffs2_rename(): Link succeeded, unlink failed (err %d). You now have a hard link\n", ret); + /* Might as well let the VFS know */ + d_instantiate(new_dentry, old_dentry->d_inode); + atomic_inc(&old_dentry->d_inode->i_count); +- } + return ret; ++ } ++ ++ if (S_ISDIR(old_dentry->d_inode->i_mode)) ++ old_dir_i->i_nlink--; ++ ++ return 0; + } + +diff -Nurb linux-mips-2.4.27/fs/jffs2/erase.c linux/fs/jffs2/erase.c +--- linux-mips-2.4.27/fs/jffs2/erase.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/erase.c 2004-11-19 10:25:12.099169392 +0100 +@@ -1,68 +1,60 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: erase.c,v 1.24.2.1 2003/11/02 13:51:17 dwmw2 Exp $ ++ * $Id: erase.c,v 1.58 2003/11/26 13:02:46 dwmw2 Exp $ + * + */ ++ + #include <linux/kernel.h> + #include <linux/slab.h> + #include <linux/mtd/mtd.h> +-#include <linux/jffs2.h> +-#include <linux/interrupt.h> +-#include "nodelist.h" ++#include <linux/compiler.h> + #include <linux/crc32.h> ++#include <linux/sched.h> ++#include <linux/pagemap.h> ++#include "nodelist.h" + + struct erase_priv_struct { + struct jffs2_eraseblock *jeb; + struct jffs2_sb_info *c; + }; + ++#ifndef __ECOS + static void jffs2_erase_callback(struct erase_info *); ++#endif ++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); + static void jffs2_free_all_node_refs(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); + + void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) + { +- struct erase_info *instr; + int ret; ++#ifdef __ECOS ++ ret = jffs2_flash_erase(c, jeb); ++ if (!ret) { ++ jffs2_erase_succeeded(c, jeb); ++ return; ++ } ++#else /* Linux */ ++ struct erase_info *instr; + + instr = kmalloc(sizeof(struct erase_info) + sizeof(struct erase_priv_struct), GFP_KERNEL); + if (!instr) { + printk(KERN_WARNING "kmalloc for struct erase_info in jffs2_erase_block failed. Refiling block for later\n"); +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + list_del(&jeb->list); + list_add(&jeb->list, &c->erase_pending_list); + c->erasing_size -= c->sector_size; +- spin_unlock_bh(&c->erase_completion_lock); ++ c->dirty_size += c->sector_size; ++ jeb->dirty_size = c->sector_size; ++ spin_unlock(&c->erase_completion_lock); + return; + } + +@@ -77,19 +69,27 @@ + ((struct erase_priv_struct *)instr->priv)->jeb = jeb; + ((struct erase_priv_struct *)instr->priv)->c = c; + ++ /* NAND , read out the fail counter, if possible */ ++ if (!jffs2_can_mark_obsolete(c)) ++ jffs2_nand_read_failcnt(c,jeb); ++ + ret = c->mtd->erase(c->mtd, instr); +- if (!ret) { ++ if (!ret) + return; +- } ++ ++ kfree(instr); ++#endif /* __ECOS */ ++ + if (ret == -ENOMEM || ret == -EAGAIN) { + /* Erase failed immediately. Refile it on the list */ + D1(printk(KERN_DEBUG "Erase at 0x%08x failed: %d. Refiling on erase_pending_list\n", jeb->offset, ret)); +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + list_del(&jeb->list); + list_add(&jeb->list, &c->erase_pending_list); + c->erasing_size -= c->sector_size; +- spin_unlock_bh(&c->erase_completion_lock); +- kfree(instr); ++ c->dirty_size += c->sector_size; ++ jeb->dirty_size = c->sector_size; ++ spin_unlock(&c->erase_completion_lock); + return; + } + +@@ -97,74 +97,101 @@ + printk(KERN_WARNING "Erase at 0x%08x failed immediately: -EROFS. Is the sector locked?\n", jeb->offset); + else + printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret); +- spin_lock_bh(&c->erase_completion_lock); +- list_del(&jeb->list); +- list_add(&jeb->list, &c->bad_list); +- c->nr_erasing_blocks--; +- c->bad_size += c->sector_size; +- c->erasing_size -= c->sector_size; +- spin_unlock_bh(&c->erase_completion_lock); +- wake_up(&c->erase_wait); +- kfree(instr); ++ ++ jffs2_erase_failed(c, jeb); + } + +-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) ++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count) + { + struct jffs2_eraseblock *jeb; + +- spin_lock_bh(&c->erase_completion_lock); +- while (!list_empty(&c->erase_pending_list)) { ++ down(&c->erase_free_sem); + +- jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); ++ spin_lock(&c->erase_completion_lock); + +- D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); ++ while (!list_empty(&c->erase_complete_list) || ++ !list_empty(&c->erase_pending_list)) { + ++ if (!list_empty(&c->erase_complete_list)) { ++ jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); ++ list_del(&jeb->list); ++ spin_unlock(&c->erase_completion_lock); ++ jffs2_mark_erased_block(c, jeb); ++ ++ if (!--count) { ++ D1(printk(KERN_DEBUG "Count reached. jffs2_erase_pending_blocks leaving\n")); ++ goto done; ++ } ++ ++ } else if (!list_empty(&c->erase_pending_list)) { ++ jeb = list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list); ++ D1(printk(KERN_DEBUG "Starting erase of pending block 0x%08x\n", jeb->offset)); + list_del(&jeb->list); + c->erasing_size += c->sector_size; ++ c->wasted_size -= jeb->wasted_size; + c->free_size -= jeb->free_size; + c->used_size -= jeb->used_size; + c->dirty_size -= jeb->dirty_size; +- jeb->used_size = jeb->dirty_size = jeb->free_size = 0; ++ jeb->wasted_size = jeb->used_size = jeb->dirty_size = jeb->free_size = 0; + jffs2_free_all_node_refs(c, jeb); + list_add(&jeb->list, &c->erasing_list); +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + + jffs2_erase_block(c, jeb); ++ ++ } else { ++ BUG(); ++ } ++ + /* Be nice */ +- if (current->need_resched) +- schedule(); +- spin_lock_bh(&c->erase_completion_lock); ++ cond_resched(); ++ spin_lock(&c->erase_completion_lock); + } +- spin_unlock_bh(&c->erase_completion_lock); ++ ++ spin_unlock(&c->erase_completion_lock); ++ done: + D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); ++ ++ up(&c->erase_free_sem); ++} ++ ++static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", jeb->offset)); ++ spin_lock(&c->erase_completion_lock); ++ list_del(&jeb->list); ++ list_add_tail(&jeb->list, &c->erase_complete_list); ++ spin_unlock(&c->erase_completion_lock); ++ /* Ensure that kupdated calls us again to mark them clean */ ++ jffs2_erase_pending_trigger(c); + } + ++static void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ spin_lock(&c->erase_completion_lock); ++ c->erasing_size -= c->sector_size; ++ c->bad_size += c->sector_size; ++ list_del(&jeb->list); ++ list_add(&jeb->list, &c->bad_list); ++ c->nr_erasing_blocks--; ++ spin_unlock(&c->erase_completion_lock); ++ wake_up(&c->erase_wait); ++} + ++#ifndef __ECOS + static void jffs2_erase_callback(struct erase_info *instr) + { + struct erase_priv_struct *priv = (void *)instr->priv; + + if(instr->state != MTD_ERASE_DONE) { + printk(KERN_WARNING "Erase at 0x%08x finished, but state != MTD_ERASE_DONE. State is 0x%x instead.\n", instr->addr, instr->state); +- spin_lock(&priv->c->erase_completion_lock); +- priv->c->erasing_size -= priv->c->sector_size; +- priv->c->bad_size += priv->c->sector_size; +- list_del(&priv->jeb->list); +- list_add(&priv->jeb->list, &priv->c->bad_list); +- priv->c->nr_erasing_blocks--; +- spin_unlock(&priv->c->erase_completion_lock); +- wake_up(&priv->c->erase_wait); ++ jffs2_erase_failed(priv->c, priv->jeb); + } else { +- D1(printk(KERN_DEBUG "Erase completed successfully at 0x%08x\n", instr->addr)); +- spin_lock(&priv->c->erase_completion_lock); +- list_del(&priv->jeb->list); +- list_add_tail(&priv->jeb->list, &priv->c->erase_complete_list); +- spin_unlock(&priv->c->erase_completion_lock); ++ jffs2_erase_succeeded(priv->c, priv->jeb); + } +- /* Make sure someone picks up the block off the erase_complete list */ +- OFNI_BS_2SFFJ(priv->c)->s_dirt = 1; + kfree(instr); + } ++#endif /* !__ECOS */ + + /* Hmmm. Maybe we should accept the extra space it takes and make + this a standard doubly-linked list? */ +@@ -221,7 +248,7 @@ + this = ic->nodes; + + while(this) { +- printk( "0x%08x(%d)->", this->flash_offset & ~3, this->flash_offset &3); ++ printk( "0x%08x(%d)->", ref_offset(this), ref_flags(this)); + if (++i == 5) { + printk("\n" KERN_DEBUG); + i=0; +@@ -256,54 +283,43 @@ + jeb->last_node = NULL; + } + +-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) ++static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) + { +- OFNI_BS_2SFFJ(c)->s_dirt = 1; +-} +- +-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c) +-{ +- static struct jffs2_unknown_node marker = {JFFS2_MAGIC_BITMASK, JFFS2_NODETYPE_CLEANMARKER, sizeof(struct jffs2_unknown_node)}; +- struct jffs2_eraseblock *jeb; +- struct jffs2_raw_node_ref *marker_ref; ++ struct jffs2_raw_node_ref *marker_ref = NULL; + unsigned char *ebuf; +- ssize_t retlen; ++ size_t retlen; + int ret; + +- marker.hdr_crc = crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4); +- +- spin_lock_bh(&c->erase_completion_lock); +- while (!list_empty(&c->erase_complete_list)) { +- jeb = list_entry(c->erase_complete_list.next, struct jffs2_eraseblock, list); +- list_del(&jeb->list); +- spin_unlock_bh(&c->erase_completion_lock); +- ++ if (!jffs2_cleanmarker_oob(c)) { + marker_ref = jffs2_alloc_raw_node_ref(); + if (!marker_ref) { + printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n"); +- /* Come back later */ ++ /* Stick it back on the list from whence it came and come back later */ + jffs2_erase_pending_trigger(c); ++ spin_lock(&c->erase_completion_lock); ++ list_add(&jeb->list, &c->erase_complete_list); ++ spin_unlock(&c->erase_completion_lock); + return; + } +- ++ } + ebuf = kmalloc(PAGE_SIZE, GFP_KERNEL); + if (!ebuf) { + printk(KERN_WARNING "Failed to allocate page buffer for verifying erase at 0x%08x. Assuming it worked\n", jeb->offset); + } else { +- __u32 ofs = jeb->offset; ++ uint32_t ofs = jeb->offset; + + D1(printk(KERN_DEBUG "Verifying erase at 0x%08x\n", jeb->offset)); + while(ofs < jeb->offset + c->sector_size) { +- __u32 readlen = min((__u32)PAGE_SIZE, jeb->offset + c->sector_size - ofs); ++ uint32_t readlen = min((uint32_t)PAGE_SIZE, jeb->offset + c->sector_size - ofs); + int i; + +- ret = c->mtd->read(c->mtd, ofs, readlen, &retlen, ebuf); +- if (ret < 0) { ++ ret = jffs2_flash_read(c, ofs, readlen, &retlen, ebuf); ++ if (ret) { + printk(KERN_WARNING "Read of newly-erased block at 0x%08x failed: %d. Putting on bad_list\n", ofs, ret); + goto bad; + } + if (retlen != readlen) { +- printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen); ++ printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); + goto bad; + } + for (i=0; i<readlen; i += sizeof(unsigned long)) { +@@ -312,62 +328,89 @@ + if (datum + 1) { + printk(KERN_WARNING "Newly-erased block contained word 0x%lx at offset 0x%08x\n", datum, ofs + i); + bad: ++ if (!jffs2_cleanmarker_oob(c)) + jffs2_free_raw_node_ref(marker_ref); ++ else ++ jffs2_write_nand_badblock( c ,jeb ); + kfree(ebuf); + bad2: +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; + c->bad_size += c->sector_size; + + list_add_tail(&jeb->list, &c->bad_list); + c->nr_erasing_blocks--; +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + wake_up(&c->erase_wait); + return; + } + } + ofs += readlen; ++ cond_resched(); + } + kfree(ebuf); + } + + /* Write the erase complete marker */ + D1(printk(KERN_DEBUG "Writing erased marker to block at 0x%08x\n", jeb->offset)); +- ret = c->mtd->write(c->mtd, jeb->offset, sizeof(marker), &retlen, (char *)&marker); ++ if (jffs2_cleanmarker_oob(c)) { ++ ++ if (jffs2_write_nand_cleanmarker(c, jeb)) ++ goto bad2; ++ ++ jeb->first_node = jeb->last_node = NULL; ++ ++ jeb->free_size = c->sector_size; ++ jeb->used_size = 0; ++ jeb->dirty_size = 0; ++ jeb->wasted_size = 0; ++ } else { ++ struct jffs2_unknown_node marker = { ++ .magic = cpu_to_je16(JFFS2_MAGIC_BITMASK), ++ .nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER), ++ .totlen = cpu_to_je32(c->cleanmarker_size) ++ }; ++ ++ marker.hdr_crc = cpu_to_je32(crc32(0, &marker, sizeof(struct jffs2_unknown_node)-4)); ++ ++ /* We only write the header; the rest was noise or padding anyway */ ++ ret = jffs2_flash_write(c, jeb->offset, sizeof(marker), &retlen, (char *)&marker); + if (ret) { + printk(KERN_WARNING "Write clean marker to block at 0x%08x failed: %d\n", + jeb->offset, ret); + goto bad2; + } + if (retlen != sizeof(marker)) { +- printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %d\n", ++ printk(KERN_WARNING "Short write to newly-erased block at 0x%08x: Wanted %d, got %zd\n", + jeb->offset, sizeof(marker), retlen); + goto bad2; + } + + marker_ref->next_in_ino = NULL; + marker_ref->next_phys = NULL; +- marker_ref->flash_offset = jeb->offset; +- marker_ref->totlen = PAD(sizeof(marker)); ++ marker_ref->flash_offset = jeb->offset | REF_NORMAL; ++ marker_ref->__totlen = c->cleanmarker_size; + + jeb->first_node = jeb->last_node = marker_ref; + +- jeb->free_size = c->sector_size - marker_ref->totlen; +- jeb->used_size = marker_ref->totlen; ++ jeb->free_size = c->sector_size - c->cleanmarker_size; ++ jeb->used_size = c->cleanmarker_size; + jeb->dirty_size = 0; ++ jeb->wasted_size = 0; ++ } + +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + c->erasing_size -= c->sector_size; + c->free_size += jeb->free_size; + c->used_size += jeb->used_size; + + ACCT_SANITY_CHECK(c,jeb); +- ACCT_PARANOIA_CHECK(jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); + + list_add_tail(&jeb->list, &c->free_list); + c->nr_erasing_blocks--; + c->nr_free_blocks++; ++ spin_unlock(&c->erase_completion_lock); + wake_up(&c->erase_wait); +- } +- spin_unlock_bh(&c->erase_completion_lock); + } ++ +diff -Nurb linux-mips-2.4.27/fs/jffs2/file.c linux/fs/jffs2/file.c +--- linux-mips-2.4.27/fs/jffs2/file.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/file.c 2004-11-19 10:25:12.101169088 +0100 +@@ -1,319 +1,106 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: file.c,v 1.58.2.7 2003/11/02 13:51:17 dwmw2 Exp $ ++ * $Id: file.c,v 1.97 2003/11/02 08:52:35 dwmw2 Exp $ + * + */ + ++#include <linux/version.h> + #include <linux/kernel.h> +-#include <linux/mtd/compatmac.h> /* for min() */ + #include <linux/slab.h> + #include <linux/fs.h> ++#include <linux/time.h> + #include <linux/pagemap.h> ++#include <linux/highmem.h> ++#include <linux/crc32.h> + #include <linux/jffs2.h> + #include "nodelist.h" +-#include <linux/crc32.h> + + extern int generic_file_open(struct inode *, struct file *) __attribute__((weak)); + extern loff_t generic_file_llseek(struct file *file, loff_t offset, int origin) __attribute__((weak)); + + +-int jffs2_null_fsync(struct file *filp, struct dentry *dentry, int datasync) ++int jffs2_fsync(struct file *filp, struct dentry *dentry, int datasync) + { +- /* Move along. Nothing to see here */ ++ struct inode *inode = dentry->d_inode; ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); ++ ++ /* Trigger GC to flush any pending writes for this inode */ ++ jffs2_flush_wbuf_gc(c, inode->i_ino); ++ + return 0; + } + + struct file_operations jffs2_file_operations = + { +- llseek: generic_file_llseek, +- open: generic_file_open, +- read: generic_file_read, +- write: generic_file_write, +- ioctl: jffs2_ioctl, +- mmap: generic_file_mmap, +- fsync: jffs2_null_fsync ++ .llseek = generic_file_llseek, ++ .open = generic_file_open, ++ .read = generic_file_read, ++ .write = generic_file_write, ++ .ioctl = jffs2_ioctl, ++ .mmap = generic_file_readonly_mmap, ++ .fsync = jffs2_fsync, ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,29) ++ .sendfile = generic_file_sendfile ++#endif + }; + + /* jffs2_file_inode_operations */ + + struct inode_operations jffs2_file_inode_operations = + { +- setattr: jffs2_setattr ++ .setattr = jffs2_setattr + }; + + struct address_space_operations jffs2_file_address_operations = + { +- readpage: jffs2_readpage, +- prepare_write: jffs2_prepare_write, +- commit_write: jffs2_commit_write ++ .readpage = jffs2_readpage, ++ .prepare_write =jffs2_prepare_write, ++ .commit_write = jffs2_commit_write + }; + +-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr) +-{ +- struct jffs2_full_dnode *old_metadata, *new_metadata; +- struct inode *inode = dentry->d_inode; +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); +- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_raw_inode *ri; +- unsigned short dev; +- unsigned char *mdata = NULL; +- int mdatalen = 0; +- unsigned int ivalid; +- __u32 phys_ofs, alloclen; +- int ret; +- D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); +- ret = inode_change_ok(inode, iattr); +- if (ret) +- return ret; +- +- /* Special cases - we don't want more than one data node +- for these types on the medium at any time. So setattr +- must read the original data associated with the node +- (i.e. the device numbers or the target name) and write +- it out again with the appropriate data attached */ +- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { +- /* For these, we don't actually need to read the old node */ +- dev = (MAJOR(to_kdev_t(dentry->d_inode->i_rdev)) << 8) | +- MINOR(to_kdev_t(dentry->d_inode->i_rdev)); +- mdata = (char *)&dev; +- mdatalen = sizeof(dev); +- D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); +- } else if (S_ISLNK(inode->i_mode)) { +- mdatalen = f->metadata->size; +- mdata = kmalloc(f->metadata->size, GFP_USER); +- if (!mdata) +- return -ENOMEM; +- ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen); +- if (ret) { +- kfree(mdata); +- return ret; +- } +- D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); +- } +- +- ri = jffs2_alloc_raw_inode(); +- if (!ri) { +- if (S_ISLNK(inode->i_mode)) +- kfree(mdata); +- return -ENOMEM; +- } +- +- ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL); +- if (ret) { +- jffs2_free_raw_inode(ri); +- if (S_ISLNK(inode->i_mode)) +- kfree(mdata); +- return ret; +- } +- down(&f->sem); +- ivalid = iattr->ia_valid; +- +- ri->magic = JFFS2_MAGIC_BITMASK; +- ri->nodetype = JFFS2_NODETYPE_INODE; +- ri->totlen = sizeof(*ri) + mdatalen; +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri->ino = inode->i_ino; +- ri->version = ++f->highest_version; +- +- ri->mode = (ivalid & ATTR_MODE)?iattr->ia_mode:inode->i_mode; +- ri->uid = (ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid; +- ri->gid = (ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid; +- +- if (ivalid & ATTR_MODE && ri->mode & S_ISGID && +- !in_group_p(ri->gid) && !capable(CAP_FSETID)) +- ri->mode &= ~S_ISGID; +- +- ri->isize = (ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size; +- ri->atime = (ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime; +- ri->mtime = (ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime; +- ri->ctime = (ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime; +- +- ri->offset = 0; +- ri->csize = ri->dsize = mdatalen; +- ri->compr = JFFS2_COMPR_NONE; +- if (inode->i_size < ri->isize) { +- /* It's an extension. Make it a hole node */ +- ri->compr = JFFS2_COMPR_ZERO; +- ri->dsize = ri->isize - inode->i_size; +- ri->offset = inode->i_size; +- } +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); +- if (mdatalen) +- ri->data_crc = crc32(0, mdata, mdatalen); +- else +- ri->data_crc = 0; +- +- new_metadata = jffs2_write_dnode(inode, ri, mdata, mdatalen, phys_ofs, NULL); +- if (S_ISLNK(inode->i_mode)) +- kfree(mdata); +- +- jffs2_complete_reservation(c); +- +- if (IS_ERR(new_metadata)) { +- jffs2_free_raw_inode(ri); +- up(&f->sem); +- return PTR_ERR(new_metadata); +- } +- /* It worked. Update the inode */ +- inode->i_atime = ri->atime; +- inode->i_ctime = ri->ctime; +- inode->i_mtime = ri->mtime; +- inode->i_mode = ri->mode; +- inode->i_uid = ri->uid; +- inode->i_gid = ri->gid; +- +- +- old_metadata = f->metadata; +- +- if (inode->i_size > ri->isize) { +- vmtruncate(inode, ri->isize); +- jffs2_truncate_fraglist (c, &f->fraglist, ri->isize); +- } +- +- if (inode->i_size < ri->isize) { +- jffs2_add_full_dnode_to_inode(c, f, new_metadata); +- inode->i_size = ri->isize; +- f->metadata = NULL; +- } else { +- f->metadata = new_metadata; +- } +- if (old_metadata) { +- jffs2_mark_node_obsolete(c, old_metadata->raw); +- jffs2_free_full_dnode(old_metadata); +- } +- jffs2_free_raw_inode(ri); +- up(&f->sem); +- return 0; +-} +- + int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg) + { + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_node_frag *frag = f->fraglist; +- __u32 offset = pg->index << PAGE_CACHE_SHIFT; +- __u32 end = offset + PAGE_CACHE_SIZE; + unsigned char *pg_buf; + int ret; + +- D1(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%x\n", inode->i_ino, offset)); ++ D2(printk(KERN_DEBUG "jffs2_do_readpage_nolock(): ino #%lu, page at offset 0x%lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT)); + + if (!PageLocked(pg)) + PAGE_BUG(pg); + +- while(frag && frag->ofs + frag->size <= offset) { +- // D1(printk(KERN_DEBUG "skipping frag %d-%d; before the region we care about\n", frag->ofs, frag->ofs + frag->size)); +- frag = frag->next; +- } +- + pg_buf = kmap(pg); ++ /* FIXME: Can kmap fail? */ + +- /* XXX FIXME: Where a single physical node actually shows up in two +- frags, we read it twice. Don't do that. */ +- /* Now we're pointing at the first frag which overlaps our page */ +- while(offset < end) { +- D2(printk(KERN_DEBUG "jffs2_readpage: offset %d, end %d\n", offset, end)); +- if (!frag || frag->ofs > offset) { +- __u32 holesize = end - offset; +- if (frag) { +- D1(printk(KERN_NOTICE "Eep. Hole in ino %ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", inode->i_ino, frag->ofs, offset)); +- holesize = min(holesize, frag->ofs - offset); +- D1(jffs2_print_frag_list(f)); +- } +- D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); +- memset(pg_buf, 0, holesize); +- pg_buf += holesize; +- offset += holesize; +- continue; +- } else if (frag->ofs < offset && (offset & (PAGE_CACHE_SIZE-1)) != 0) { +- D1(printk(KERN_NOTICE "Eep. Overlap in ino #%ld fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", +- inode->i_ino, frag->ofs, offset)); +- D1(jffs2_print_frag_list(f)); +- memset(pg_buf, 0, end - offset); +- ClearPageUptodate(pg); +- SetPageError(pg); +- kunmap(pg); +- return -EIO; +- } else if (!frag->node) { +- __u32 holeend = min(end, frag->ofs + frag->size); +- D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size)); +- memset(pg_buf, 0, holeend - offset); +- pg_buf += holeend - offset; +- offset = holeend; +- frag = frag->next; +- continue; +- } else { +- __u32 readlen; +- __u32 fragofs; /* offset within the frag to start reading */ ++ ret = jffs2_read_inode_range(c, f, pg_buf, pg->index << PAGE_CACHE_SHIFT, PAGE_CACHE_SIZE); + +- fragofs = offset - frag->ofs; +- readlen = min(frag->size - fragofs, end - offset); +- D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%x\n", frag->ofs+fragofs, +- fragofs+frag->ofs+readlen, frag->node->raw->flash_offset & ~3)); +- ret = jffs2_read_dnode(c, frag->node, pg_buf, fragofs + frag->ofs - frag->node->ofs, readlen); +- D2(printk(KERN_DEBUG "node read done\n")); + if (ret) { +- D1(printk(KERN_DEBUG"jffs2_readpage error %d\n",ret)); +- memset(pg_buf, 0, readlen); + ClearPageUptodate(pg); + SetPageError(pg); +- kunmap(pg); +- return ret; +- } +- +- pg_buf += readlen; +- offset += readlen; +- frag = frag->next; +- D2(printk(KERN_DEBUG "node read was OK. Looping\n")); +- } +- } +- D2(printk(KERN_DEBUG "readpage finishing\n")); ++ } else { + SetPageUptodate(pg); + ClearPageError(pg); ++ } + + flush_dcache_page(pg); +- + kunmap(pg); +- D1(printk(KERN_DEBUG "readpage finished\n")); ++ ++ D2(printk(KERN_DEBUG "readpage finished\n")); + return 0; + } + + int jffs2_do_readpage_unlock(struct inode *inode, struct page *pg) + { + int ret = jffs2_do_readpage_nolock(inode, pg); +- UnlockPage(pg); ++ unlock_page(pg); + return ret; + } + +@@ -333,17 +120,17 @@ + { + struct inode *inode = pg->mapping->host; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); +- __u32 pageofs = pg->index << PAGE_CACHE_SHIFT; ++ uint32_t pageofs = pg->index << PAGE_CACHE_SHIFT; + int ret = 0; + +- D1(printk(KERN_DEBUG "jffs2_prepare_write() nrpages %ld\n", inode->i_mapping->nrpages)); ++ D1(printk(KERN_DEBUG "jffs2_prepare_write()\n")); + + if (pageofs > inode->i_size) { + /* Make new hole frag from old EOF to new page */ + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); + struct jffs2_raw_inode ri; + struct jffs2_full_dnode *fn; +- __u32 phys_ofs, alloc_len; ++ uint32_t phys_ofs, alloc_len; + + D1(printk(KERN_DEBUG "Writing new hole frag 0x%x-0x%x between current EOF and new page\n", + (unsigned int)inode->i_size, pageofs)); +@@ -355,29 +142,30 @@ + down(&f->sem); + memset(&ri, 0, sizeof(ri)); + +- ri.magic = JFFS2_MAGIC_BITMASK; +- ri.nodetype = JFFS2_NODETYPE_INODE; +- ri.totlen = sizeof(ri); +- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri.ino = f->inocache->ino; +- ri.version = ++f->highest_version; +- ri.mode = inode->i_mode; +- ri.uid = inode->i_uid; +- ri.gid = inode->i_gid; +- ri.isize = max((__u32)inode->i_size, pageofs); +- ri.atime = ri.ctime = ri.mtime = CURRENT_TIME; +- ri.offset = inode->i_size; +- ri.dsize = pageofs - inode->i_size; +- ri.csize = 0; ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri)); ++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri.ino = cpu_to_je32(f->inocache->ino); ++ ri.version = cpu_to_je32(++f->highest_version); ++ ri.mode = cpu_to_jemode(inode->i_mode); ++ ri.uid = cpu_to_je16(inode->i_uid); ++ ri.gid = cpu_to_je16(inode->i_gid); ++ ri.isize = cpu_to_je32(max((uint32_t)inode->i_size, pageofs)); ++ ri.atime = ri.ctime = ri.mtime = cpu_to_je32(get_seconds()); ++ ri.offset = cpu_to_je32(inode->i_size); ++ ri.dsize = cpu_to_je32(pageofs - inode->i_size); ++ ri.csize = cpu_to_je32(0); + ri.compr = JFFS2_COMPR_ZERO; +- ri.node_crc = crc32(0, &ri, sizeof(ri)-8); +- ri.data_crc = 0; ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ++ ri.data_crc = cpu_to_je32(0); ++ ++ fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_NORMAL); + +- fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL); +- jffs2_complete_reservation(c); + if (IS_ERR(fn)) { + ret = PTR_ERR(fn); ++ jffs2_complete_reservation(c); + up(&f->sem); + return ret; + } +@@ -391,16 +179,17 @@ + D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in prepare_write, returned %d\n", ret)); + jffs2_mark_node_obsolete(c, fn->raw); + jffs2_free_full_dnode(fn); ++ jffs2_complete_reservation(c); + up(&f->sem); + return ret; + } ++ jffs2_complete_reservation(c); + inode->i_size = pageofs; + up(&f->sem); + } + +- + /* Read in the page if it wasn't already present, unless it's a whole page */ +- if (!Page_Uptodate(pg) && (start || end < PAGE_CACHE_SIZE)) { ++ if (!PageUptodate(pg) && (start || end < PAGE_CACHE_SIZE)) { + down(&f->sem); + ret = jffs2_do_readpage_nolock(inode, pg); + up(&f->sem); +@@ -417,14 +206,12 @@ + struct inode *inode = pg->mapping->host; + struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- __u32 newsize = max_t(__u32, filp->f_dentry->d_inode->i_size, (pg->index << PAGE_CACHE_SHIFT) + end); +- __u32 file_ofs = (pg->index << PAGE_CACHE_SHIFT); +- __u32 writelen = min((__u32)PAGE_CACHE_SIZE, newsize - file_ofs); + struct jffs2_raw_inode *ri; + int ret = 0; +- ssize_t writtenlen = 0; ++ uint32_t writtenlen = 0; + +- D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags)); ++ D1(printk(KERN_DEBUG "jffs2_commit_write(): ino #%lu, page at 0x%lx, range %d-%d, flags %lx\n", ++ inode->i_ino, pg->index << PAGE_CACHE_SHIFT, start, end, pg->flags)); + + if (!start && end == PAGE_CACHE_SIZE) { + /* We need to avoid deadlock with page_cache_read() in +@@ -435,109 +222,47 @@ + } + + ri = jffs2_alloc_raw_inode(); +- if (!ri) +- return -ENOMEM; +- +- while(writelen) { +- struct jffs2_full_dnode *fn; +- unsigned char *comprbuf = NULL; +- unsigned char comprtype = JFFS2_COMPR_NONE; +- __u32 phys_ofs, alloclen; +- __u32 datalen, cdatalen; + +- D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, file_ofs)); +- +- ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL); +- if (ret) { +- SetPageError(pg); +- D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); +- break; ++ if (!ri) { ++ D1(printk(KERN_DEBUG "jffs2_commit_write(): Allocation of raw inode failed\n")); ++ return -ENOMEM; + } +- down(&f->sem); +- datalen = writelen; +- cdatalen = min(alloclen - sizeof(*ri), writelen); +- +- comprbuf = kmalloc(cdatalen, GFP_KERNEL); +- if (comprbuf) { +- comprtype = jffs2_compress(page_address(pg)+ (file_ofs & (PAGE_CACHE_SIZE-1)), comprbuf, &datalen, &cdatalen); +- } +- if (comprtype == JFFS2_COMPR_NONE) { +- /* Either compression failed, or the allocation of comprbuf failed */ +- if (comprbuf) +- kfree(comprbuf); +- comprbuf = page_address(pg) + (file_ofs & (PAGE_CACHE_SIZE -1)); +- datalen = cdatalen; +- } +- /* Now comprbuf points to the data to be written, be it compressed or not. +- comprtype holds the compression type, and comprtype == JFFS2_COMPR_NONE means +- that the comprbuf doesn't need to be kfree()d. +- */ +- +- ri->magic = JFFS2_MAGIC_BITMASK; +- ri->nodetype = JFFS2_NODETYPE_INODE; +- ri->totlen = sizeof(*ri) + cdatalen; +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri->ino = inode->i_ino; +- ri->version = ++f->highest_version; +- ri->mode = inode->i_mode; +- ri->uid = inode->i_uid; +- ri->gid = inode->i_gid; +- ri->isize = max((__u32)inode->i_size, file_ofs + datalen); +- ri->atime = ri->ctime = ri->mtime = CURRENT_TIME; +- ri->offset = file_ofs; +- ri->csize = cdatalen; +- ri->dsize = datalen; +- ri->compr = comprtype; +- ri->node_crc = crc32(0, ri, sizeof(*ri)-8); +- ri->data_crc = crc32(0, comprbuf, cdatalen); +- +- fn = jffs2_write_dnode(inode, ri, comprbuf, cdatalen, phys_ofs, NULL); + +- jffs2_complete_reservation(c); ++ /* Set the fields that the generic jffs2_write_inode_range() code can't find */ ++ ri->ino = cpu_to_je32(inode->i_ino); ++ ri->mode = cpu_to_jemode(inode->i_mode); ++ ri->uid = cpu_to_je16(inode->i_uid); ++ ri->gid = cpu_to_je16(inode->i_gid); ++ ri->isize = cpu_to_je32((uint32_t)inode->i_size); ++ ri->atime = ri->ctime = ri->mtime = cpu_to_je32(get_seconds()); ++ ++ /* In 2.4, it was already kmapped by generic_file_write(). Doesn't ++ hurt to do it again. The alternative is ifdefs, which are ugly. */ ++ kmap(pg); ++ ++ ret = jffs2_write_inode_range(c, f, ri, page_address(pg) + start, ++ (pg->index << PAGE_CACHE_SHIFT) + start, ++ end - start, &writtenlen); + +- if (comprtype != JFFS2_COMPR_NONE) +- kfree(comprbuf); ++ kunmap(pg); + +- if (IS_ERR(fn)) { +- ret = PTR_ERR(fn); +- up(&f->sem); +- SetPageError(pg); +- break; +- } +- ret = jffs2_add_full_dnode_to_inode(c, f, fn); +- if (f->metadata) { +- jffs2_mark_node_obsolete(c, f->metadata->raw); +- jffs2_free_full_dnode(f->metadata); +- f->metadata = NULL; +- } +- up(&f->sem); + if (ret) { +- /* Eep */ +- D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret)); +- jffs2_mark_node_obsolete(c, fn->raw); +- jffs2_free_full_dnode(fn); ++ /* There was an error writing. */ + SetPageError(pg); +- break; + } +- inode->i_size = ri->isize; ++ ++ if (writtenlen) { ++ if (inode->i_size < (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen) { ++ inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen; + inode->i_blocks = (inode->i_size + 511) >> 9; +- inode->i_ctime = inode->i_mtime = ri->ctime; +- if (!datalen) { +- printk(KERN_WARNING "Eep. We didn't actually write any bloody data\n"); +- ret = -EIO; +- SetPageError(pg); +- break; ++ ++ inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime)); + } +- D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen)); +- writtenlen += datalen; +- file_ofs += datalen; +- writelen -= datalen; + } + + jffs2_free_raw_inode(ri); + +- if (writtenlen < end) { ++ if (start+writtenlen < end) { + /* generic_file_write has written more to the page cache than we've + actually written to the medium. Mark the page !Uptodate so that + it gets reread */ +@@ -545,13 +270,7 @@ + SetPageError(pg); + ClearPageUptodate(pg); + } +- if (writtenlen <= start) { +- /* We didn't even get to the start of the affected part */ +- ret = ret?ret:-ENOSPC; +- D1(printk(KERN_DEBUG "jffs2_commit_write(): Only %x bytes written to page. start (%x) not reached, returning %d\n", writtenlen, start, ret)); +- } +- writtenlen = min(end-start, writtenlen-start); + +- D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d. nrpages is %ld\n",writtenlen?writtenlen:ret, inode->i_mapping->nrpages)); ++ D1(printk(KERN_DEBUG "jffs2_commit_write() returning %d\n",writtenlen?writtenlen:ret)); + return writtenlen?writtenlen:ret; + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/fs.c linux/fs/jffs2/fs.c +--- linux-mips-2.4.27/fs/jffs2/fs.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/fs.c 2004-11-19 10:25:12.102168936 +0100 +@@ -0,0 +1,618 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001-2003 Red Hat, Inc. ++ * ++ * Created by David Woodhouse <dwmw2@redhat.com> ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: fs.c,v 1.37 2004/01/26 12:34:21 dwmw2 Exp $ ++ * ++ */ ++ ++#include <linux/version.h> ++#include <linux/config.h> ++#include <linux/kernel.h> ++#include <linux/sched.h> ++#include <linux/fs.h> ++#include <linux/list.h> ++#include <linux/mtd/mtd.h> ++#include <linux/pagemap.h> ++#include <linux/slab.h> ++#include <linux/vfs.h> ++#include <linux/crc32.h> ++#include "nodelist.h" ++ ++ ++static int jffs2_do_setattr (struct inode *inode, struct iattr *iattr) ++{ ++ struct jffs2_full_dnode *old_metadata, *new_metadata; ++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); ++ struct jffs2_raw_inode *ri; ++ unsigned short dev; ++ unsigned char *mdata = NULL; ++ int mdatalen = 0; ++ unsigned int ivalid; ++ uint32_t phys_ofs, alloclen; ++ int ret; ++ D1(printk(KERN_DEBUG "jffs2_setattr(): ino #%lu\n", inode->i_ino)); ++ ret = inode_change_ok(inode, iattr); ++ if (ret) ++ return ret; ++ ++ /* Special cases - we don't want more than one data node ++ for these types on the medium at any time. So setattr ++ must read the original data associated with the node ++ (i.e. the device numbers or the target name) and write ++ it out again with the appropriate data attached */ ++ if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { ++ /* For these, we don't actually need to read the old node */ ++ dev = old_encode_dev(inode->i_rdev); ++ mdata = (char *)&dev; ++ mdatalen = sizeof(dev); ++ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of kdev_t\n", mdatalen)); ++ } else if (S_ISLNK(inode->i_mode)) { ++ mdatalen = f->metadata->size; ++ mdata = kmalloc(f->metadata->size, GFP_USER); ++ if (!mdata) ++ return -ENOMEM; ++ ret = jffs2_read_dnode(c, f->metadata, mdata, 0, mdatalen); ++ if (ret) { ++ kfree(mdata); ++ return ret; ++ } ++ D1(printk(KERN_DEBUG "jffs2_setattr(): Writing %d bytes of symlink target\n", mdatalen)); ++ } ++ ++ ri = jffs2_alloc_raw_inode(); ++ if (!ri) { ++ if (S_ISLNK(inode->i_mode)) ++ kfree(mdata); ++ return -ENOMEM; ++ } ++ ++ ret = jffs2_reserve_space(c, sizeof(*ri) + mdatalen, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ if (ret) { ++ jffs2_free_raw_inode(ri); ++ if (S_ISLNK(inode->i_mode & S_IFMT)) ++ kfree(mdata); ++ return ret; ++ } ++ down(&f->sem); ++ ivalid = iattr->ia_valid; ++ ++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri->totlen = cpu_to_je32(sizeof(*ri) + mdatalen); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri->ino = cpu_to_je32(inode->i_ino); ++ ri->version = cpu_to_je32(++f->highest_version); ++ ++ ri->uid = cpu_to_je16((ivalid & ATTR_UID)?iattr->ia_uid:inode->i_uid); ++ ri->gid = cpu_to_je16((ivalid & ATTR_GID)?iattr->ia_gid:inode->i_gid); ++ ++ if (ivalid & ATTR_MODE) ++ if (iattr->ia_mode & S_ISGID && ++ !in_group_p(je16_to_cpu(ri->gid)) && !capable(CAP_FSETID)) ++ ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID); ++ else ++ ri->mode = cpu_to_jemode(iattr->ia_mode); ++ else ++ ri->mode = cpu_to_jemode(inode->i_mode); ++ ++ ++ ri->isize = cpu_to_je32((ivalid & ATTR_SIZE)?iattr->ia_size:inode->i_size); ++ ri->atime = cpu_to_je32(I_SEC((ivalid & ATTR_ATIME)?iattr->ia_atime:inode->i_atime)); ++ ri->mtime = cpu_to_je32(I_SEC((ivalid & ATTR_MTIME)?iattr->ia_mtime:inode->i_mtime)); ++ ri->ctime = cpu_to_je32(I_SEC((ivalid & ATTR_CTIME)?iattr->ia_ctime:inode->i_ctime)); ++ ++ ri->offset = cpu_to_je32(0); ++ ri->csize = ri->dsize = cpu_to_je32(mdatalen); ++ ri->compr = JFFS2_COMPR_NONE; ++ if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { ++ /* It's an extension. Make it a hole node */ ++ ri->compr = JFFS2_COMPR_ZERO; ++ ri->dsize = cpu_to_je32(iattr->ia_size - inode->i_size); ++ ri->offset = cpu_to_je32(inode->i_size); ++ } ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ++ if (mdatalen) ++ ri->data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); ++ else ++ ri->data_crc = cpu_to_je32(0); ++ ++ new_metadata = jffs2_write_dnode(c, f, ri, mdata, mdatalen, phys_ofs, ALLOC_NORMAL); ++ if (S_ISLNK(inode->i_mode)) ++ kfree(mdata); ++ ++ if (IS_ERR(new_metadata)) { ++ jffs2_complete_reservation(c); ++ jffs2_free_raw_inode(ri); ++ up(&f->sem); ++ return PTR_ERR(new_metadata); ++ } ++ /* It worked. Update the inode */ ++ inode->i_atime = ITIME(je32_to_cpu(ri->atime)); ++ inode->i_ctime = ITIME(je32_to_cpu(ri->ctime)); ++ inode->i_mtime = ITIME(je32_to_cpu(ri->mtime)); ++ inode->i_mode = jemode_to_cpu(ri->mode); ++ inode->i_uid = je16_to_cpu(ri->uid); ++ inode->i_gid = je16_to_cpu(ri->gid); ++ ++ ++ old_metadata = f->metadata; ++ ++ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) ++ jffs2_truncate_fraglist (c, &f->fragtree, iattr->ia_size); ++ ++ if (ivalid & ATTR_SIZE && inode->i_size < iattr->ia_size) { ++ jffs2_add_full_dnode_to_inode(c, f, new_metadata); ++ inode->i_size = iattr->ia_size; ++ f->metadata = NULL; ++ } else { ++ f->metadata = new_metadata; ++ } ++ if (old_metadata) { ++ jffs2_mark_node_obsolete(c, old_metadata->raw); ++ jffs2_free_full_dnode(old_metadata); ++ } ++ jffs2_free_raw_inode(ri); ++ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ ++ /* We have to do the vmtruncate() without f->sem held, since ++ some pages may be locked and waiting for it in readpage(). ++ We are protected from a simultaneous write() extending i_size ++ back past iattr->ia_size, because do_truncate() holds the ++ generic inode semaphore. */ ++ if (ivalid & ATTR_SIZE && inode->i_size > iattr->ia_size) ++ vmtruncate(inode, iattr->ia_size); ++ ++ return 0; ++} ++ ++int jffs2_setattr(struct dentry *dentry, struct iattr *iattr) ++{ ++ return jffs2_do_setattr(dentry->d_inode, iattr); ++} ++ ++int jffs2_statfs(struct super_block *sb, struct kstatfs *buf) ++{ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); ++ unsigned long avail; ++ ++ buf->f_type = JFFS2_SUPER_MAGIC; ++ buf->f_bsize = 1 << PAGE_SHIFT; ++ buf->f_blocks = c->flash_size >> PAGE_SHIFT; ++ buf->f_files = 0; ++ buf->f_ffree = 0; ++ buf->f_namelen = JFFS2_MAX_NAME_LEN; ++ ++ spin_lock(&c->erase_completion_lock); ++ ++ avail = c->dirty_size + c->free_size; ++ if (avail > c->sector_size * c->resv_blocks_write) ++ avail -= c->sector_size * c->resv_blocks_write; ++ else ++ avail = 0; ++ ++ buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; ++ ++ D1(jffs2_dump_block_lists(c)); ++ ++ spin_unlock(&c->erase_completion_lock); ++ ++ return 0; ++} ++ ++ ++void jffs2_clear_inode (struct inode *inode) ++{ ++ /* We can forget about this inode for now - drop all ++ * the nodelists associated with it, etc. ++ */ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); ++ struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); ++ ++ D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); ++ ++ jffs2_do_clear_inode(c, f); ++} ++ ++void jffs2_read_inode (struct inode *inode) ++{ ++ struct jffs2_inode_info *f; ++ struct jffs2_sb_info *c; ++ struct jffs2_raw_inode latest_node; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); ++ ++ f = JFFS2_INODE_INFO(inode); ++ c = JFFS2_SB_INFO(inode->i_sb); ++ ++ jffs2_init_inode_info(f); ++ ++ ret = jffs2_do_read_inode(c, f, inode->i_ino, &latest_node); ++ ++ if (ret) { ++ make_bad_inode(inode); ++ up(&f->sem); ++ return; ++ } ++ inode->i_mode = jemode_to_cpu(latest_node.mode); ++ inode->i_uid = je16_to_cpu(latest_node.uid); ++ inode->i_gid = je16_to_cpu(latest_node.gid); ++ inode->i_size = je32_to_cpu(latest_node.isize); ++ inode->i_atime = ITIME(je32_to_cpu(latest_node.atime)); ++ inode->i_mtime = ITIME(je32_to_cpu(latest_node.mtime)); ++ inode->i_ctime = ITIME(je32_to_cpu(latest_node.ctime)); ++ ++ inode->i_nlink = f->inocache->nlink; ++ ++ inode->i_blksize = PAGE_SIZE; ++ inode->i_blocks = (inode->i_size + 511) >> 9; ++ ++ switch (inode->i_mode & S_IFMT) { ++ jint16_t rdev; ++ ++ case S_IFLNK: ++ inode->i_op = &jffs2_symlink_inode_operations; ++ break; ++ ++ case S_IFDIR: ++ { ++ struct jffs2_full_dirent *fd; ++ ++ for (fd=f->dents; fd; fd = fd->next) { ++ if (fd->type == DT_DIR && fd->ino) ++ inode->i_nlink++; ++ } ++ /* and '..' */ ++ inode->i_nlink++; ++ /* Root dir gets i_nlink 3 for some reason */ ++ if (inode->i_ino == 1) ++ inode->i_nlink++; ++ ++ inode->i_op = &jffs2_dir_inode_operations; ++ inode->i_fop = &jffs2_dir_operations; ++ break; ++ } ++ case S_IFREG: ++ inode->i_op = &jffs2_file_inode_operations; ++ inode->i_fop = &jffs2_file_operations; ++ inode->i_mapping->a_ops = &jffs2_file_address_operations; ++ inode->i_mapping->nrpages = 0; ++ break; ++ ++ case S_IFBLK: ++ case S_IFCHR: ++ /* Read the device numbers from the media */ ++ D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); ++ if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { ++ /* Eep */ ++ printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ make_bad_inode(inode); ++ return; ++ } ++ ++ case S_IFSOCK: ++ case S_IFIFO: ++ inode->i_op = &jffs2_file_inode_operations; ++ init_special_inode(inode, inode->i_mode, ++ old_decode_dev((je16_to_cpu(rdev)))); ++ break; ++ ++ default: ++ printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu\n", inode->i_mode, (unsigned long)inode->i_ino); ++ } ++ ++ up(&f->sem); ++ ++ D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); ++} ++ ++void jffs2_dirty_inode(struct inode *inode) ++{ ++ struct iattr iattr; ++ ++ if (!(inode->i_state & I_DIRTY_DATASYNC)) { ++ D2(printk(KERN_DEBUG "jffs2_dirty_inode() not calling setattr() for ino #%lu\n", inode->i_ino)); ++ return; ++ } ++ ++ D1(printk(KERN_DEBUG "jffs2_dirty_inode() calling setattr() for ino #%lu\n", inode->i_ino)); ++ ++ iattr.ia_valid = ATTR_MODE|ATTR_UID|ATTR_GID|ATTR_ATIME|ATTR_MTIME|ATTR_CTIME; ++ iattr.ia_mode = inode->i_mode; ++ iattr.ia_uid = inode->i_uid; ++ iattr.ia_gid = inode->i_gid; ++ iattr.ia_atime = inode->i_atime; ++ iattr.ia_mtime = inode->i_mtime; ++ iattr.ia_ctime = inode->i_ctime; ++ ++ jffs2_do_setattr(inode, &iattr); ++} ++ ++int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) ++{ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); ++ ++ if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) ++ return -EROFS; ++ ++ /* We stop if it was running, then restart if it needs to. ++ This also catches the case where it was stopped and this ++ is just a remount to restart it */ ++ if (!(sb->s_flags & MS_RDONLY)) ++ jffs2_stop_garbage_collect_thread(c); ++ ++ if (!(*flags & MS_RDONLY)) ++ jffs2_start_garbage_collect_thread(c); ++ ++ sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY); ++ ++ return 0; ++} ++ ++void jffs2_write_super (struct super_block *sb) ++{ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); ++ sb->s_dirt = 0; ++ ++ if (sb->s_flags & MS_RDONLY) ++ return; ++ ++ D1(printk(KERN_DEBUG "jffs2_write_super()\n")); ++ jffs2_garbage_collect_trigger(c); ++ jffs2_erase_pending_blocks(c, 0); ++ jffs2_flush_wbuf_gc(c, 0); ++} ++ ++ ++/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, ++ fill in the raw_inode while you're at it. */ ++struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) ++{ ++ struct inode *inode; ++ struct super_block *sb = dir_i->i_sb; ++ struct jffs2_sb_info *c; ++ struct jffs2_inode_info *f; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode)); ++ ++ c = JFFS2_SB_INFO(sb); ++ ++ inode = new_inode(sb); ++ ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ ++ f = JFFS2_INODE_INFO(inode); ++ jffs2_init_inode_info(f); ++ ++ memset(ri, 0, sizeof(*ri)); ++ /* Set OS-specific defaults for new inodes */ ++ ri->uid = cpu_to_je16(current->fsuid); ++ ++ if (dir_i->i_mode & S_ISGID) { ++ ri->gid = cpu_to_je16(dir_i->i_gid); ++ if (S_ISDIR(mode)) ++ mode |= S_ISGID; ++ } else { ++ ri->gid = cpu_to_je16(current->fsgid); ++ } ++ ri->mode = cpu_to_jemode(mode); ++ ret = jffs2_do_new_inode (c, f, mode, ri); ++ if (ret) { ++ make_bad_inode(inode); ++ iput(inode); ++ return ERR_PTR(ret); ++ } ++ inode->i_nlink = 1; ++ inode->i_ino = je32_to_cpu(ri->ino); ++ inode->i_mode = jemode_to_cpu(ri->mode); ++ inode->i_gid = je16_to_cpu(ri->gid); ++ inode->i_uid = je16_to_cpu(ri->uid); ++ inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; ++ ri->atime = ri->mtime = ri->ctime = cpu_to_je32(I_SEC(inode->i_mtime)); ++ ++ inode->i_blksize = PAGE_SIZE; ++ inode->i_blocks = 0; ++ inode->i_size = 0; ++ ++ insert_inode_hash(inode); ++ ++ return inode; ++} ++ ++ ++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent) ++{ ++ struct jffs2_sb_info *c; ++ struct inode *root_i; ++ int ret; ++ size_t blocks; ++ ++ c = JFFS2_SB_INFO(sb); ++ ++ c->flash_size = c->mtd->size; ++ ++ /* ++ * Check, if we have to concatenate physical blocks to larger virtual blocks ++ * to reduce the memorysize for c->blocks. (kmalloc allows max. 128K allocation) ++ */ ++ blocks = c->flash_size / c->mtd->erasesize; ++ while ((blocks * sizeof (struct jffs2_eraseblock)) > (128 * 1024)) ++ blocks >>= 1; ++ ++ c->sector_size = c->flash_size / blocks; ++ if (c->sector_size != c->mtd->erasesize) ++ printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using virtual blocks size (%dKiB) instead\n", ++ c->mtd->erasesize / 1024, c->sector_size / 1024); ++ ++ if (c->flash_size < 5*c->sector_size) { ++ printk(KERN_ERR "jffs2: Too few erase blocks (%d)\n", c->flash_size / c->sector_size); ++ return -EINVAL; ++ } ++ ++ c->cleanmarker_size = sizeof(struct jffs2_unknown_node); ++ /* Joern -- stick alignment for weird 8-byte-page flash here */ ++ ++ if (jffs2_cleanmarker_oob(c)) { ++ /* NAND (or other bizarre) flash... do setup accordingly */ ++ ret = jffs2_nand_flash_setup(c); ++ if (ret) ++ return ret; ++ } ++ ++ c->inocache_list = kmalloc(INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *), GFP_KERNEL); ++ if (!c->inocache_list) { ++ ret = -ENOMEM; ++ goto out_wbuf; ++ } ++ memset(c->inocache_list, 0, INOCACHE_HASHSIZE * sizeof(struct jffs2_inode_cache *)); ++ ++ if ((ret = jffs2_do_mount_fs(c))) ++ goto out_inohash; ++ ++ ret = -EINVAL; ++ ++ D1(printk(KERN_DEBUG "jffs2_do_fill_super(): Getting root inode\n")); ++ root_i = iget(sb, 1); ++ if (is_bad_inode(root_i)) { ++ D1(printk(KERN_WARNING "get root inode failed\n")); ++ goto out_nodes; ++ } ++ ++ D1(printk(KERN_DEBUG "jffs2_do_fill_super(): d_alloc_root()\n")); ++ sb->s_root = d_alloc_root(root_i); ++ if (!sb->s_root) ++ goto out_root_i; ++ ++#if LINUX_VERSION_CODE >= 0x20403 ++ sb->s_maxbytes = 0xFFFFFFFF; ++#endif ++ sb->s_blocksize = PAGE_CACHE_SIZE; ++ sb->s_blocksize_bits = PAGE_CACHE_SHIFT; ++ sb->s_magic = JFFS2_SUPER_MAGIC; ++ if (!(sb->s_flags & MS_RDONLY)) ++ jffs2_start_garbage_collect_thread(c); ++ return 0; ++ ++ out_root_i: ++ iput(root_i); ++ out_nodes: ++ jffs2_free_ino_caches(c); ++ jffs2_free_raw_node_refs(c); ++ kfree(c->blocks); ++ out_inohash: ++ kfree(c->inocache_list); ++ out_wbuf: ++ jffs2_nand_flash_cleanup(c); ++ ++ return ret; ++} ++ ++void jffs2_gc_release_inode(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f) ++{ ++ iput(OFNI_EDONI_2SFFJ(f)); ++} ++ ++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, ++ int inum, int nlink) ++{ ++ struct inode *inode; ++ struct jffs2_inode_cache *ic; ++ if (!nlink) { ++ /* The inode has zero nlink but its nodes weren't yet marked ++ obsolete. This has to be because we're still waiting for ++ the final (close() and) iput() to happen. ++ ++ There's a possibility that the final iput() could have ++ happened while we were contemplating. In order to ensure ++ that we don't cause a new read_inode() (which would fail) ++ for the inode in question, we use ilookup() in this case ++ instead of iget(). ++ ++ The nlink can't _become_ zero at this point because we're ++ holding the alloc_sem, and jffs2_do_unlink() would also ++ need that while decrementing nlink on any inode. ++ */ ++ inode = ilookup(OFNI_BS_2SFFJ(c), inum); ++ if (!inode) { ++ D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", ++ inum)); ++ ++ spin_lock(&c->inocache_lock); ++ ic = jffs2_get_ino_cache(c, inum); ++ if (!ic) { ++ D1(printk(KERN_DEBUG "Inode cache for ino #%u is gone.\n", inum)); ++ spin_unlock(&c->inocache_lock); ++ return NULL; ++ } ++ if (ic->state != INO_STATE_CHECKEDABSENT) { ++ /* Wait for progress. Don't just loop */ ++ D1(printk(KERN_DEBUG "Waiting for ino #%u in state %d\n", ++ ic->ino, ic->state)); ++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); ++ } else { ++ spin_unlock(&c->inocache_lock); ++ } ++ ++ return NULL; ++ } ++ } else { ++ /* Inode has links to it still; they're not going away because ++ jffs2_do_unlink() would need the alloc_sem and we have it. ++ Just iget() it, and if read_inode() is necessary that's OK. ++ */ ++ inode = iget(OFNI_BS_2SFFJ(c), inum); ++ if (!inode) ++ return ERR_PTR(-ENOMEM); ++ } ++ if (is_bad_inode(inode)) { ++ printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u. nlink %d\n", ++ inum, nlink); ++ /* NB. This will happen again. We need to do something appropriate here. */ ++ iput(inode); ++ return ERR_PTR(-EIO); ++ } ++ ++ return JFFS2_INODE_INFO(inode); ++} ++ ++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f, ++ unsigned long offset, ++ unsigned long *priv) ++{ ++ struct inode *inode = OFNI_EDONI_2SFFJ(f); ++ struct page *pg; ++ ++ pg = read_cache_page(inode->i_mapping, offset >> PAGE_CACHE_SHIFT, ++ (void *)jffs2_do_readpage_unlock, inode); ++ if (IS_ERR(pg)) ++ return (void *)pg; ++ ++ *priv = (unsigned long)pg; ++ return kmap(pg); ++} ++ ++void jffs2_gc_release_page(struct jffs2_sb_info *c, ++ unsigned char *ptr, ++ unsigned long *priv) ++{ ++ struct page *pg = (void *)*priv; ++ ++ kunmap(pg); ++ page_cache_release(pg); ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/gc.c linux/fs/jffs2/gc.c +--- linux-mips-2.4.27/fs/jffs2/gc.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/gc.c 2004-11-19 10:25:12.104168632 +0100 +@@ -1,76 +1,67 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: gc.c,v 1.52.2.7 2003/11/02 13:54:20 dwmw2 Exp $ ++ * $Id: gc.c,v 1.132 2003/12/01 11:32:11 dwmw2 Exp $ + * + */ + + #include <linux/kernel.h> + #include <linux/mtd/mtd.h> + #include <linux/slab.h> +-#include <linux/jffs2.h> +-#include <linux/sched.h> +-#include <linux/interrupt.h> + #include <linux/pagemap.h> +-#include "nodelist.h" + #include <linux/crc32.h> ++#include <linux/compiler.h> ++#include <linux/stat.h> ++#include "nodelist.h" + ++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, ++ struct jffs2_inode_cache *ic, ++ struct jffs2_raw_node_ref *raw); + static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fd); ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fd); + static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dirent *fd); ++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd); + static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dirent *fd); ++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd); + static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *indeo, struct jffs2_full_dnode *fn, +- __u32 start, __u32 end); ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, ++ uint32_t start, uint32_t end); + static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fn, +- __u32 start, __u32 end); ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, ++ uint32_t start, uint32_t end); ++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f); + + /* Called with erase_completion_lock held */ + static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) + { + struct jffs2_eraseblock *ret; + struct list_head *nextlist = NULL; ++ int n = jiffies % 128; + + /* Pick an eraseblock to garbage collect next. This is where we'll + put the clever wear-levelling algorithms. Eventually. */ +- if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > JFFS2_RESERVED_BLOCKS_GCBAD) { ++ /* We possibly want to favour the dirtier blocks more when the ++ number of free blocks is low. */ ++ if (!list_empty(&c->bad_used_list) && c->nr_free_blocks > c->resv_blocks_gcbad) { + D1(printk(KERN_DEBUG "Picking block from bad_used_list to GC next\n")); + nextlist = &c->bad_used_list; +- } else if (jiffies % 100 && !list_empty(&c->dirty_list)) { +- /* Most of the time, pick one off the dirty list */ ++ } else if (n < 50 && !list_empty(&c->erasable_list)) { ++ /* Note that most of them will have gone directly to be erased. ++ So don't favour the erasable_list _too_ much. */ ++ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next\n")); ++ nextlist = &c->erasable_list; ++ } else if (n < 110 && !list_empty(&c->very_dirty_list)) { ++ /* Most of the time, pick one off the very_dirty list */ ++ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next\n")); ++ nextlist = &c->very_dirty_list; ++ } else if (n < 126 && !list_empty(&c->dirty_list)) { + D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next\n")); + nextlist = &c->dirty_list; + } else if (!list_empty(&c->clean_list)) { +@@ -80,9 +71,16 @@ + D1(printk(KERN_DEBUG "Picking block from dirty_list to GC next (clean_list was empty)\n")); + + nextlist = &c->dirty_list; ++ } else if (!list_empty(&c->very_dirty_list)) { ++ D1(printk(KERN_DEBUG "Picking block from very_dirty_list to GC next (clean_list and dirty_list were empty)\n")); ++ nextlist = &c->very_dirty_list; ++ } else if (!list_empty(&c->erasable_list)) { ++ D1(printk(KERN_DEBUG "Picking block from erasable_list to GC next (clean_list and {very_,}dirty_list were empty)\n")); ++ ++ nextlist = &c->erasable_list; + } else { +- /* Eep. Both were empty */ +- printk(KERN_NOTICE "jffs2: No clean _or_ dirty blocks to GC from! Where are they all?\n"); ++ /* Eep. All were empty */ ++ printk(KERN_NOTICE "jffs2: No clean, dirty _or_ erasable blocks to GC from! Where are they all?\n"); + return NULL; + } + +@@ -94,6 +92,17 @@ + printk(KERN_WARNING "Eep. ret->gc_node for block at 0x%08x is NULL\n", ret->offset); + BUG(); + } ++ ++ /* Have we accidentally picked a clean block with wasted space ? */ ++ if (ret->wasted_size) { ++ D1(printk(KERN_DEBUG "Converting wasted_size %08x to dirty_size\n", ret->wasted_size)); ++ ret->dirty_size += ret->wasted_size; ++ c->wasted_size -= ret->wasted_size; ++ c->dirty_size += ret->wasted_size; ++ ret->wasted_size = 0; ++ } ++ ++ D1(jffs2_dump_block_lists(c)); + return ret; + } + +@@ -103,21 +112,90 @@ + */ + int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) + { +- struct jffs2_eraseblock *jeb; + struct jffs2_inode_info *f; +- struct jffs2_raw_node_ref *raw; +- struct jffs2_node_frag *frag; +- struct jffs2_full_dnode *fn = NULL; +- struct jffs2_full_dirent *fd; + struct jffs2_inode_cache *ic; +- __u32 start = 0, end = 0, nrfrags = 0; +- struct inode *inode; +- int ret = 0; ++ struct jffs2_eraseblock *jeb; ++ struct jffs2_raw_node_ref *raw; ++ int ret = 0, inum, nlink; + + if (down_interruptible(&c->alloc_sem)) + return -EINTR; + +- spin_lock_bh(&c->erase_completion_lock); ++ for (;;) { ++ spin_lock(&c->erase_completion_lock); ++ if (!c->unchecked_size) ++ break; ++ ++ /* We can't start doing GC yet. We haven't finished checking ++ the node CRCs etc. Do it now. */ ++ ++ /* checked_ino is protected by the alloc_sem */ ++ if (c->checked_ino > c->highest_ino) { ++ printk(KERN_CRIT "Checked all inodes but still 0x%x bytes of unchecked space?\n", ++ c->unchecked_size); ++ D1(jffs2_dump_block_lists(c)); ++ spin_unlock(&c->erase_completion_lock); ++ BUG(); ++ } ++ ++ spin_unlock(&c->erase_completion_lock); ++ ++ spin_lock(&c->inocache_lock); ++ ++ ic = jffs2_get_ino_cache(c, c->checked_ino++); ++ ++ if (!ic) { ++ spin_unlock(&c->inocache_lock); ++ continue; ++ } ++ ++ if (!ic->nlink) { ++ D1(printk(KERN_DEBUG "Skipping check of ino #%d with nlink zero\n", ++ ic->ino)); ++ spin_unlock(&c->inocache_lock); ++ continue; ++ } ++ switch(ic->state) { ++ case INO_STATE_CHECKEDABSENT: ++ case INO_STATE_PRESENT: ++ D1(printk(KERN_DEBUG "Skipping ino #%u already checked\n", ic->ino)); ++ spin_unlock(&c->inocache_lock); ++ continue; ++ ++ case INO_STATE_GC: ++ case INO_STATE_CHECKING: ++ printk(KERN_WARNING "Inode #%u is in state %d during CRC check phase!\n", ic->ino, ic->state); ++ spin_unlock(&c->inocache_lock); ++ BUG(); ++ ++ case INO_STATE_READING: ++ /* We need to wait for it to finish, lest we move on ++ and trigger the BUG() above while we haven't yet ++ finished checking all its nodes */ ++ D1(printk(KERN_DEBUG "Waiting for ino #%u to finish reading\n", ic->ino)); ++ up(&c->alloc_sem); ++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); ++ return 0; ++ ++ default: ++ BUG(); ++ ++ case INO_STATE_UNCHECKED: ++ ; ++ } ++ ic->state = INO_STATE_CHECKING; ++ spin_unlock(&c->inocache_lock); ++ ++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%u\n", ic->ino)); ++ ++ ret = jffs2_do_crccheck_inode(c, ic); ++ if (ret) ++ printk(KERN_WARNING "Returned error for crccheck of ino #%u. Expect badness...\n", ic->ino); ++ ++ jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); ++ up(&c->alloc_sem); ++ return ret; ++ } + + /* First, work out which block we're garbage-collecting */ + jeb = c->gcblock; +@@ -127,12 +205,14 @@ + + if (!jeb) { + printk(KERN_NOTICE "jffs2: Couldn't find erase block to garbage collect!\n"); +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); + return -EIO; + } + +- D1(printk(KERN_DEBUG "garbage collect from block at phys 0x%08x\n", jeb->offset)); ++ D1(printk(KERN_DEBUG "GC from block %08x, used_size %08x, dirty_size %08x, free_size %08x\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->free_size)); ++ D1(if (c->nextblock) ++ printk(KERN_DEBUG "Nextblock at %08x, used_size %08x, dirty_size %08x, wasted_size %08x, free_size %08x\n", c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->free_size)); + + if (!jeb->used_size) { + up(&c->alloc_sem); +@@ -141,92 +221,211 @@ + + raw = jeb->gc_node; + +- while(raw->flash_offset & 1) { +- D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", raw->flash_offset &~3)); +- jeb->gc_node = raw = raw->next_phys; +- if (!raw) { ++ while(ref_obsolete(raw)) { ++ D1(printk(KERN_DEBUG "Node at 0x%08x is obsolete... skipping\n", ref_offset(raw))); ++ raw = raw->next_phys; ++ if (unlikely(!raw)) { + printk(KERN_WARNING "eep. End of raw list while still supposedly nodes to GC\n"); + printk(KERN_WARNING "erase block at 0x%08x. free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size); +- spin_unlock_bh(&c->erase_completion_lock); ++ jeb->gc_node = raw; ++ spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); + BUG(); + } + } +- D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", raw->flash_offset &~3)); ++ jeb->gc_node = raw; ++ ++ D1(printk(KERN_DEBUG "Going to garbage collect node at 0x%08x\n", ref_offset(raw))); ++ + if (!raw->next_in_ino) { + /* Inode-less node. Clean marker, snapshot or something like that */ +- spin_unlock_bh(&c->erase_completion_lock); ++ /* FIXME: If it's something that needs to be copied, including something ++ we don't grok that has JFFS2_NODETYPE_RWCOMPAT_COPY, we should do so */ ++ spin_unlock(&c->erase_completion_lock); + jffs2_mark_node_obsolete(c, raw); + up(&c->alloc_sem); + goto eraseit_lock; + } + + ic = jffs2_raw_ref_to_ic(raw); +- D1(printk(KERN_DEBUG "Inode number is #%u\n", ic->ino)); +- +- spin_unlock_bh(&c->erase_completion_lock); + +- D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, raw->flash_offset&~3, ic->ino)); +- if (!ic->nlink) { +- /* The inode has zero nlink but its nodes weren't yet marked +- obsolete. This has to be because we're still waiting for +- the final (close() and) iput() to happen. +- +- There's a possibility that the final iput() could have +- happened while we were contemplating. In order to ensure +- that we don't cause a new read_inode() (which would fail) +- for the inode in question, we use ilookup() in this case +- instead of iget(). +- +- The nlink can't _become_ zero at this point because we're +- holding the alloc_sem, and jffs2_do_unlink() would also +- need that while decrementing nlink on any inode. ++ /* We need to hold the inocache. Either the erase_completion_lock or ++ the inocache_lock are sufficient; we trade down since the inocache_lock ++ causes less contention. */ ++ spin_lock(&c->inocache_lock); ++ ++ spin_unlock(&c->erase_completion_lock); ++ ++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x(%d), ino #%u\n", jeb->offset, ref_offset(raw), ref_flags(raw), ic->ino)); ++ ++ /* Three possibilities: ++ 1. Inode is already in-core. We must iget it and do proper ++ updating to its fragtree, etc. ++ 2. Inode is not in-core, node is REF_PRISTINE. We lock the ++ inocache to prevent a read_inode(), copy the node intact. ++ 3. Inode is not in-core, node is not pristine. We must iget() ++ and take the slow path. + */ +- inode = ilookup(OFNI_BS_2SFFJ(c), ic->ino); +- if (!inode) { +- D1(printk(KERN_DEBUG "ilookup() failed for ino #%u; inode is probably deleted.\n", ++ ++ switch(ic->state) { ++ case INO_STATE_CHECKEDABSENT: ++ /* It's been checked, but it's not currently in-core. ++ We can just copy any pristine nodes, but have ++ to prevent anyone else from doing read_inode() while ++ we're at it, so we set the state accordingly */ ++ if (ref_flags(raw) == REF_PRISTINE) ++ ic->state = INO_STATE_GC; ++ else { ++ D1(printk(KERN_DEBUG "Ino #%u is absent but node not REF_PRISTINE. Reading.\n", + ic->ino)); +- up(&c->alloc_sem); +- return 0; + } +- } else { +- /* Inode has links to it still; they're not going away because +- jffs2_do_unlink() would need the alloc_sem and we have it. +- Just iget() it, and if read_inode() is necessary that's OK. ++ break; ++ ++ case INO_STATE_PRESENT: ++ /* It's in-core. GC must iget() it. */ ++ break; ++ ++ case INO_STATE_UNCHECKED: ++ case INO_STATE_CHECKING: ++ case INO_STATE_GC: ++ /* Should never happen. We should have finished checking ++ by the time we actually start doing any GC, and since ++ we're holding the alloc_sem, no other garbage collection ++ can happen. + */ +- inode = iget(OFNI_BS_2SFFJ(c), ic->ino); +- if (!inode) { ++ printk(KERN_CRIT "Inode #%u already in state %d in jffs2_garbage_collect_pass()!\n", ++ ic->ino, ic->state); + up(&c->alloc_sem); +- return -ENOMEM; ++ spin_unlock(&c->inocache_lock); ++ BUG(); ++ ++ case INO_STATE_READING: ++ /* Someone's currently trying to read it. We must wait for ++ them to finish and then go through the full iget() route ++ to do the GC. However, sometimes read_inode() needs to get ++ the alloc_sem() (for marking nodes invalid) so we must ++ drop the alloc_sem before sleeping. */ ++ ++ up(&c->alloc_sem); ++ D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() waiting for ino #%u in state %d\n", ++ ic->ino, ic->state)); ++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); ++ /* And because we dropped the alloc_sem we must start again from the ++ beginning. Ponder chance of livelock here -- we're returning success ++ without actually making any progress. ++ ++ Q: What are the chances that the inode is back in INO_STATE_READING ++ again by the time we next enter this function? And that this happens ++ enough times to cause a real delay? ++ ++ A: Small enough that I don't care :) ++ */ ++ return 0; + } ++ ++ /* OK. Now if the inode is in state INO_STATE_GC, we are going to copy the ++ node intact, and we don't have to muck about with the fragtree etc. ++ because we know it's not in-core. If it _was_ in-core, we go through ++ all the iget() crap anyway */ ++ ++ if (ic->state == INO_STATE_GC) { ++ spin_unlock(&c->inocache_lock); ++ ++ ret = jffs2_garbage_collect_pristine(c, ic, raw); ++ ++ spin_lock(&c->inocache_lock); ++ ic->state = INO_STATE_CHECKEDABSENT; ++ wake_up(&c->inocache_wq); ++ ++ if (ret != -EBADFD) { ++ spin_unlock(&c->inocache_lock); ++ goto release_sem; + } +- if (is_bad_inode(inode)) { +- printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino); +- /* NB. This will happen again. We need to do something appropriate here. */ ++ ++ /* Fall through if it wanted us to, with inocache_lock held */ ++ } ++ ++ /* Prevent the fairly unlikely race where the gcblock is ++ entirely obsoleted by the final close of a file which had ++ the only valid nodes in the block, followed by erasure, ++ followed by freeing of the ic because the erased block(s) ++ held _all_ the nodes of that inode.... never been seen but ++ it's vaguely possible. */ ++ ++ inum = ic->ino; ++ nlink = ic->nlink; ++ spin_unlock(&c->inocache_lock); ++ ++ f = jffs2_gc_fetch_inode(c, inum, nlink); ++ if (IS_ERR(f)) ++ return PTR_ERR(f); ++ if (!f) ++ return 0; ++ ++ ret = jffs2_garbage_collect_live(c, jeb, raw, f); ++ ++ jffs2_gc_release_inode(c, f); ++ ++ release_sem: + up(&c->alloc_sem); +- iput(inode); +- return -EIO; ++ ++ eraseit_lock: ++ /* If we've finished this block, start it erasing */ ++ spin_lock(&c->erase_completion_lock); ++ ++ eraseit: ++ if (c->gcblock && !c->gcblock->used_size) { ++ D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); ++ /* We're GC'ing an empty block? */ ++ list_add_tail(&c->gcblock->list, &c->erase_pending_list); ++ c->gcblock = NULL; ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); + } ++ spin_unlock(&c->erase_completion_lock); ++ ++ return ret; ++} ++ ++static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_node_ref *raw, struct jffs2_inode_info *f) ++{ ++ struct jffs2_node_frag *frag; ++ struct jffs2_full_dnode *fn = NULL; ++ struct jffs2_full_dirent *fd; ++ uint32_t start = 0, end = 0, nrfrags = 0; ++ int ret = 0; + +- f = JFFS2_INODE_INFO(inode); + down(&f->sem); ++ + /* Now we have the lock for this inode. Check that it's still the one at the head + of the list. */ + +- if (raw->flash_offset & 1) { ++ spin_lock(&c->erase_completion_lock); ++ ++ if (c->gcblock != jeb) { ++ spin_unlock(&c->erase_completion_lock); ++ D1(printk(KERN_DEBUG "GC block is no longer gcblock. Restart\n")); ++ goto upnout; ++ } ++ if (ref_obsolete(raw)) { ++ spin_unlock(&c->erase_completion_lock); + D1(printk(KERN_DEBUG "node to be GC'd was obsoleted in the meantime.\n")); + /* They'll call again */ + goto upnout; + } ++ spin_unlock(&c->erase_completion_lock); ++ + /* OK. Looks safe. And nobody can get us now because we have the semaphore. Move the block */ + if (f->metadata && f->metadata->raw == raw) { + fn = f->metadata; +- ret = jffs2_garbage_collect_metadata(c, jeb, inode, fn); ++ ret = jffs2_garbage_collect_metadata(c, jeb, f, fn); + goto upnout; + } + +- for (frag = f->fraglist; frag; frag = frag->next) { ++ /* FIXME. Read node and do lookup? */ ++ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { + if (frag->node && frag->node->raw == raw) { + fn = frag->node; + end = frag->ofs + frag->size; +@@ -237,13 +436,22 @@ + } + } + if (fn) { ++ if (ref_flags(raw) == REF_PRISTINE) { ++ ret = jffs2_garbage_collect_pristine(c, f->inocache, raw); ++ if (!ret) { ++ /* Urgh. Return it sensibly. */ ++ frag->node->raw = f->inocache->nodes; ++ } ++ if (ret != -EBADFD) ++ goto upnout; ++ } + /* We found a datanode. Do the GC */ + if((start >> PAGE_CACHE_SHIFT) < ((end-1) >> PAGE_CACHE_SHIFT)) { + /* It crosses a page boundary. Therefore, it must be a hole. */ +- ret = jffs2_garbage_collect_hole(c, jeb, inode, fn, start, end); ++ ret = jffs2_garbage_collect_hole(c, jeb, f, fn, start, end); + } else { + /* It could still be a hole. But we GC the page this way anyway */ +- ret = jffs2_garbage_collect_dnode(c, jeb, inode, fn, start, end); ++ ret = jffs2_garbage_collect_dnode(c, jeb, f, fn, start, end); + } + goto upnout; + } +@@ -255,12 +463,13 @@ + } + + if (fd && fd->ino) { +- ret = jffs2_garbage_collect_dirent(c, jeb, inode, fd); ++ ret = jffs2_garbage_collect_dirent(c, jeb, f, fd); + } else if (fd) { +- ret = jffs2_garbage_collect_deletion_dirent(c, jeb, inode, fd); ++ ret = jffs2_garbage_collect_deletion_dirent(c, jeb, f, fd); + } else { +- printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%lu\n", raw->flash_offset&~3, inode->i_ino); +- if (raw->flash_offset & 1) { ++ printk(KERN_WARNING "Raw node at 0x%08x wasn't in node lists for ino #%u\n", ++ ref_offset(raw), f->inocache->ino); ++ if (ref_obsolete(raw)) { + printk(KERN_WARNING "But it's obsolete so we don't mind too much\n"); + } else { + ret = -EIO; +@@ -268,46 +477,197 @@ + } + upnout: + up(&f->sem); +- up(&c->alloc_sem); +- iput(inode); + +- eraseit_lock: +- /* If we've finished this block, start it erasing */ +- spin_lock_bh(&c->erase_completion_lock); ++ return ret; ++} + +- eraseit: +- if (c->gcblock && !c->gcblock->used_size) { +- D1(printk(KERN_DEBUG "Block at 0x%08x completely obsoleted by GC. Moving to erase_pending_list\n", c->gcblock->offset)); +- /* We're GC'ing an empty block? */ +- list_add_tail(&c->gcblock->list, &c->erase_pending_list); +- c->gcblock = NULL; +- c->nr_erasing_blocks++; +- jffs2_erase_pending_trigger(c); ++static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, ++ struct jffs2_inode_cache *ic, ++ struct jffs2_raw_node_ref *raw) ++{ ++ union jffs2_node_union *node; ++ struct jffs2_raw_node_ref *nraw; ++ size_t retlen; ++ int ret; ++ uint32_t phys_ofs, alloclen; ++ uint32_t crc, rawlen; ++ int retried = 0; ++ ++ D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(raw))); ++ ++ rawlen = ref_totlen(c, c->gcblock, raw); ++ ++ /* Ask for a small amount of space (or the totlen if smaller) because we ++ don't want to force wastage of the end of a block if splitting would ++ work. */ ++ ret = jffs2_reserve_space_gc(c, min_t(uint32_t, sizeof(struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN, ++ rawlen), &phys_ofs, &alloclen); ++ if (ret) ++ return ret; ++ ++ if (alloclen < rawlen) { ++ /* Doesn't fit untouched. We'll go the old route and split it */ ++ return -EBADFD; ++ } ++ ++ node = kmalloc(rawlen, GFP_KERNEL); ++ if (!node) ++ return -ENOMEM; ++ ++ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)node); ++ if (!ret && retlen != rawlen) ++ ret = -EIO; ++ if (ret) ++ goto out_node; ++ ++ crc = crc32(0, node, sizeof(struct jffs2_unknown_node)-4); ++ if (je32_to_cpu(node->u.hdr_crc) != crc) { ++ printk(KERN_WARNING "Header CRC failed on REF_PRISTINE node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->u.hdr_crc), crc); ++ goto bail; ++ } ++ ++ switch(je16_to_cpu(node->u.nodetype)) { ++ case JFFS2_NODETYPE_INODE: ++ crc = crc32(0, node, sizeof(node->i)-8); ++ if (je32_to_cpu(node->i.node_crc) != crc) { ++ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->i.node_crc), crc); ++ goto bail; ++ } ++ ++ if (je32_to_cpu(node->i.dsize)) { ++ crc = crc32(0, node->i.data, je32_to_cpu(node->i.csize)); ++ if (je32_to_cpu(node->i.data_crc) != crc) { ++ printk(KERN_WARNING "Data CRC failed on REF_PRISTINE data node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->i.data_crc), crc); ++ goto bail; + } +- spin_unlock_bh(&c->erase_completion_lock); ++ } ++ break; ++ ++ case JFFS2_NODETYPE_DIRENT: ++ crc = crc32(0, node, sizeof(node->d)-8); ++ if (je32_to_cpu(node->d.node_crc) != crc) { ++ printk(KERN_WARNING "Node CRC failed on REF_PRISTINE dirent node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->d.node_crc), crc); ++ goto bail; ++ } ++ ++ if (node->d.nsize) { ++ crc = crc32(0, node->d.name, node->d.nsize); ++ if (je32_to_cpu(node->d.name_crc) != crc) { ++ printk(KERN_WARNING "Name CRC failed on REF_PRISTINE dirent ode at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(raw), je32_to_cpu(node->d.name_crc), crc); ++ goto bail; ++ } ++ } ++ break; ++ default: ++ printk(KERN_WARNING "Unknown node type for REF_PRISTINE node at 0x%08x: 0x%04x\n", ++ ref_offset(raw), je16_to_cpu(node->u.nodetype)); ++ goto bail; ++ } ++ ++ nraw = jffs2_alloc_raw_node_ref(); ++ if (!nraw) { ++ ret = -ENOMEM; ++ goto out_node; ++ } ++ ++ /* OK, all the CRCs are good; this node can just be copied as-is. */ ++ retry: ++ nraw->flash_offset = phys_ofs; ++ nraw->__totlen = rawlen; ++ nraw->next_phys = NULL; ++ ++ ret = jffs2_flash_write(c, phys_ofs, rawlen, &retlen, (char *)node); ++ ++ if (ret || (retlen != rawlen)) { ++ printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", ++ rawlen, phys_ofs, ret, retlen); ++ if (retlen) { ++ /* Doesn't belong to any inode */ ++ nraw->next_in_ino = NULL; ++ ++ nraw->flash_offset |= REF_OBSOLETE; ++ jffs2_add_physical_node_ref(c, nraw); ++ jffs2_mark_node_obsolete(c, nraw); ++ } else { ++ printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", nraw->flash_offset); ++ jffs2_free_raw_node_ref(nraw); ++ } ++ if (!retried && (nraw == jffs2_alloc_raw_node_ref())) { ++ /* Try to reallocate space and retry */ ++ uint32_t dummy; ++ struct jffs2_eraseblock *jeb = &c->blocks[phys_ofs / c->sector_size]; ++ ++ retried = 1; ++ ++ D1(printk(KERN_DEBUG "Retrying failed write of REF_PRISTINE node.\n")); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ ret = jffs2_reserve_space_gc(c, rawlen, &phys_ofs, &dummy); ++ ++ if (!ret) { ++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", phys_ofs)); + ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ goto retry; ++ } ++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); ++ jffs2_free_raw_node_ref(nraw); ++ } ++ ++ if (!ret) ++ ret = -EIO; ++ goto out_node; ++ } ++ nraw->flash_offset |= REF_PRISTINE; ++ jffs2_add_physical_node_ref(c, nraw); ++ ++ /* Link into per-inode list. This is safe because of the ic ++ state being INO_STATE_GC. Note that if we're doing this ++ for an inode which is in-code, the 'nraw' pointer is then ++ going to be fetched from ic->nodes by our caller. */ ++ nraw->next_in_ino = ic->nodes; ++ ic->nodes = nraw; ++ ++ jffs2_mark_node_obsolete(c, raw); ++ D1(printk(KERN_DEBUG "WHEEE! GC REF_PRISTINE node at 0x%08x succeeded\n", ref_offset(raw))); ++ ++ out_node: ++ kfree(node); + return ret; ++ bail: ++ ret = -EBADFD; ++ goto out_node; + } + + static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fn) ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dnode *new_fn; + struct jffs2_raw_inode ri; +- unsigned short dev; ++ jint16_t dev; + char *mdata = NULL, mdatalen = 0; +- __u32 alloclen, phys_ofs; ++ uint32_t alloclen, phys_ofs; + int ret; + +- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode)) { ++ if (S_ISBLK(JFFS2_F_I_MODE(f)) || ++ S_ISCHR(JFFS2_F_I_MODE(f)) ) { + /* For these, we don't actually need to read the old node */ +- dev = (MAJOR(to_kdev_t(inode->i_rdev)) << 8) | +- MINOR(to_kdev_t(inode->i_rdev)); ++ /* FIXME: for minor or major > 255. */ ++ dev = cpu_to_je16(((JFFS2_F_I_RDEV_MAJ(f) << 8) | ++ JFFS2_F_I_RDEV_MIN(f))); + mdata = (char *)&dev; + mdatalen = sizeof(dev); + D1(printk(KERN_DEBUG "jffs2_garbage_collect_metadata(): Writing %d bytes of kdev_t\n", mdatalen)); +- } else if (S_ISLNK(inode->i_mode)) { ++ } else if (S_ISLNK(JFFS2_F_I_MODE(f))) { + mdatalen = fn->size; + mdata = kmalloc(fn->size, GFP_KERNEL); + if (!mdata) { +@@ -326,34 +686,34 @@ + + ret = jffs2_reserve_space_gc(c, sizeof(ri) + mdatalen, &phys_ofs, &alloclen); + if (ret) { +- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_metadata failed: %d\n", ++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_metadata failed: %d\n", + sizeof(ri)+ mdatalen, ret); + goto out; + } + + memset(&ri, 0, sizeof(ri)); +- ri.magic = JFFS2_MAGIC_BITMASK; +- ri.nodetype = JFFS2_NODETYPE_INODE; +- ri.totlen = sizeof(ri) + mdatalen; +- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri.ino = inode->i_ino; +- ri.version = ++f->highest_version; +- ri.mode = inode->i_mode; +- ri.uid = inode->i_uid; +- ri.gid = inode->i_gid; +- ri.isize = inode->i_size; +- ri.atime = inode->i_atime; +- ri.ctime = inode->i_ctime; +- ri.mtime = inode->i_mtime; +- ri.offset = 0; +- ri.csize = mdatalen; +- ri.dsize = mdatalen; ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri) + mdatalen); ++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri.ino = cpu_to_je32(f->inocache->ino); ++ ri.version = cpu_to_je32(++f->highest_version); ++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); ++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ++ ri.offset = cpu_to_je32(0); ++ ri.csize = cpu_to_je32(mdatalen); ++ ri.dsize = cpu_to_je32(mdatalen); + ri.compr = JFFS2_COMPR_NONE; +- ri.node_crc = crc32(0, &ri, sizeof(ri)-8); +- ri.data_crc = crc32(0, mdata, mdatalen); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ++ ri.data_crc = cpu_to_je32(crc32(0, mdata, mdatalen)); + +- new_fn = jffs2_write_dnode(inode, &ri, mdata, mdatalen, phys_ofs, NULL); ++ new_fn = jffs2_write_dnode(c, f, &ri, mdata, mdatalen, phys_ofs, ALLOC_GC); + + if (IS_ERR(new_fn)) { + printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); +@@ -364,41 +724,40 @@ + jffs2_free_full_dnode(fn); + f->metadata = new_fn; + out: +- if (S_ISLNK(inode->i_mode)) ++ if (S_ISLNK(JFFS2_F_I_MODE(f))) + kfree(mdata); + return ret; + } + + static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dirent *fd) ++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dirent *new_fd; + struct jffs2_raw_dirent rd; +- __u32 alloclen, phys_ofs; ++ uint32_t alloclen, phys_ofs; + int ret; + +- rd.magic = JFFS2_MAGIC_BITMASK; +- rd.nodetype = JFFS2_NODETYPE_DIRENT; ++ rd.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd.nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); + rd.nsize = strlen(fd->name); +- rd.totlen = sizeof(rd) + rd.nsize; +- rd.hdr_crc = crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4); ++ rd.totlen = cpu_to_je32(sizeof(rd) + rd.nsize); ++ rd.hdr_crc = cpu_to_je32(crc32(0, &rd, sizeof(struct jffs2_unknown_node)-4)); + +- rd.pino = inode->i_ino; +- rd.version = ++f->highest_version; +- rd.ino = fd->ino; +- rd.mctime = max(inode->i_mtime, inode->i_ctime); ++ rd.pino = cpu_to_je32(f->inocache->ino); ++ rd.version = cpu_to_je32(++f->highest_version); ++ rd.ino = cpu_to_je32(fd->ino); ++ rd.mctime = cpu_to_je32(max(JFFS2_F_I_MTIME(f), JFFS2_F_I_CTIME(f))); + rd.type = fd->type; +- rd.node_crc = crc32(0, &rd, sizeof(rd)-8); +- rd.name_crc = crc32(0, fd->name, rd.nsize); ++ rd.node_crc = cpu_to_je32(crc32(0, &rd, sizeof(rd)-8)); ++ rd.name_crc = cpu_to_je32(crc32(0, fd->name, rd.nsize)); + + ret = jffs2_reserve_space_gc(c, sizeof(rd)+rd.nsize, &phys_ofs, &alloclen); + if (ret) { +- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dirent failed: %d\n", ++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dirent failed: %d\n", + sizeof(rd)+rd.nsize, ret); + return ret; + } +- new_fd = jffs2_write_dirent(inode, &rd, fd->name, rd.nsize, phys_ofs, NULL); ++ new_fd = jffs2_write_dirent(c, f, &rd, fd->name, rd.nsize, phys_ofs, ALLOC_GC); + + if (IS_ERR(new_fd)) { + printk(KERN_WARNING "jffs2_write_dirent in garbage_collect_dirent failed: %ld\n", PTR_ERR(new_fd)); +@@ -409,19 +768,98 @@ + } + + static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dirent *fd) ++ struct jffs2_inode_info *f, struct jffs2_full_dirent *fd) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dirent **fdp = &f->dents; + int found = 0; + +- /* FIXME: When we run on NAND flash, we need to work out whether +- this deletion dirent is still needed to actively delete a +- 'real' dirent with the same name that's still somewhere else +- on the flash. For now, we know that we've actually obliterated +- all the older dirents when they became obsolete, so we didn't +- really need to write the deletion to flash in the first place. +- */ ++ /* On a medium where we can't actually mark nodes obsolete ++ pernamently, such as NAND flash, we need to work out ++ whether this deletion dirent is still needed to actively ++ delete a 'real' dirent with the same name that's still ++ somewhere else on the flash. */ ++ if (!jffs2_can_mark_obsolete(c)) { ++ struct jffs2_raw_dirent *rd; ++ struct jffs2_raw_node_ref *raw; ++ int ret; ++ size_t retlen; ++ int name_len = strlen(fd->name); ++ uint32_t name_crc = crc32(0, fd->name, name_len); ++ uint32_t rawlen = ref_totlen(c, jeb, fd->raw); ++ ++ rd = kmalloc(rawlen, GFP_KERNEL); ++ if (!rd) ++ return -ENOMEM; ++ ++ /* Prevent the erase code from nicking the obsolete node refs while ++ we're looking at them. I really don't like this extra lock but ++ can't see any alternative. Suggestions on a postcard to... */ ++ down(&c->erase_free_sem); ++ ++ for (raw = f->inocache->nodes; raw != (void *)f->inocache; raw = raw->next_in_ino) { ++ ++ /* We only care about obsolete ones */ ++ if (!(ref_obsolete(raw))) ++ continue; ++ ++ /* Any dirent with the same name is going to have the same length... */ ++ if (ref_totlen(c, NULL, raw) != rawlen) ++ continue; ++ ++ /* Doesn't matter if there's one in the same erase block. We're going to ++ delete it too at the same time. */ ++ if ((raw->flash_offset & ~(c->sector_size-1)) == ++ (fd->raw->flash_offset & ~(c->sector_size-1))) ++ continue; ++ ++ D1(printk(KERN_DEBUG "Check potential deletion dirent at %08x\n", ref_offset(raw))); ++ ++ /* This is an obsolete node belonging to the same directory, and it's of the right ++ length. We need to take a closer look...*/ ++ ret = jffs2_flash_read(c, ref_offset(raw), rawlen, &retlen, (char *)rd); ++ if (ret) { ++ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Read error (%d) reading obsolete node at %08x\n", ret, ref_offset(raw)); ++ /* If we can't read it, we don't need to continue to obsolete it. Continue */ ++ continue; ++ } ++ if (retlen != rawlen) { ++ printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n", ++ retlen, rawlen, ref_offset(raw)); ++ continue; ++ } ++ ++ if (je16_to_cpu(rd->nodetype) != JFFS2_NODETYPE_DIRENT) ++ continue; ++ ++ /* If the name CRC doesn't match, skip */ ++ if (je32_to_cpu(rd->name_crc) != name_crc) ++ continue; ++ ++ /* If the name length doesn't match, or it's another deletion dirent, skip */ ++ if (rd->nsize != name_len || !je32_to_cpu(rd->ino)) ++ continue; ++ ++ /* OK, check the actual name now */ ++ if (memcmp(rd->name, fd->name, name_len)) ++ continue; ++ ++ /* OK. The name really does match. There really is still an older node on ++ the flash which our deletion dirent obsoletes. So we have to write out ++ a new deletion dirent to replace it */ ++ up(&c->erase_free_sem); ++ ++ D1(printk(KERN_DEBUG "Deletion dirent at %08x still obsoletes real dirent \"%s\" at %08x for ino #%u\n", ++ ref_offset(fd->raw), fd->name, ref_offset(raw), je32_to_cpu(rd->ino))); ++ kfree(rd); ++ ++ return jffs2_garbage_collect_dirent(c, jeb, f, fd); ++ } ++ ++ up(&c->erase_free_sem); ++ kfree(rd); ++ } ++ ++ /* No need for it any more. Just mark it obsolete and remove it from the list */ + while (*fdp) { + if ((*fdp) == fd) { + found = 1; +@@ -431,7 +869,7 @@ + fdp = &(*fdp)->next; + } + if (!found) { +- printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%lu\n", fd->name, inode->i_ino); ++ printk(KERN_WARNING "Deletion dirent \"%s\" not found in list for ino #%u\n", fd->name, f->inocache->ino); + } + jffs2_mark_node_obsolete(c, fd->raw); + jffs2_free_full_dirent(fd); +@@ -439,93 +877,95 @@ + } + + static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fn, +- __u32 start, __u32 end) ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, ++ uint32_t start, uint32_t end) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_raw_inode ri; + struct jffs2_node_frag *frag; + struct jffs2_full_dnode *new_fn; +- __u32 alloclen, phys_ofs; ++ uint32_t alloclen, phys_ofs; + int ret; + +- D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%lu from offset 0x%x to 0x%x\n", +- inode->i_ino, start, end)); ++ D1(printk(KERN_DEBUG "Writing replacement hole node for ino #%u from offset 0x%x to 0x%x\n", ++ f->inocache->ino, start, end)); + + memset(&ri, 0, sizeof(ri)); + + if(fn->frags > 1) { + size_t readlen; +- __u32 crc; ++ uint32_t crc; + /* It's partially obsoleted by a later write. So we have to + write it out again with the _same_ version as before */ +- ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(ri), &readlen, (char *)&ri); ++ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(ri), &readlen, (char *)&ri); + if (readlen != sizeof(ri) || ret) { +- printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %d. Data will be lost by writing new hold node\n", ret, readlen); ++ printk(KERN_WARNING "Node read failed in jffs2_garbage_collect_hole. Ret %d, retlen %zd. Data will be lost by writing new hole node\n", ret, readlen); + goto fill; + } +- if (ri.nodetype != JFFS2_NODETYPE_INODE) { ++ if (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) { + printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had node type 0x%04x instead of JFFS2_NODETYPE_INODE(0x%04x)\n", +- fn->raw->flash_offset & ~3, ri.nodetype, JFFS2_NODETYPE_INODE); ++ ref_offset(fn->raw), ++ je16_to_cpu(ri.nodetype), JFFS2_NODETYPE_INODE); + return -EIO; + } +- if (ri.totlen != sizeof(ri)) { +- printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%x\n", +- fn->raw->flash_offset & ~3, ri.totlen, sizeof(ri)); ++ if (je32_to_cpu(ri.totlen) != sizeof(ri)) { ++ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had totlen 0x%x instead of expected 0x%zx\n", ++ ref_offset(fn->raw), ++ je32_to_cpu(ri.totlen), sizeof(ri)); + return -EIO; + } + crc = crc32(0, &ri, sizeof(ri)-8); +- if (crc != ri.node_crc) { ++ if (crc != je32_to_cpu(ri.node_crc)) { + printk(KERN_WARNING "jffs2_garbage_collect_hole: Node at 0x%08x had CRC 0x%08x which doesn't match calculated CRC 0x%08x\n", +- fn->raw->flash_offset & ~3, ri.node_crc, crc); ++ ref_offset(fn->raw), ++ je32_to_cpu(ri.node_crc), crc); + /* FIXME: We could possibly deal with this by writing new holes for each frag */ +- printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n", +- start, end, inode->i_ino); ++ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", ++ start, end, f->inocache->ino); + goto fill; + } + if (ri.compr != JFFS2_COMPR_ZERO) { +- printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", fn->raw->flash_offset & ~3); +- printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%lu will be lost\n", +- start, end, inode->i_ino); ++ printk(KERN_WARNING "jffs2_garbage_collect_hole: Node 0x%08x wasn't a hole node!\n", ref_offset(fn->raw)); ++ printk(KERN_WARNING "Data in the range 0x%08x to 0x%08x of inode #%u will be lost\n", ++ start, end, f->inocache->ino); + goto fill; + } + } else { + fill: +- ri.magic = JFFS2_MAGIC_BITMASK; +- ri.nodetype = JFFS2_NODETYPE_INODE; +- ri.totlen = sizeof(ri); +- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri.ino = inode->i_ino; +- ri.version = ++f->highest_version; +- ri.offset = start; +- ri.dsize = end - start; +- ri.csize = 0; ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri)); ++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri.ino = cpu_to_je32(f->inocache->ino); ++ ri.version = cpu_to_je32(++f->highest_version); ++ ri.offset = cpu_to_je32(start); ++ ri.dsize = cpu_to_je32(end - start); ++ ri.csize = cpu_to_je32(0); + ri.compr = JFFS2_COMPR_ZERO; + } +- ri.mode = inode->i_mode; +- ri.uid = inode->i_uid; +- ri.gid = inode->i_gid; +- ri.isize = inode->i_size; +- ri.atime = inode->i_atime; +- ri.ctime = inode->i_ctime; +- ri.mtime = inode->i_mtime; +- ri.data_crc = 0; +- ri.node_crc = crc32(0, &ri, sizeof(ri)-8); ++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); ++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ++ ri.data_crc = cpu_to_je32(0); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); + + ret = jffs2_reserve_space_gc(c, sizeof(ri), &phys_ofs, &alloclen); + if (ret) { +- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_hole failed: %d\n", ++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_hole failed: %d\n", + sizeof(ri), ret); + return ret; + } +- new_fn = jffs2_write_dnode(inode, &ri, NULL, 0, phys_ofs, NULL); ++ new_fn = jffs2_write_dnode(c, f, &ri, NULL, 0, phys_ofs, ALLOC_GC); + + if (IS_ERR(new_fn)) { + printk(KERN_WARNING "Error writing new hole node: %ld\n", PTR_ERR(new_fn)); + return PTR_ERR(new_fn); + } +- if (ri.version == f->highest_version) { ++ if (je32_to_cpu(ri.version) == f->highest_version) { + jffs2_add_full_dnode_to_inode(c, f, new_fn); + if (f->metadata) { + jffs2_mark_node_obsolete(c, f->metadata->raw); +@@ -541,12 +981,17 @@ + * number as before. (Except in case of error -- see 'goto fill;' + * above.) + */ +- D1(if(fn->frags <= 1) { ++ D1(if(unlikely(fn->frags <= 1)) { + printk(KERN_WARNING "jffs2_garbage_collect_hole: Replacing fn with %d frag(s) but new ver %d != highest_version %d of ino #%d\n", +- fn->frags, ri.version, f->highest_version, ri.ino); ++ fn->frags, je32_to_cpu(ri.version), f->highest_version, ++ je32_to_cpu(ri.ino)); + }); + +- for (frag = f->fraglist; frag; frag = frag->next) { ++ /* This is a partially-overlapped hole node. Mark it REF_NORMAL not REF_PRISTINE */ ++ mark_ref_normal(new_fn->raw); ++ ++ for (frag = jffs2_lookup_node_frag(&f->fragtree, fn->ofs); ++ frag; frag = frag_next(frag)) { + if (frag->ofs > fn->size + fn->ofs) + break; + if (frag->node == fn) { +@@ -571,49 +1016,146 @@ + } + + static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, +- struct inode *inode, struct jffs2_full_dnode *fn, +- __u32 start, __u32 end) ++ struct jffs2_inode_info *f, struct jffs2_full_dnode *fn, ++ uint32_t start, uint32_t end) + { +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_full_dnode *new_fn; + struct jffs2_raw_inode ri; +- __u32 alloclen, phys_ofs, offset, orig_end; ++ uint32_t alloclen, phys_ofs, offset, orig_end, orig_start; + int ret = 0; + unsigned char *comprbuf = NULL, *writebuf; +- struct page *pg; ++ unsigned long pg; + unsigned char *pg_ptr; + +- + memset(&ri, 0, sizeof(ri)); + +- D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%lu from offset 0x%x to 0x%x\n", +- inode->i_ino, start, end)); ++ D1(printk(KERN_DEBUG "Writing replacement dnode for ino #%u from offset 0x%x to 0x%x\n", ++ f->inocache->ino, start, end)); + + orig_end = end; ++ orig_start = start; + ++ if (c->nr_free_blocks + c->nr_erasing_blocks > c->resv_blocks_gcmerge) { ++ /* Attempt to do some merging. But only expand to cover logically ++ adjacent frags if the block containing them is already considered ++ to be dirty. Otherwise we end up with GC just going round in ++ circles dirtying the nodes it already wrote out, especially ++ on NAND where we have small eraseblocks and hence a much higher ++ chance of nodes having to be split to cross boundaries. */ + +- /* If we're looking at the last node in the block we're +- garbage-collecting, we allow ourselves to merge as if the +- block was already erasing. We're likely to be GC'ing a +- partial page, and the next block we GC is likely to have +- the other half of this page right at the beginning, which +- means we'd expand it _then_, as nr_erasing_blocks would have +- increased since we checked, and in doing so would obsolete +- the partial node which we'd have written here. Meaning that +- the GC would churn and churn, and just leave dirty blocks in +- it's wake. +- */ +- if(c->nr_free_blocks + c->nr_erasing_blocks > JFFS2_RESERVED_BLOCKS_GCMERGE - (fn->raw->next_phys?0:1)) { +- /* Shitloads of space */ +- /* FIXME: Integrate this properly with GC calculations */ +- start &= ~(PAGE_CACHE_SIZE-1); +- end = min_t(__u32, start + PAGE_CACHE_SIZE, inode->i_size); +- D1(printk(KERN_DEBUG "Plenty of free space, so expanding to write from offset 0x%x to 0x%x\n", +- start, end)); +- if (end < orig_end) { +- printk(KERN_WARNING "Eep. jffs2_garbage_collect_dnode extended node to write, but it got smaller: start 0x%x, orig_end 0x%x, end 0x%x\n", start, orig_end, end); +- end = orig_end; ++ struct jffs2_node_frag *frag; ++ uint32_t min, max; ++ ++ min = start & ~(PAGE_CACHE_SIZE-1); ++ max = min + PAGE_CACHE_SIZE; ++ ++ frag = jffs2_lookup_node_frag(&f->fragtree, start); ++ ++ /* BUG_ON(!frag) but that'll happen anyway... */ ++ ++ BUG_ON(frag->ofs != start); ++ ++ /* First grow down... */ ++ while((frag = frag_prev(frag)) && frag->ofs >= min) { ++ ++ /* If the previous frag doesn't even reach the beginning, there's ++ excessive fragmentation. Just merge. */ ++ if (frag->ofs > min) { ++ D1(printk(KERN_DEBUG "Expanding down to cover partial frag (0x%x-0x%x)\n", ++ frag->ofs, frag->ofs+frag->size)); ++ start = frag->ofs; ++ continue; ++ } ++ /* OK. This frag holds the first byte of the page. */ ++ if (!frag->node || !frag->node->raw) { ++ D1(printk(KERN_DEBUG "First frag in page is hole (0x%x-0x%x). Not expanding down.\n", ++ frag->ofs, frag->ofs+frag->size)); ++ break; ++ } else { ++ ++ /* OK, it's a frag which extends to the beginning of the page. Does it live ++ in a block which is still considered clean? If so, don't obsolete it. ++ If not, cover it anyway. */ ++ ++ struct jffs2_raw_node_ref *raw = frag->node->raw; ++ struct jffs2_eraseblock *jeb; ++ ++ jeb = &c->blocks[raw->flash_offset / c->sector_size]; ++ ++ if (jeb == c->gcblock) { ++ D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in gcblock at %08x\n", ++ frag->ofs, frag->ofs+frag->size, ref_offset(raw))); ++ start = frag->ofs; ++ break; + } ++ if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { ++ D1(printk(KERN_DEBUG "Not expanding down to cover frag (0x%x-0x%x) in clean block %08x\n", ++ frag->ofs, frag->ofs+frag->size, jeb->offset)); ++ break; ++ } ++ ++ D1(printk(KERN_DEBUG "Expanding down to cover frag (0x%x-0x%x) in dirty block %08x\n", ++ frag->ofs, frag->ofs+frag->size, jeb->offset)); ++ start = frag->ofs; ++ break; ++ } ++ } ++ ++ /* ... then up */ ++ ++ /* Find last frag which is actually part of the node we're to GC. */ ++ frag = jffs2_lookup_node_frag(&f->fragtree, end-1); ++ ++ while((frag = frag_next(frag)) && frag->ofs+frag->size <= max) { ++ ++ /* If the previous frag doesn't even reach the beginning, there's lots ++ of fragmentation. Just merge. */ ++ if (frag->ofs+frag->size < max) { ++ D1(printk(KERN_DEBUG "Expanding up to cover partial frag (0x%x-0x%x)\n", ++ frag->ofs, frag->ofs+frag->size)); ++ end = frag->ofs + frag->size; ++ continue; ++ } ++ ++ if (!frag->node || !frag->node->raw) { ++ D1(printk(KERN_DEBUG "Last frag in page is hole (0x%x-0x%x). Not expanding up.\n", ++ frag->ofs, frag->ofs+frag->size)); ++ break; ++ } else { ++ ++ /* OK, it's a frag which extends to the beginning of the page. Does it live ++ in a block which is still considered clean? If so, don't obsolete it. ++ If not, cover it anyway. */ ++ ++ struct jffs2_raw_node_ref *raw = frag->node->raw; ++ struct jffs2_eraseblock *jeb; ++ ++ jeb = &c->blocks[raw->flash_offset / c->sector_size]; ++ ++ if (jeb == c->gcblock) { ++ D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in gcblock at %08x\n", ++ frag->ofs, frag->ofs+frag->size, ref_offset(raw))); ++ end = frag->ofs + frag->size; ++ break; ++ } ++ if (!ISDIRTY(jeb->dirty_size + jeb->wasted_size)) { ++ D1(printk(KERN_DEBUG "Not expanding up to cover frag (0x%x-0x%x) in clean block %08x\n", ++ frag->ofs, frag->ofs+frag->size, jeb->offset)); ++ break; ++ } ++ ++ D1(printk(KERN_DEBUG "Expanding up to cover frag (0x%x-0x%x) in dirty block %08x\n", ++ frag->ofs, frag->ofs+frag->size, jeb->offset)); ++ end = frag->ofs + frag->size; ++ break; ++ } ++ } ++ D1(printk(KERN_DEBUG "Expanded dnode to write from (0x%x-0x%x) to (0x%x-0x%x)\n", ++ orig_start, orig_end, start, end)); ++ ++ BUG_ON(end > JFFS2_F_I_SIZE(f)); ++ BUG_ON(end < orig_end); ++ BUG_ON(start > orig_start); + } + + /* First, use readpage() to read the appropriate page into the page cache */ +@@ -623,63 +1165,57 @@ + * page OK. We'll actually write it out again in commit_write, which is a little + * suboptimal, but at least we're correct. + */ +- pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); ++ pg_ptr = jffs2_gc_fetch_page(c, f, start, &pg); + +- if (IS_ERR(pg)) { +- printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg)); +- return PTR_ERR(pg); ++ if (IS_ERR(pg_ptr)) { ++ printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg_ptr)); ++ return PTR_ERR(pg_ptr); + } +- pg_ptr = (char *)kmap(pg); +- comprbuf = kmalloc(end - start, GFP_KERNEL); + + offset = start; + while(offset < orig_end) { +- __u32 datalen; +- __u32 cdatalen; ++ uint32_t datalen; ++ uint32_t cdatalen; + char comprtype = JFFS2_COMPR_NONE; + + ret = jffs2_reserve_space_gc(c, sizeof(ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen); + + if (ret) { +- printk(KERN_WARNING "jffs2_reserve_space_gc of %d bytes for garbage_collect_dnode failed: %d\n", ++ printk(KERN_WARNING "jffs2_reserve_space_gc of %zd bytes for garbage_collect_dnode failed: %d\n", + sizeof(ri)+ JFFS2_MIN_DATA_LEN, ret); + break; + } +- cdatalen = min(alloclen - sizeof(ri), end - offset); ++ cdatalen = min_t(uint32_t, alloclen - sizeof(ri), end - offset); + datalen = end - offset; + + writebuf = pg_ptr + (offset & (PAGE_CACHE_SIZE -1)); + +- if (comprbuf) { +- comprtype = jffs2_compress(writebuf, comprbuf, &datalen, &cdatalen); +- } +- if (comprtype) { +- writebuf = comprbuf; +- } else { +- datalen = cdatalen; +- } +- ri.magic = JFFS2_MAGIC_BITMASK; +- ri.nodetype = JFFS2_NODETYPE_INODE; +- ri.totlen = sizeof(ri) + cdatalen; +- ri.hdr_crc = crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4); +- +- ri.ino = inode->i_ino; +- ri.version = ++f->highest_version; +- ri.mode = inode->i_mode; +- ri.uid = inode->i_uid; +- ri.gid = inode->i_gid; +- ri.isize = inode->i_size; +- ri.atime = inode->i_atime; +- ri.ctime = inode->i_ctime; +- ri.mtime = inode->i_mtime; +- ri.offset = offset; +- ri.csize = cdatalen; +- ri.dsize = datalen; ++ comprtype = jffs2_compress(writebuf, &comprbuf, &datalen, &cdatalen); ++ ++ ri.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri.nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri.totlen = cpu_to_je32(sizeof(ri) + cdatalen); ++ ri.hdr_crc = cpu_to_je32(crc32(0, &ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri.ino = cpu_to_je32(f->inocache->ino); ++ ri.version = cpu_to_je32(++f->highest_version); ++ ri.mode = cpu_to_jemode(JFFS2_F_I_MODE(f)); ++ ri.uid = cpu_to_je16(JFFS2_F_I_UID(f)); ++ ri.gid = cpu_to_je16(JFFS2_F_I_GID(f)); ++ ri.isize = cpu_to_je32(JFFS2_F_I_SIZE(f)); ++ ri.atime = cpu_to_je32(JFFS2_F_I_ATIME(f)); ++ ri.ctime = cpu_to_je32(JFFS2_F_I_CTIME(f)); ++ ri.mtime = cpu_to_je32(JFFS2_F_I_MTIME(f)); ++ ri.offset = cpu_to_je32(offset); ++ ri.csize = cpu_to_je32(cdatalen); ++ ri.dsize = cpu_to_je32(datalen); + ri.compr = comprtype; +- ri.node_crc = crc32(0, &ri, sizeof(ri)-8); +- ri.data_crc = crc32(0, writebuf, cdatalen); ++ ri.node_crc = cpu_to_je32(crc32(0, &ri, sizeof(ri)-8)); ++ ri.data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); ++ ++ new_fn = jffs2_write_dnode(c, f, &ri, comprbuf, cdatalen, phys_ofs, ALLOC_GC); + +- new_fn = jffs2_write_dnode(inode, &ri, writebuf, cdatalen, phys_ofs, NULL); ++ jffs2_free_comprbuf(comprbuf, writebuf); + + if (IS_ERR(new_fn)) { + printk(KERN_WARNING "Error writing new dnode: %ld\n", PTR_ERR(new_fn)); +@@ -694,12 +1230,8 @@ + f->metadata = NULL; + } + } +- if (comprbuf) kfree(comprbuf); + +- kunmap(pg); +- /* XXX: Does the page get freed automatically? */ +- /* AAA: Judging by the unmount getting stuck in __wait_on_page, nope. */ +- page_cache_release(pg); ++ jffs2_gc_release_page(c, pg_ptr, &pg); + return ret; + } + +diff -Nurb linux-mips-2.4.27/fs/jffs2/ioctl.c linux/fs/jffs2/ioctl.c +--- linux-mips-2.4.27/fs/jffs2/ioctl.c 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/ioctl.c 2004-11-19 10:25:12.108168024 +0100 +@@ -1,37 +1,13 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: ioctl.c,v 1.5 2001/03/15 15:38:24 dwmw2 Exp $ ++ * $Id: ioctl.c,v 1.8 2003/10/28 16:16:28 dwmw2 Exp $ + * + */ + +@@ -42,6 +18,6 @@ + { + /* Later, this will provide for lsattr.jffs2 and chattr.jffs2, which + will include compression support etc. */ +- return -EINVAL; ++ return -ENOTTY; + } + +diff -Nurb linux-mips-2.4.27/fs/jffs2/malloc.c linux/fs/jffs2/malloc.c +--- linux-mips-2.4.27/fs/jffs2/malloc.c 2001-10-19 03:24:56.000000000 +0200 ++++ linux/fs/jffs2/malloc.c 2004-11-19 10:25:12.110167720 +0100 +@@ -1,37 +1,13 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: malloc.c,v 1.16 2001/03/15 15:38:24 dwmw2 Exp $ ++ * $Id: malloc.c,v 1.27 2003/10/28 17:14:58 dwmw2 Exp $ + * + */ + +@@ -47,6 +23,9 @@ + #define JFFS2_SLAB_POISON 0 + #endif + ++// replace this by #define D3 (x) x for cache debugging ++#define D3(x) ++ + /* These are initialised to NULL in the kernel startup code. + If you're porting to other operating systems, beware */ + static kmem_cache_t *full_dnode_slab; +@@ -57,57 +36,47 @@ + static kmem_cache_t *node_frag_slab; + static kmem_cache_t *inode_cache_slab; + +-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn) +-{ +- struct jffs2_tmp_dnode_info *next; +- +- while (tn) { +- next = tn; +- tn = tn->next; +- jffs2_free_full_dnode(next->fn); +- jffs2_free_tmp_dnode_info(next); +- } +-} +- +-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) +-{ +- struct jffs2_full_dirent *next; +- +- while (fd) { +- next = fd->next; +- jffs2_free_full_dirent(fd); +- fd = next; +- } +-} +- + int __init jffs2_create_slab_caches(void) + { +- full_dnode_slab = kmem_cache_create("jffs2_full_dnode", sizeof(struct jffs2_full_dnode), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ full_dnode_slab = kmem_cache_create("jffs2_full_dnode", ++ sizeof(struct jffs2_full_dnode), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!full_dnode_slab) + goto err; + +- raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", sizeof(struct jffs2_raw_dirent), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ raw_dirent_slab = kmem_cache_create("jffs2_raw_dirent", ++ sizeof(struct jffs2_raw_dirent), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!raw_dirent_slab) + goto err; + +- raw_inode_slab = kmem_cache_create("jffs2_raw_inode", sizeof(struct jffs2_raw_inode), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ raw_inode_slab = kmem_cache_create("jffs2_raw_inode", ++ sizeof(struct jffs2_raw_inode), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!raw_inode_slab) + goto err; + +- tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", sizeof(struct jffs2_tmp_dnode_info), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ tmp_dnode_info_slab = kmem_cache_create("jffs2_tmp_dnode", ++ sizeof(struct jffs2_tmp_dnode_info), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!tmp_dnode_info_slab) + goto err; + +- raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", sizeof(struct jffs2_raw_node_ref), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ raw_node_ref_slab = kmem_cache_create("jffs2_raw_node_ref", ++ sizeof(struct jffs2_raw_node_ref), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!raw_node_ref_slab) + goto err; + +- node_frag_slab = kmem_cache_create("jffs2_node_frag", sizeof(struct jffs2_node_frag), 0, JFFS2_SLAB_POISON, NULL, NULL); ++ node_frag_slab = kmem_cache_create("jffs2_node_frag", ++ sizeof(struct jffs2_node_frag), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (!node_frag_slab) + goto err; + +- inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), 0, JFFS2_SLAB_POISON, NULL, NULL); +- ++ inode_cache_slab = kmem_cache_create("jffs2_inode_cache", ++ sizeof(struct jffs2_inode_cache), ++ 0, JFFS2_SLAB_POISON, NULL, NULL); + if (inode_cache_slab) + return 0; + err: +@@ -131,7 +100,6 @@ + kmem_cache_destroy(node_frag_slab); + if(inode_cache_slab) + kmem_cache_destroy(inode_cache_slab); +- + } + + struct jffs2_full_dirent *jffs2_alloc_full_dirent(int namesize) +@@ -146,75 +114,92 @@ + + struct jffs2_full_dnode *jffs2_alloc_full_dnode(void) + { +- void *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL); ++ struct jffs2_full_dnode *ret = kmem_cache_alloc(full_dnode_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_full_dnode at %p\n", ret)); + return ret; + } + + void jffs2_free_full_dnode(struct jffs2_full_dnode *x) + { ++ D3 (printk (KERN_DEBUG "free full_dnode at %p\n", x)); + kmem_cache_free(full_dnode_slab, x); + } + + struct jffs2_raw_dirent *jffs2_alloc_raw_dirent(void) + { +- return kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL); ++ struct jffs2_raw_dirent *ret = kmem_cache_alloc(raw_dirent_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_raw_dirent\n", ret)); ++ return ret; + } + + void jffs2_free_raw_dirent(struct jffs2_raw_dirent *x) + { ++ D3 (printk (KERN_DEBUG "free_raw_dirent at %p\n", x)); + kmem_cache_free(raw_dirent_slab, x); + } + + struct jffs2_raw_inode *jffs2_alloc_raw_inode(void) + { +- return kmem_cache_alloc(raw_inode_slab, GFP_KERNEL); ++ struct jffs2_raw_inode *ret = kmem_cache_alloc(raw_inode_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_raw_inode at %p\n", ret)); ++ return ret; + } + + void jffs2_free_raw_inode(struct jffs2_raw_inode *x) + { ++ D3 (printk (KERN_DEBUG "free_raw_inode at %p\n", x)); + kmem_cache_free(raw_inode_slab, x); + } + + struct jffs2_tmp_dnode_info *jffs2_alloc_tmp_dnode_info(void) + { +- return kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL); ++ struct jffs2_tmp_dnode_info *ret = kmem_cache_alloc(tmp_dnode_info_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_tmp_dnode_info at %p\n", ret)); ++ return ret; + } + + void jffs2_free_tmp_dnode_info(struct jffs2_tmp_dnode_info *x) + { ++ D3 (printk (KERN_DEBUG "free_tmp_dnode_info at %p\n", x)); + kmem_cache_free(tmp_dnode_info_slab, x); + } + + struct jffs2_raw_node_ref *jffs2_alloc_raw_node_ref(void) + { +- return kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); ++ struct jffs2_raw_node_ref *ret = kmem_cache_alloc(raw_node_ref_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_raw_node_ref at %p\n", ret)); ++ return ret; + } + + void jffs2_free_raw_node_ref(struct jffs2_raw_node_ref *x) + { ++ D3 (printk (KERN_DEBUG "free_raw_node_ref at %p\n", x)); + kmem_cache_free(raw_node_ref_slab, x); + } + + struct jffs2_node_frag *jffs2_alloc_node_frag(void) + { +- return kmem_cache_alloc(node_frag_slab, GFP_KERNEL); ++ struct jffs2_node_frag *ret = kmem_cache_alloc(node_frag_slab, GFP_KERNEL); ++ D3 (printk (KERN_DEBUG "alloc_node_frag at %p\n", ret)); ++ return ret; + } + + void jffs2_free_node_frag(struct jffs2_node_frag *x) + { ++ D3 (printk (KERN_DEBUG "free_node_frag at %p\n", x)); + kmem_cache_free(node_frag_slab, x); + } + + struct jffs2_inode_cache *jffs2_alloc_inode_cache(void) + { + struct jffs2_inode_cache *ret = kmem_cache_alloc(inode_cache_slab, GFP_KERNEL); +- D1(printk(KERN_DEBUG "Allocated inocache at %p\n", ret)); ++ D3 (printk(KERN_DEBUG "Allocated inocache at %p\n", ret)); + return ret; + } + + void jffs2_free_inode_cache(struct jffs2_inode_cache *x) + { +- D1(printk(KERN_DEBUG "Freeing inocache at %p\n", x)); ++ D3 (printk(KERN_DEBUG "Freeing inocache at %p\n", x)); + kmem_cache_free(inode_cache_slab, x); + } + +diff -Nurb linux-mips-2.4.27/fs/jffs2/nodelist.c linux/fs/jffs2/nodelist.c +--- linux-mips-2.4.27/fs/jffs2/nodelist.c 2003-07-05 05:23:44.000000000 +0200 ++++ linux/fs/jffs2/nodelist.c 2004-11-19 10:25:12.112167416 +0100 +@@ -1,44 +1,24 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001, 2002 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: nodelist.c,v 1.30.2.6 2003/02/24 21:49:33 dwmw2 Exp $ ++ * $Id: nodelist.c,v 1.86 2003/10/31 15:37:51 dwmw2 Exp $ + * + */ + + #include <linux/kernel.h> +-#include <linux/jffs2.h> ++#include <linux/sched.h> + #include <linux/fs.h> + #include <linux/mtd/mtd.h> ++#include <linux/rbtree.h> ++#include <linux/crc32.h> ++#include <linux/slab.h> ++#include <linux/pagemap.h> + #include "nodelist.h" + + void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) +@@ -78,7 +58,7 @@ + /* Put a new tmp_dnode_info into the list, keeping the list in + order of increasing version + */ +-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) ++static void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list) + { + struct jffs2_tmp_dnode_info **prev = list; + +@@ -89,13 +69,37 @@ + *prev = tn; + } + ++static void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn) ++{ ++ struct jffs2_tmp_dnode_info *next; ++ ++ while (tn) { ++ next = tn; ++ tn = tn->next; ++ jffs2_free_full_dnode(next->fn); ++ jffs2_free_tmp_dnode_info(next); ++ } ++} ++ ++static void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd) ++{ ++ struct jffs2_full_dirent *next; ++ ++ while (fd) { ++ next = fd->next; ++ jffs2_free_full_dirent(fd); ++ fd = next; ++ } ++} ++ ++ + /* Get tmp_dnode_info and full_dirent for all non-obsolete nodes associated + with this ino, returning the former in order of version */ + + int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, + struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, +- __u32 *highest_version, __u32 *latest_mctime, +- __u32 *mctime_ver) ++ uint32_t *highest_version, uint32_t *latest_mctime, ++ uint32_t *mctime_ver) + { + struct jffs2_raw_node_ref *ref = f->inocache->nodes; + struct jffs2_tmp_dnode_info *tn, *ret_tn = NULL; +@@ -109,43 +113,71 @@ + + D1(printk(KERN_DEBUG "jffs2_get_inode_nodes(): ino #%lu\n", ino)); + if (!f->inocache->nodes) { +- printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", ino); ++ printk(KERN_WARNING "Eep. no nodes for ino #%lu\n", (unsigned long)ino); + } ++ ++ spin_lock(&c->erase_completion_lock); ++ + for (ref = f->inocache->nodes; ref && ref->next_in_ino; ref = ref->next_in_ino) { + /* Work out whether it's a data node or a dirent node */ +- if (ref->flash_offset & 1) { ++ if (ref_obsolete(ref)) { + /* FIXME: On NAND flash we may need to read these */ +- D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref->flash_offset &~3)); ++ D1(printk(KERN_DEBUG "node at 0x%08x is obsoleted. Ignoring.\n", ref_offset(ref))); + continue; + } +- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3), min(ref->totlen, sizeof(node)), &retlen, (void *)&node); ++ /* We can hold a pointer to a non-obsolete node without the spinlock, ++ but _obsolete_ nodes may disappear at any time, if the block ++ they're in gets erased */ ++ spin_unlock(&c->erase_completion_lock); ++ ++ cond_resched(); ++ ++ /* FIXME: point() */ ++ err = jffs2_flash_read(c, (ref_offset(ref)), ++ min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node)), ++ &retlen, (void *)&node); + if (err) { +- printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, (ref->flash_offset) & ~3); ++ printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref)); + goto free_out; + } + + + /* Check we've managed to read at least the common node header */ +- if (retlen < min(ref->totlen, sizeof(node.u))) { ++ if (retlen < min_t(uint32_t, ref_totlen(c, NULL, ref), sizeof(node.u))) { + printk(KERN_WARNING "short read in get_inode_nodes()\n"); + err = -EIO; + goto free_out; + } + +- switch (node.u.nodetype) { ++ switch (je16_to_cpu(node.u.nodetype)) { + case JFFS2_NODETYPE_DIRENT: +- D1(printk(KERN_DEBUG "Node at %08x is a dirent node\n", ref->flash_offset &~3)); ++ D1(printk(KERN_DEBUG "Node at %08x (%d) is a dirent node\n", ref_offset(ref), ref_flags(ref))); ++ if (ref_flags(ref) == REF_UNCHECKED) { ++ printk(KERN_WARNING "BUG: Dirent node at 0x%08x never got checked? How?\n", ref_offset(ref)); ++ BUG(); ++ } + if (retlen < sizeof(node.d)) { + printk(KERN_WARNING "short read in get_inode_nodes()\n"); + err = -EIO; + goto free_out; + } +- if (node.d.version > *highest_version) +- *highest_version = node.d.version; +- if (ref->flash_offset & 1) { +- /* Obsoleted */ ++ /* sanity check */ ++ if (PAD((node.d.nsize + sizeof (node.d))) != PAD(je32_to_cpu (node.d.totlen))) { ++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Illegal nsize in node at 0x%08x: nsize 0x%02x, totlen %04x\n", ++ ref_offset(ref), node.d.nsize, je32_to_cpu(node.d.totlen)); ++ jffs2_mark_node_obsolete(c, ref); ++ spin_lock(&c->erase_completion_lock); + continue; + } ++ if (je32_to_cpu(node.d.version) > *highest_version) ++ *highest_version = je32_to_cpu(node.d.version); ++ if (ref_obsolete(ref)) { ++ /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ ++ printk(KERN_ERR "Dirent node at 0x%08x became obsolete while we weren't looking\n", ++ ref_offset(ref)); ++ BUG(); ++ } ++ + fd = jffs2_alloc_full_dirent(node.d.nsize+1); + if (!fd) { + err = -ENOMEM; +@@ -153,29 +185,30 @@ + } + memset(fd,0,sizeof(struct jffs2_full_dirent) + node.d.nsize+1); + fd->raw = ref; +- fd->version = node.d.version; +- fd->ino = node.d.ino; ++ fd->version = je32_to_cpu(node.d.version); ++ fd->ino = je32_to_cpu(node.d.ino); + fd->type = node.d.type; + + /* Pick out the mctime of the latest dirent */ + if(fd->version > *mctime_ver) { + *mctime_ver = fd->version; +- *latest_mctime = node.d.mctime; ++ *latest_mctime = je32_to_cpu(node.d.mctime); + } + + /* memcpy as much of the name as possible from the raw + dirent we've already read from the flash + */ + if (retlen > sizeof(struct jffs2_raw_dirent)) +- memcpy(&fd->name[0], &node.d.name[0], min((__u32)node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent)))); ++ memcpy(&fd->name[0], &node.d.name[0], min_t(uint32_t, node.d.nsize, (retlen-sizeof(struct jffs2_raw_dirent)))); + + /* Do we need to copy any more of the name directly + from the flash? + */ + if (node.d.nsize + sizeof(struct jffs2_raw_dirent) > retlen) { ++ /* FIXME: point() */ + int already = retlen - sizeof(struct jffs2_raw_dirent); + +- err = c->mtd->read(c->mtd, (ref->flash_offset & ~3) + retlen, ++ err = jffs2_flash_read(c, (ref_offset(ref)) + retlen, + node.d.nsize - already, &retlen, &fd->name[already]); + if (!err && retlen != node.d.nsize - already) + err = -EIO; +@@ -196,21 +229,126 @@ + break; + + case JFFS2_NODETYPE_INODE: +- D1(printk(KERN_DEBUG "Node at %08x is a data node\n", ref->flash_offset &~3)); ++ D1(printk(KERN_DEBUG "Node at %08x (%d) is a data node\n", ref_offset(ref), ref_flags(ref))); + if (retlen < sizeof(node.i)) { + printk(KERN_WARNING "read too short for dnode\n"); + err = -EIO; + goto free_out; + } +- if (node.i.version > *highest_version) +- *highest_version = node.i.version; +- D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", node.i.version, *highest_version)); +- +- if (ref->flash_offset & 1) { +- D1(printk(KERN_DEBUG "obsoleted\n")); +- /* Obsoleted */ ++ if (je32_to_cpu(node.i.version) > *highest_version) ++ *highest_version = je32_to_cpu(node.i.version); ++ D1(printk(KERN_DEBUG "version %d, highest_version now %d\n", je32_to_cpu(node.i.version), *highest_version)); ++ ++ if (ref_obsolete(ref)) { ++ /* Obsoleted. This cannot happen, surely? dwmw2 20020308 */ ++ printk(KERN_ERR "Inode node at 0x%08x became obsolete while we weren't looking\n", ++ ref_offset(ref)); ++ BUG(); ++ } ++ ++ /* If we've never checked the CRCs on this node, check them now. */ ++ if (ref_flags(ref) == REF_UNCHECKED) { ++ uint32_t crc, len; ++ struct jffs2_eraseblock *jeb; ++ ++ crc = crc32(0, &node, sizeof(node.i)-8); ++ if (crc != je32_to_cpu(node.i.node_crc)) { ++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(ref), je32_to_cpu(node.i.node_crc), crc); ++ jffs2_mark_node_obsolete(c, ref); ++ spin_lock(&c->erase_completion_lock); + continue; + } ++ ++ /* sanity checks */ ++ if ( je32_to_cpu(node.i.offset) > je32_to_cpu(node.i.isize) || ++ PAD(je32_to_cpu(node.i.csize) + sizeof (node.i)) != PAD(je32_to_cpu(node.i.totlen))) { ++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Inode corrupted at 0x%08x, totlen %d, #ino %d, version %d, isize %d, csize %d, dsize %d \n", ++ ref_offset(ref), je32_to_cpu(node.i.totlen), je32_to_cpu(node.i.ino), ++ je32_to_cpu(node.i.version), je32_to_cpu(node.i.isize), ++ je32_to_cpu(node.i.csize), je32_to_cpu(node.i.dsize)); ++ jffs2_mark_node_obsolete(c, ref); ++ spin_lock(&c->erase_completion_lock); ++ continue; ++ } ++ ++ if (node.i.compr != JFFS2_COMPR_ZERO && je32_to_cpu(node.i.csize)) { ++ unsigned char *buf=NULL; ++ uint32_t pointed = 0; ++#ifndef __ECOS ++ if (c->mtd->point) { ++ err = c->mtd->point (c->mtd, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize), ++ &retlen, &buf); ++ if (!err && retlen < je32_to_cpu(node.i.csize)) { ++ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", retlen)); ++ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize)); ++ } else if (err){ ++ D1(printk(KERN_DEBUG "MTD point failed %d\n", err)); ++ } else ++ pointed = 1; /* succefully pointed to device */ ++ } ++#endif ++ if(!pointed){ ++ buf = kmalloc(je32_to_cpu(node.i.csize), GFP_KERNEL); ++ if (!buf) ++ return -ENOMEM; ++ ++ err = jffs2_flash_read(c, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize), ++ &retlen, buf); ++ if (!err && retlen != je32_to_cpu(node.i.csize)) ++ err = -EIO; ++ if (err) { ++ kfree(buf); ++ return err; ++ } ++ } ++ crc = crc32(0, buf, je32_to_cpu(node.i.csize)); ++ if(!pointed) ++ kfree(buf); ++#ifndef __ECOS ++ else ++ c->mtd->unpoint(c->mtd, buf, ref_offset(ref) + sizeof(node.i), je32_to_cpu(node.i.csize)); ++#endif ++ ++ if (crc != je32_to_cpu(node.i.data_crc)) { ++ printk(KERN_NOTICE "jffs2_get_inode_nodes(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ref_offset(ref), je32_to_cpu(node.i.data_crc), crc); ++ jffs2_mark_node_obsolete(c, ref); ++ spin_lock(&c->erase_completion_lock); ++ continue; ++ } ++ ++ } ++ ++ /* Mark the node as having been checked and fix the accounting accordingly */ ++ spin_lock(&c->erase_completion_lock); ++ jeb = &c->blocks[ref->flash_offset / c->sector_size]; ++ len = ref_totlen(c, jeb, ref); ++ ++ jeb->used_size += len; ++ jeb->unchecked_size -= len; ++ c->used_size += len; ++ c->unchecked_size -= len; ++ ++ /* If node covers at least a whole page, or if it starts at the ++ beginning of a page and runs to the end of the file, or if ++ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. ++ ++ If it's actually overlapped, it'll get made NORMAL (or OBSOLETE) ++ when the overlapping node(s) get added to the tree anyway. ++ */ ++ if ((je32_to_cpu(node.i.dsize) >= PAGE_CACHE_SIZE) || ++ ( ((je32_to_cpu(node.i.offset)&(PAGE_CACHE_SIZE-1))==0) && ++ (je32_to_cpu(node.i.dsize)+je32_to_cpu(node.i.offset) == je32_to_cpu(node.i.isize)))) { ++ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_PRISTINE\n", ref_offset(ref))); ++ ref->flash_offset = ref_offset(ref) | REF_PRISTINE; ++ } else { ++ D1(printk(KERN_DEBUG "Marking node at 0x%08x REF_NORMAL\n", ref_offset(ref))); ++ ref->flash_offset = ref_offset(ref) | REF_NORMAL; ++ } ++ spin_unlock(&c->erase_completion_lock); ++ } ++ + tn = jffs2_alloc_tmp_dnode_info(); + if (!tn) { + D1(printk(KERN_DEBUG "alloc tn failed\n")); +@@ -225,36 +363,76 @@ + jffs2_free_tmp_dnode_info(tn); + goto free_out; + } +- tn->version = node.i.version; +- tn->fn->ofs = node.i.offset; ++ tn->version = je32_to_cpu(node.i.version); ++ tn->fn->ofs = je32_to_cpu(node.i.offset); + /* There was a bug where we wrote hole nodes out with + csize/dsize swapped. Deal with it */ +- if (node.i.compr == JFFS2_COMPR_ZERO && !node.i.dsize && node.i.csize) +- tn->fn->size = node.i.csize; ++ if (node.i.compr == JFFS2_COMPR_ZERO && !je32_to_cpu(node.i.dsize) && je32_to_cpu(node.i.csize)) ++ tn->fn->size = je32_to_cpu(node.i.csize); + else // normal case... +- tn->fn->size = node.i.dsize; ++ tn->fn->size = je32_to_cpu(node.i.dsize); + tn->fn->raw = ref; +- D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ref->flash_offset &~3, node.i.version, node.i.offset, node.i.dsize)); ++ D1(printk(KERN_DEBUG "dnode @%08x: ver %u, offset %04x, dsize %04x\n", ++ ref_offset(ref), je32_to_cpu(node.i.version), ++ je32_to_cpu(node.i.offset), je32_to_cpu(node.i.dsize))); + jffs2_add_tn_to_list(tn, &ret_tn); + break; + + default: +- switch(node.u.nodetype & JFFS2_COMPAT_MASK) { ++ if (ref_flags(ref) == REF_UNCHECKED) { ++ struct jffs2_eraseblock *jeb; ++ uint32_t len; ++ ++ printk(KERN_ERR "Eep. Unknown node type %04x at %08x was marked REF_UNCHECKED\n", ++ je16_to_cpu(node.u.nodetype), ref_offset(ref)); ++ ++ /* Mark the node as having been checked and fix the accounting accordingly */ ++ spin_lock(&c->erase_completion_lock); ++ jeb = &c->blocks[ref->flash_offset / c->sector_size]; ++ len = ref_totlen(c, jeb, ref); ++ ++ jeb->used_size += len; ++ jeb->unchecked_size -= len; ++ c->used_size += len; ++ c->unchecked_size -= len; ++ ++ mark_ref_normal(ref); ++ spin_unlock(&c->erase_completion_lock); ++ } ++ node.u.nodetype = cpu_to_je16(JFFS2_NODE_ACCURATE | je16_to_cpu(node.u.nodetype)); ++ if (crc32(0, &node, sizeof(struct jffs2_unknown_node)-4) != je32_to_cpu(node.u.hdr_crc)) { ++ /* Hmmm. This should have been caught at scan time. */ ++ printk(KERN_ERR "Node header CRC failed at %08x. But it must have been OK earlier.\n", ++ ref_offset(ref)); ++ printk(KERN_ERR "Node was: { %04x, %04x, %08x, %08x }\n", ++ je16_to_cpu(node.u.magic), je16_to_cpu(node.u.nodetype), je32_to_cpu(node.u.totlen), ++ je32_to_cpu(node.u.hdr_crc)); ++ jffs2_mark_node_obsolete(c, ref); ++ } else switch(je16_to_cpu(node.u.nodetype) & JFFS2_COMPAT_MASK) { + case JFFS2_FEATURE_INCOMPAT: +- printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); ++ printk(KERN_NOTICE "Unknown INCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); ++ /* EEP */ ++ BUG(); + break; + case JFFS2_FEATURE_ROCOMPAT: +- printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); ++ printk(KERN_NOTICE "Unknown ROCOMPAT nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); ++ if (!(c->flags & JFFS2_SB_FLAG_RO)) ++ BUG(); + break; + case JFFS2_FEATURE_RWCOMPAT_COPY: +- printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); ++ printk(KERN_NOTICE "Unknown RWCOMPAT_COPY nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); + break; + case JFFS2_FEATURE_RWCOMPAT_DELETE: +- printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08X\n", node.u.nodetype, ref->flash_offset & ~3); ++ printk(KERN_NOTICE "Unknown RWCOMPAT_DELETE nodetype %04X at %08x\n", je16_to_cpu(node.u.nodetype), ref_offset(ref)); ++ jffs2_mark_node_obsolete(c, ref); + break; + } ++ + } ++ spin_lock(&c->erase_completion_lock); ++ + } ++ spin_unlock(&c->erase_completion_lock); + *tnp = ret_tn; + *fdp = ret_fd; + +@@ -266,19 +444,30 @@ + return err; + } + ++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state) ++{ ++ spin_lock(&c->inocache_lock); ++ ic->state = state; ++ wake_up(&c->inocache_wq); ++ spin_unlock(&c->inocache_lock); ++} ++ ++/* During mount, this needs no locking. During normal operation, its ++ callers want to do other stuff while still holding the inocache_lock. ++ Rather than introducing special case get_ino_cache functions or ++ callbacks, we just let the caller do the locking itself. */ ++ + struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino) + { + struct jffs2_inode_cache *ret; + + D2(printk(KERN_DEBUG "jffs2_get_ino_cache(): ino %u\n", ino)); +- spin_lock (&c->inocache_lock); ++ + ret = c->inocache_list[ino % INOCACHE_HASHSIZE]; + while (ret && ret->ino < ino) { + ret = ret->next; + } + +- spin_unlock(&c->inocache_lock); +- + if (ret && ret->ino != ino) + ret = NULL; + +@@ -299,6 +488,7 @@ + } + new->next = *prev; + *prev = new; ++ + spin_unlock(&c->inocache_lock); + } + +@@ -316,6 +506,7 @@ + if ((*prev) == old) { + *prev = old->next; + } ++ + spin_unlock(&c->inocache_lock); + } + +@@ -352,3 +543,128 @@ + } + } + ++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset) ++{ ++ /* The common case in lookup is that there will be a node ++ which precisely matches. So we go looking for that first */ ++ struct rb_node *next; ++ struct jffs2_node_frag *prev = NULL; ++ struct jffs2_node_frag *frag = NULL; ++ ++ D2(printk(KERN_DEBUG "jffs2_lookup_node_frag(%p, %d)\n", fragtree, offset)); ++ ++ next = fragtree->rb_node; ++ ++ while(next) { ++ frag = rb_entry(next, struct jffs2_node_frag, rb); ++ ++ D2(printk(KERN_DEBUG "Considering frag %d-%d (%p). left %p, right %p\n", ++ frag->ofs, frag->ofs+frag->size, frag, frag->rb.rb_left, frag->rb.rb_right)); ++ if (frag->ofs + frag->size <= offset) { ++ D2(printk(KERN_DEBUG "Going right from frag %d-%d, before the region we care about\n", ++ frag->ofs, frag->ofs+frag->size)); ++ /* Remember the closest smaller match on the way down */ ++ if (!prev || frag->ofs > prev->ofs) ++ prev = frag; ++ next = frag->rb.rb_right; ++ } else if (frag->ofs > offset) { ++ D2(printk(KERN_DEBUG "Going left from frag %d-%d, after the region we care about\n", ++ frag->ofs, frag->ofs+frag->size)); ++ next = frag->rb.rb_left; ++ } else { ++ D2(printk(KERN_DEBUG "Returning frag %d,%d, matched\n", ++ frag->ofs, frag->ofs+frag->size)); ++ return frag; ++ } ++ } ++ ++ /* Exact match not found. Go back up looking at each parent, ++ and return the closest smaller one */ ++ ++ if (prev) ++ D2(printk(KERN_DEBUG "No match. Returning frag %d,%d, closest previous\n", ++ prev->ofs, prev->ofs+prev->size)); ++ else ++ D2(printk(KERN_DEBUG "Returning NULL, empty fragtree\n")); ++ ++ return prev; ++} ++ ++/* Pass 'c' argument to indicate that nodes should be marked obsolete as ++ they're killed. */ ++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c) ++{ ++ struct jffs2_node_frag *frag; ++ struct jffs2_node_frag *parent; ++ ++ if (!root->rb_node) ++ return; ++ ++ frag = (rb_entry(root->rb_node, struct jffs2_node_frag, rb)); ++ ++ while(frag) { ++ if (frag->rb.rb_left) { ++ D2(printk(KERN_DEBUG "Going left from frag (%p) %d-%d\n", ++ frag, frag->ofs, frag->ofs+frag->size)); ++ frag = frag_left(frag); ++ continue; ++ } ++ if (frag->rb.rb_right) { ++ D2(printk(KERN_DEBUG "Going right from frag (%p) %d-%d\n", ++ frag, frag->ofs, frag->ofs+frag->size)); ++ frag = frag_right(frag); ++ continue; ++ } ++ ++ D2(printk(KERN_DEBUG "jffs2_kill_fragtree: frag at 0x%x-0x%x: node %p, frags %d--\n", ++ frag->ofs, frag->ofs+frag->size, frag->node, ++ frag->node?frag->node->frags:0)); ++ ++ if (frag->node && !(--frag->node->frags)) { ++ /* Not a hole, and it's the final remaining frag ++ of this node. Free the node */ ++ if (c) ++ jffs2_mark_node_obsolete(c, frag->node->raw); ++ ++ jffs2_free_full_dnode(frag->node); ++ } ++ parent = frag_parent(frag); ++ if (parent) { ++ if (frag_left(parent) == frag) ++ parent->rb.rb_left = NULL; ++ else ++ parent->rb.rb_right = NULL; ++ } ++ ++ jffs2_free_node_frag(frag); ++ frag = parent; ++ ++ cond_resched(); ++ } ++} ++ ++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base) ++{ ++ struct rb_node *parent = &base->rb; ++ struct rb_node **link = &parent; ++ ++ D2(printk(KERN_DEBUG "jffs2_fragtree_insert(%p; %d-%d, %p)\n", newfrag, ++ newfrag->ofs, newfrag->ofs+newfrag->size, base)); ++ ++ while (*link) { ++ parent = *link; ++ base = rb_entry(parent, struct jffs2_node_frag, rb); ++ ++ D2(printk(KERN_DEBUG "fragtree_insert considering frag at 0x%x\n", base->ofs)); ++ if (newfrag->ofs > base->ofs) ++ link = &base->rb.rb_right; ++ else if (newfrag->ofs < base->ofs) ++ link = &base->rb.rb_left; ++ else { ++ printk(KERN_CRIT "Duplicate frag at %08x (%p,%p)\n", newfrag->ofs, newfrag, base); ++ BUG(); ++ } ++ } ++ ++ rb_link_node(&newfrag->rb, &base->rb, link); ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/nodelist.h linux/fs/jffs2/nodelist.h +--- linux-mips-2.4.27/fs/jffs2/nodelist.h 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/nodelist.h 2004-11-19 10:25:12.113167264 +0100 +@@ -1,48 +1,35 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: nodelist.h,v 1.46.2.5 2003/11/02 13:54:20 dwmw2 Exp $ ++ * $Id: nodelist.h,v 1.115 2003/11/26 15:30:58 dwmw2 Exp $ + * + */ + ++#ifndef __JFFS2_NODELIST_H__ ++#define __JFFS2_NODELIST_H__ ++ + #include <linux/config.h> + #include <linux/fs.h> +- ++#include <linux/types.h> ++#include <linux/jffs2.h> + #include <linux/jffs2_fs_sb.h> + #include <linux/jffs2_fs_i.h> + ++#ifdef __ECOS ++#include "os-ecos.h" ++#else ++#include <linux/mtd/compatmac.h> /* For min/max in older kernels */ ++#include "os-linux.h" ++#endif ++ + #ifndef CONFIG_JFFS2_FS_DEBUG +-#define CONFIG_JFFS2_FS_DEBUG 2 ++#define CONFIG_JFFS2_FS_DEBUG 1 + #endif + + #if CONFIG_JFFS2_FS_DEBUG > 0 +@@ -71,17 +58,21 @@ + for this inode instead. The inode_cache will have NULL in the first + word so you know when you've got there :) */ + struct jffs2_raw_node_ref *next_phys; +- // __u32 ino; +- __u32 flash_offset; +- __u32 totlen; +-// __u16 nodetype; ++ uint32_t flash_offset; ++ uint32_t __totlen; /* This may die; use ref_totlen(c, jeb, ) below */ ++}; + + /* flash_offset & 3 always has to be zero, because nodes are + always aligned at 4 bytes. So we have a couple of extra bits +- to play with. So we set the least significant bit to 1 to +- signify that the node is obsoleted by later nodes. +- */ +-}; ++ to play with, which indicate the node's status; see below: */ ++#define REF_UNCHECKED 0 /* We haven't yet checked the CRC or built its inode */ ++#define REF_OBSOLETE 1 /* Obsolete, can be completely ignored */ ++#define REF_PRISTINE 2 /* Completely clean. GC without looking */ ++#define REF_NORMAL 3 /* Possibly overlapped. Read the page and write again on GC */ ++#define ref_flags(ref) ((ref)->flash_offset & 3) ++#define ref_offset(ref) ((ref)->flash_offset & ~3) ++#define ref_obsolete(ref) (((ref)->flash_offset & 3) == REF_OBSOLETE) ++#define mark_ref_normal(ref) do { (ref)->flash_offset = ref_offset(ref) | REF_NORMAL; } while(0) + + /* + Used for keeping track of deletion nodes &c, which can only be marked +@@ -101,19 +92,35 @@ + a pointer to the first physical node which is part of this inode, too. + */ + struct jffs2_inode_cache { +- struct jffs2_scan_info *scan; /* Used during scan to hold +- temporary lists of nodes, and later must be set to ++ struct jffs2_full_dirent *scan_dents; /* Used during scan to hold ++ temporary lists of dirents, and later must be set to + NULL to mark the end of the raw_node_ref->next_in_ino + chain. */ + struct jffs2_inode_cache *next; + struct jffs2_raw_node_ref *nodes; +- __u32 ino; ++ uint32_t ino; + int nlink; ++ int state; + }; + ++/* Inode states for 'state' above. We need the 'GC' state to prevent ++ someone from doing a read_inode() while we're moving a 'REF_PRISTINE' ++ node without going through all the iget() nonsense */ ++#define INO_STATE_UNCHECKED 0 /* CRC checks not yet done */ ++#define INO_STATE_CHECKING 1 /* CRC checks in progress */ ++#define INO_STATE_PRESENT 2 /* In core */ ++#define INO_STATE_CHECKEDABSENT 3 /* Checked, cleared again */ ++#define INO_STATE_GC 4 /* GCing a 'pristine' node */ ++#define INO_STATE_READING 5 /* In read_inode() */ ++ ++#define INOCACHE_HASHSIZE 128 ++ + struct jffs2_scan_info { + struct jffs2_full_dirent *dents; + struct jffs2_tmp_dnode_info *tmpnodes; ++ /* Latest i_size info */ ++ uint32_t version; ++ uint32_t isize; + }; + /* + Larger representation of a raw node, kept in-core only when the +@@ -123,9 +130,9 @@ + struct jffs2_full_dnode + { + struct jffs2_raw_node_ref *raw; +- __u32 ofs; /* Don't really need this, but optimisation */ +- __u32 size; +- __u32 frags; /* Number of fragments which currently refer ++ uint32_t ofs; /* Don't really need this, but optimisation */ ++ uint32_t size; ++ uint32_t frags; /* Number of fragments which currently refer + to this node. When this reaches zero, + the node is obsolete. + */ +@@ -140,15 +147,15 @@ + { + struct jffs2_tmp_dnode_info *next; + struct jffs2_full_dnode *fn; +- __u32 version; ++ uint32_t version; + }; + + struct jffs2_full_dirent + { + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *next; +- __u32 version; +- __u32 ino; /* == zero for unlink */ ++ uint32_t version; ++ uint32_t ino; /* == zero for unlink */ + unsigned int nhash; + unsigned char type; + unsigned char name[0]; +@@ -159,21 +166,23 @@ + */ + struct jffs2_node_frag + { +- struct jffs2_node_frag *next; ++ struct rb_node rb; + struct jffs2_full_dnode *node; /* NULL for holes */ +- __u32 size; +- __u32 ofs; /* Don't really need this, but optimisation */ ++ uint32_t size; ++ uint32_t ofs; /* Don't really need this, but optimisation */ + }; + + struct jffs2_eraseblock + { + struct list_head list; + int bad_count; +- __u32 offset; /* of this block in the MTD */ ++ uint32_t offset; /* of this block in the MTD */ + +- __u32 used_size; +- __u32 dirty_size; +- __u32 free_size; /* Note that sector_size - free_size ++ uint32_t unchecked_size; ++ uint32_t used_size; ++ uint32_t dirty_size; ++ uint32_t wasted_size; ++ uint32_t free_size; /* Note that sector_size - free_size + is the address of the first free space */ + struct jffs2_raw_node_ref *first_node; + struct jffs2_raw_node_ref *last_node; +@@ -190,45 +199,134 @@ + }; + + #define ACCT_SANITY_CHECK(c, jeb) do { \ +- if (jeb->used_size + jeb->dirty_size + jeb->free_size != c->sector_size) { \ +- printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", jeb->offset); \ +- printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x != total %08x\n", \ +- jeb->free_size, jeb->dirty_size, jeb->used_size, c->sector_size); \ ++ struct jffs2_eraseblock *___j = jeb; \ ++ if ((___j) && ___j->used_size + ___j->dirty_size + ___j->free_size + ___j->wasted_size + ___j->unchecked_size != c->sector_size) { \ ++ printk(KERN_NOTICE "Eeep. Space accounting for block at 0x%08x is screwed\n", ___j->offset); \ ++ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + wasted %08x + unchecked %08x != total %08x\n", \ ++ ___j->free_size, ___j->dirty_size, ___j->used_size, ___j->wasted_size, ___j->unchecked_size, c->sector_size); \ + BUG(); \ + } \ +- if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size != c->flash_size) { \ ++ if (c->used_size + c->dirty_size + c->free_size + c->erasing_size + c->bad_size + c->wasted_size + c->unchecked_size != c->flash_size) { \ + printk(KERN_NOTICE "Eeep. Space accounting superblock info is screwed\n"); \ +- printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x != total %08x\n", \ +- c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->flash_size); \ ++ printk(KERN_NOTICE "free 0x%08x + dirty 0x%08x + used %08x + erasing %08x + bad %08x + wasted %08x + unchecked %08x != total %08x\n", \ ++ c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, c->wasted_size, c->unchecked_size, c->flash_size); \ + BUG(); \ + } \ + } while(0) + ++static inline void paranoia_failed_dump(struct jffs2_eraseblock *jeb) ++{ ++ struct jffs2_raw_node_ref *ref; ++ int i=0; ++ ++ printk(KERN_NOTICE); ++ for (ref = jeb->first_node; ref; ref = ref->next_phys) { ++ printk("%08x->", ref_offset(ref)); ++ if (++i == 8) { ++ i = 0; ++ printk("\n" KERN_NOTICE); ++ } ++ } ++ printk("\n"); ++} ++ ++ + #define ACCT_PARANOIA_CHECK(jeb) do { \ +- __u32 my_used_size = 0; \ ++ uint32_t my_used_size = 0; \ ++ uint32_t my_unchecked_size = 0; \ + struct jffs2_raw_node_ref *ref2 = jeb->first_node; \ + while (ref2) { \ +- if (!(ref2->flash_offset & 1)) \ +- my_used_size += ref2->totlen; \ ++ if (unlikely(ref2->flash_offset < jeb->offset || \ ++ ref2->flash_offset > jeb->offset + c->sector_size)) { \ ++ printk(KERN_NOTICE "Node %08x shouldn't be in block at %08x!\n", \ ++ ref_offset(ref2), jeb->offset); \ ++ paranoia_failed_dump(jeb); \ ++ BUG(); \ ++ } \ ++ if (ref_flags(ref2) == REF_UNCHECKED) \ ++ my_unchecked_size += ref_totlen(c, jeb, ref2); \ ++ else if (!ref_obsolete(ref2)) \ ++ my_used_size += ref_totlen(c, jeb, ref2); \ ++ if (unlikely((!ref2->next_phys) != (ref2 == jeb->last_node))) { \ ++ printk("ref for node at %p (phys %08x) has next_phys->%p (%08x), last_node->%p (phys %08x)\n", \ ++ ref2, ref_offset(ref2), ref2->next_phys, ref_offset(ref2->next_phys), \ ++ jeb->last_node, ref_offset(jeb->last_node)); \ ++ paranoia_failed_dump(jeb); \ ++ BUG(); \ ++ } \ + ref2 = ref2->next_phys; \ + } \ + if (my_used_size != jeb->used_size) { \ + printk(KERN_NOTICE "Calculated used size %08x != stored used size %08x\n", my_used_size, jeb->used_size); \ + BUG(); \ + } \ ++ if (my_unchecked_size != jeb->unchecked_size) { \ ++ printk(KERN_NOTICE "Calculated unchecked size %08x != stored unchecked size %08x\n", my_unchecked_size, jeb->unchecked_size); \ ++ BUG(); \ ++ } \ + } while(0) + ++/* Calculate totlen from surrounding nodes or eraseblock */ ++static inline uint32_t __ref_totlen(struct jffs2_sb_info *c, ++ struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_node_ref *ref) ++{ ++ uint32_t ref_end; ++ ++ if (ref->next_phys) ++ ref_end = ref_offset(ref->next_phys); ++ else { ++ if (!jeb) ++ jeb = &c->blocks[ref->flash_offset / c->sector_size]; ++ ++ /* Last node in block. Use free_space */ ++ BUG_ON(ref != jeb->last_node); ++ ref_end = jeb->offset + c->sector_size - jeb->free_size; ++ } ++ return ref_end - ref_offset(ref); ++} ++ ++static inline uint32_t ref_totlen(struct jffs2_sb_info *c, ++ struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_node_ref *ref) ++{ ++ uint32_t ret; ++ ++ D1(if (jeb && jeb != &c->blocks[ref->flash_offset / c->sector_size]) { ++ printk(KERN_CRIT "ref_totlen called with wrong block -- at 0x%08x instead of 0x%08x; ref 0x%08x\n", ++ jeb->offset, c->blocks[ref->flash_offset / c->sector_size].offset, ref_offset(ref)); ++ BUG(); ++ }) ++ ++#if 1 ++ ret = ref->__totlen; ++#else ++ /* This doesn't actually work yet */ ++ ret = __ref_totlen(c, jeb, ref); ++ if (ret != ref->__totlen) { ++ printk(KERN_CRIT "Totlen for ref at %p (0x%08x-0x%08x) miscalculated as 0x%x instead of %x\n", ++ ref, ref_offset(ref), ref_offset(ref)+ref->__totlen, ++ ret, ref->__totlen); ++ if (!jeb) ++ jeb = &c->blocks[ref->flash_offset / c->sector_size]; ++ paranoia_failed_dump(jeb); ++ BUG(); ++ } ++#endif ++ return ret; ++} ++ ++ + #define ALLOC_NORMAL 0 /* Normal allocation */ + #define ALLOC_DELETION 1 /* Deletion node. Best to allow it */ + #define ALLOC_GC 2 /* Space requested for GC. Give it or die */ ++#define ALLOC_NORETRY 3 /* For jffs2_write_dnode: On failure, return -EAGAIN instead of retrying */ + +-#define JFFS2_RESERVED_BLOCKS_BASE 3 /* Number of free blocks there must be before we... */ +-#define JFFS2_RESERVED_BLOCKS_WRITE (JFFS2_RESERVED_BLOCKS_BASE + 2) /* ... allow a normal filesystem write */ +-#define JFFS2_RESERVED_BLOCKS_DELETION (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... allow a normal filesystem deletion */ +-#define JFFS2_RESERVED_BLOCKS_GCTRIGGER (JFFS2_RESERVED_BLOCKS_BASE + 3) /* ... wake up the GC thread */ +-#define JFFS2_RESERVED_BLOCKS_GCBAD (JFFS2_RESERVED_BLOCKS_BASE + 1) /* ... pick a block from the bad_list to GC */ +-#define JFFS2_RESERVED_BLOCKS_GCMERGE (JFFS2_RESERVED_BLOCKS_BASE) /* ... merge pages when garbage collecting */ ++/* How much dirty space before it goes on the very_dirty_list */ ++#define VERYDIRTY(c, size) ((size) >= ((c)->sector_size / 2)) + ++/* check if dirty space is more than 255 Byte */ ++#define ISDIRTY(size) ((size) > sizeof (struct jffs2_raw_inode) + JFFS2_MIN_DATA_LEN) + + #define PAD(x) (((x)+3)&~3) + +@@ -241,43 +339,75 @@ + return ((struct jffs2_inode_cache *)raw); + } + ++static inline struct jffs2_node_frag *frag_first(struct rb_root *root) ++{ ++ struct rb_node *node = root->rb_node; ++ ++ if (!node) ++ return NULL; ++ while(node->rb_left) ++ node = node->rb_left; ++ return rb_entry(node, struct jffs2_node_frag, rb); ++} ++#define rb_parent(rb) ((rb)->rb_parent) ++#define frag_next(frag) rb_entry(rb_next(&(frag)->rb), struct jffs2_node_frag, rb) ++#define frag_prev(frag) rb_entry(rb_prev(&(frag)->rb), struct jffs2_node_frag, rb) ++#define frag_parent(frag) rb_entry(rb_parent(&(frag)->rb), struct jffs2_node_frag, rb) ++#define frag_left(frag) rb_entry((frag)->rb.rb_left, struct jffs2_node_frag, rb) ++#define frag_right(frag) rb_entry((frag)->rb.rb_right, struct jffs2_node_frag, rb) ++#define frag_erase(frag, list) rb_erase(&frag->rb, list); ++ + /* nodelist.c */ + D1(void jffs2_print_frag_list(struct jffs2_inode_info *f)); + void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list); +-void jffs2_add_tn_to_list(struct jffs2_tmp_dnode_info *tn, struct jffs2_tmp_dnode_info **list); + int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode_info *f, + struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, +- __u32 *highest_version, __u32 *latest_mctime, +- __u32 *mctime_ver); ++ uint32_t *highest_version, uint32_t *latest_mctime, ++ uint32_t *mctime_ver); ++void jffs2_set_inocache_state(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic, int state); + struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, uint32_t ino); + void jffs2_add_ino_cache (struct jffs2_sb_info *c, struct jffs2_inode_cache *new); + void jffs2_del_ino_cache(struct jffs2_sb_info *c, struct jffs2_inode_cache *old); + void jffs2_free_ino_caches(struct jffs2_sb_info *c); + void jffs2_free_raw_node_refs(struct jffs2_sb_info *c); ++struct jffs2_node_frag *jffs2_lookup_node_frag(struct rb_root *fragtree, uint32_t offset); ++void jffs2_kill_fragtree(struct rb_root *root, struct jffs2_sb_info *c_delete); ++void jffs2_fragtree_insert(struct jffs2_node_frag *newfrag, struct jffs2_node_frag *base); ++struct rb_node *rb_next(struct rb_node *); ++struct rb_node *rb_prev(struct rb_node *); ++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root); + + /* nodemgmt.c */ +-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio); +-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len); +-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty); ++int jffs2_thread_should_wake(struct jffs2_sb_info *c); ++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio); ++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); ++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new); + void jffs2_complete_reservation(struct jffs2_sb_info *c); + void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *raw); ++void jffs2_dump_block_lists(struct jffs2_sb_info *c); + + /* write.c */ +-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri); +-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen); +-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen); ++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri); ++ ++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode); ++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode); ++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ struct jffs2_raw_inode *ri, unsigned char *buf, ++ uint32_t offset, uint32_t writelen, uint32_t *retlen); ++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen); ++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, const char *name, int namelen, struct jffs2_inode_info *dead_f); ++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen); ++ + + /* readinode.c */ +-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size); +-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn); ++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); + int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); +-void jffs2_read_inode (struct inode *); +-void jffs2_clear_inode (struct inode *); ++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ uint32_t ino, struct jffs2_raw_inode *latest_node); ++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic); ++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f); + + /* malloc.c */ +-void jffs2_free_tmp_dnode_info_list(struct jffs2_tmp_dnode_info *tn); +-void jffs2_free_full_dirent_list(struct jffs2_full_dirent *fd); +- + int jffs2_create_slab_caches(void); + void jffs2_destroy_slab_caches(void); + +@@ -301,54 +431,41 @@ + /* gc.c */ + int jffs2_garbage_collect_pass(struct jffs2_sb_info *c); + +-/* background.c */ +-int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); +-void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); +-void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); +- +-/* dir.c */ +-extern struct file_operations jffs2_dir_operations; +-extern struct inode_operations jffs2_dir_inode_operations; +- +-/* file.c */ +-extern struct file_operations jffs2_file_operations; +-extern struct inode_operations jffs2_file_inode_operations; +-extern struct address_space_operations jffs2_file_address_operations; +-int jffs2_null_fsync(struct file *, struct dentry *, int); +-int jffs2_setattr (struct dentry *dentry, struct iattr *iattr); +-int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg); +-int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); +-int jffs2_readpage (struct file *, struct page *); +-int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned); +-int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned); +- +-/* ioctl.c */ +-int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); +- + /* read.c */ + int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len); ++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ unsigned char *buf, uint32_t offset, uint32_t len); ++char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f); + + /* compr.c */ +-unsigned char jffs2_compress(unsigned char *data_in, unsigned char *cpage_out, +- __u32 *datalen, __u32 *cdatalen); ++unsigned char jffs2_compress(unsigned char *data_in, unsigned char **cpage_out, ++ uint32_t *datalen, uint32_t *cdatalen); ++void jffs2_free_comprbuf(unsigned char *comprbuf, unsigned char *orig); + int jffs2_decompress(unsigned char comprtype, unsigned char *cdata_in, +- unsigned char *data_out, __u32 cdatalen, __u32 datalen); ++ unsigned char *data_out, uint32_t cdatalen, uint32_t datalen); + + /* scan.c */ + int jffs2_scan_medium(struct jffs2_sb_info *c); ++void jffs2_rotate_lists(struct jffs2_sb_info *c); + + /* build.c */ +-int jffs2_build_filesystem(struct jffs2_sb_info *c); +- +-/* symlink.c */ +-extern struct inode_operations jffs2_symlink_inode_operations; ++int jffs2_do_mount_fs(struct jffs2_sb_info *c); + + /* erase.c */ + void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); +-void jffs2_erase_pending_blocks(struct jffs2_sb_info *c); +-void jffs2_mark_erased_blocks(struct jffs2_sb_info *c); +-void jffs2_erase_pending_trigger(struct jffs2_sb_info *c); ++void jffs2_erase_pending_blocks(struct jffs2_sb_info *c, int count); ++ ++#ifdef CONFIG_JFFS2_FS_NAND ++/* wbuf.c */ ++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino); ++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c); ++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++#endif + + /* compr_zlib.c */ + int jffs2_zlib_init(void); + void jffs2_zlib_exit(void); ++ ++#endif /* __JFFS2_NODELIST_H__ */ +diff -Nurb linux-mips-2.4.27/fs/jffs2/nodemgmt.c linux/fs/jffs2/nodemgmt.c +--- linux-mips-2.4.27/fs/jffs2/nodemgmt.c 2002-06-27 00:36:20.000000000 +0200 ++++ linux/fs/jffs2/nodemgmt.c 2004-11-19 10:25:12.115166960 +0100 +@@ -1,45 +1,21 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: nodemgmt.c,v 1.45.2.1 2002/02/23 14:13:34 dwmw2 Exp $ ++ * $Id: nodemgmt.c,v 1.107 2003/11/26 15:30:58 dwmw2 Exp $ + * + */ + + #include <linux/kernel.h> + #include <linux/slab.h> +-#include <linux/jffs2.h> + #include <linux/mtd/mtd.h> +-#include <linux/interrupt.h> ++#include <linux/compiler.h> ++#include <linux/sched.h> /* For cond_resched() */ + #include "nodelist.h" + + /** +@@ -62,53 +38,95 @@ + * for the requested allocation. + */ + +-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len); ++static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len); + +-int jffs2_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len, int prio) ++int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len, int prio) + { + int ret = -EAGAIN; +- int blocksneeded = JFFS2_RESERVED_BLOCKS_WRITE; ++ int blocksneeded = c->resv_blocks_write; + /* align it */ + minsize = PAD(minsize); + +- if (prio == ALLOC_DELETION) +- blocksneeded = JFFS2_RESERVED_BLOCKS_DELETION; +- + D1(printk(KERN_DEBUG "jffs2_reserve_space(): Requested 0x%x bytes\n", minsize)); + down(&c->alloc_sem); + + D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); + +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + +- /* this needs a little more thought */ ++ /* this needs a little more thought (true <tglx> :)) */ + while(ret == -EAGAIN) { + while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { + int ret; ++ uint32_t dirty, avail; ++ ++ /* calculate real dirty size ++ * dirty_size contains blocks on erase_pending_list ++ * those blocks are counted in c->nr_erasing_blocks. ++ * If one block is actually erased, it is not longer counted as dirty_space ++ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it ++ * with c->nr_erasing_blocks * c->sector_size again. ++ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks ++ * This helps us to force gc and pick eventually a clean block to spread the load. ++ * We add unchecked_size here, as we hopefully will find some space to use. ++ * This will affect the sum only once, as gc first finishes checking ++ * of nodes. ++ */ ++ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size + c->unchecked_size; ++ if (dirty < c->nospc_dirty_size) { ++ if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { ++ printk(KERN_NOTICE "jffs2_reserve_space(): Low on dirty space to GC, but it's a deletion. Allowing...\n"); ++ break; ++ } ++ D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < nospc_dirty_size 0x%08x, returning -ENOSPC\n", ++ dirty, c->unchecked_size, c->sector_size)); ++ ++ spin_unlock(&c->erase_completion_lock); ++ up(&c->alloc_sem); ++ return -ENOSPC; ++ } + ++ /* Calc possibly available space. Possibly available means that we ++ * don't know, if unchecked size contains obsoleted nodes, which could give us some ++ * more usable space. This will affect the sum only once, as gc first finishes checking ++ * of nodes. ++ + Return -ENOSPC, if the maximum possibly available space is less or equal than ++ * blocksneeded * sector_size. ++ * This blocks endless gc looping on a filesystem, which is nearly full, even if ++ * the check above passes. ++ */ ++ avail = c->free_size + c->dirty_size + c->erasing_size + c->unchecked_size; ++ if ( (avail / c->sector_size) <= blocksneeded) { ++ if (prio == ALLOC_DELETION && c->nr_free_blocks + c->nr_erasing_blocks >= c->resv_blocks_deletion) { ++ printk(KERN_NOTICE "jffs2_reserve_space(): Low on possibly available space, but it's a deletion. Allowing...\n"); ++ break; ++ } ++ ++ D1(printk(KERN_DEBUG "max. available size 0x%08x < blocksneeded * sector_size 0x%08x, returning -ENOSPC\n", ++ avail, blocksneeded * c->sector_size)); ++ spin_unlock(&c->erase_completion_lock); + up(&c->alloc_sem); +- if (c->dirty_size < c->sector_size) { +- D1(printk(KERN_DEBUG "Short on space, but total dirty size 0x%08x < sector size 0x%08x, so -ENOSPC\n", c->dirty_size, c->sector_size)); +- spin_unlock_bh(&c->erase_completion_lock); + return -ENOSPC; + } +- D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", +- c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->used_size, c->erasing_size, c->bad_size, +- c->free_size + c->dirty_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); +- spin_unlock_bh(&c->erase_completion_lock); ++ ++ up(&c->alloc_sem); ++ ++ D1(printk(KERN_DEBUG "Triggering GC pass. nr_free_blocks %d, nr_erasing_blocks %d, free_size 0x%08x, dirty_size 0x%08x, wasted_size 0x%08x, used_size 0x%08x, erasing_size 0x%08x, bad_size 0x%08x (total 0x%08x of 0x%08x)\n", ++ c->nr_free_blocks, c->nr_erasing_blocks, c->free_size, c->dirty_size, c->wasted_size, c->used_size, c->erasing_size, c->bad_size, ++ c->free_size + c->dirty_size + c->wasted_size + c->used_size + c->erasing_size + c->bad_size, c->flash_size)); ++ spin_unlock(&c->erase_completion_lock); + + ret = jffs2_garbage_collect_pass(c); + if (ret) + return ret; + +- if (current->need_resched) +- schedule(); ++ cond_resched(); + + if (signal_pending(current)) + return -EINTR; + + down(&c->alloc_sem); +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + } + + ret = jffs2_do_reserve_space(c, minsize, ofs, len); +@@ -116,45 +134,72 @@ + D1(printk(KERN_DEBUG "jffs2_reserve_space: ret is %d\n", ret)); + } + } +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + if (ret) + up(&c->alloc_sem); + return ret; + } + +-int jffs2_reserve_space_gc(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len) ++int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) + { + int ret = -EAGAIN; + minsize = PAD(minsize); + + D1(printk(KERN_DEBUG "jffs2_reserve_space_gc(): Requested 0x%x bytes\n", minsize)); + +- spin_lock_bh(&c->erase_completion_lock); ++ spin_lock(&c->erase_completion_lock); + while(ret == -EAGAIN) { + ret = jffs2_do_reserve_space(c, minsize, ofs, len); + if (ret) { + D1(printk(KERN_DEBUG "jffs2_reserve_space_gc: looping, ret is %d\n", ret)); + } + } +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + return ret; + } + + /* Called with alloc sem _and_ erase_completion_lock */ +-static int jffs2_do_reserve_space(struct jffs2_sb_info *c, __u32 minsize, __u32 *ofs, __u32 *len) ++static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs, uint32_t *len) + { + struct jffs2_eraseblock *jeb = c->nextblock; + + restart: + if (jeb && minsize > jeb->free_size) { + /* Skip the end of this block and file it as having some dirty space */ +- c->dirty_size += jeb->free_size; ++ /* If there's a pending write to it, flush now */ ++ if (jffs2_wbuf_dirty(c)) { ++ spin_unlock(&c->erase_completion_lock); ++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); ++ jffs2_flush_wbuf_pad(c); ++ spin_lock(&c->erase_completion_lock); ++ jeb = c->nextblock; ++ goto restart; ++ } ++ c->wasted_size += jeb->free_size; + c->free_size -= jeb->free_size; +- jeb->dirty_size += jeb->free_size; ++ jeb->wasted_size += jeb->free_size; + jeb->free_size = 0; ++ ++ /* Check, if we have a dirty block now, or if it was dirty already */ ++ if (ISDIRTY (jeb->wasted_size + jeb->dirty_size)) { ++ c->dirty_size += jeb->wasted_size; ++ c->wasted_size -= jeb->wasted_size; ++ jeb->dirty_size += jeb->wasted_size; ++ jeb->wasted_size = 0; ++ if (VERYDIRTY(c, jeb->dirty_size)) { ++ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to very_dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", ++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); ++ list_add_tail(&jeb->list, &c->very_dirty_list); ++ } else { + D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to dirty_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); + list_add_tail(&jeb->list, &c->dirty_list); ++ } ++ } else { ++ D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", ++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); ++ list_add_tail(&jeb->list, &c->clean_list); ++ } + c->nextblock = jeb = NULL; + } + +@@ -164,33 +209,44 @@ + + if (list_empty(&c->free_list)) { + +- DECLARE_WAITQUEUE(wait, current); ++ if (!c->nr_erasing_blocks && ++ !list_empty(&c->erasable_list)) { ++ struct jffs2_eraseblock *ejeb; ++ ++ ejeb = list_entry(c->erasable_list.next, struct jffs2_eraseblock, list); ++ list_del(&ejeb->list); ++ list_add_tail(&ejeb->list, &c->erase_pending_list); ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); ++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Triggering erase of erasable block at 0x%08x\n", ++ ejeb->offset)); ++ } ++ ++ if (!c->nr_erasing_blocks && ++ !list_empty(&c->erasable_pending_wbuf_list)) { ++ D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); ++ /* c->nextblock is NULL, no update to c->nextblock allowed */ ++ spin_unlock(&c->erase_completion_lock); ++ jffs2_flush_wbuf_pad(c); ++ spin_lock(&c->erase_completion_lock); ++ /* Have another go. It'll be on the erasable_list now */ ++ return -EAGAIN; ++ } + + if (!c->nr_erasing_blocks) { +-// if (list_empty(&c->erasing_list) && list_empty(&c->erase_pending_list) && list_empty(c->erase_complete_list)) { + /* Ouch. We're in GC, or we wouldn't have got here. + And there's no space left. At all. */ +- printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", +- c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); ++ printk(KERN_CRIT "Argh. No free space left for GC. nr_erasing_blocks is %d. nr_free_blocks is %d. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", ++ c->nr_erasing_blocks, c->nr_free_blocks, list_empty(&c->erasable_list)?"yes":"no", ++ list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no"); + return -ENOSPC; + } +- /* Make sure this can't deadlock. Someone has to start the erases +- of erase_pending blocks */ +- set_current_state(TASK_INTERRUPTIBLE); +- add_wait_queue(&c->erase_wait, &wait); +- D1(printk(KERN_DEBUG "Waiting for erases to complete. erasing_blocks is %d. (erasingempty: %s, erasependingempty: %s)\n", +- c->nr_erasing_blocks, list_empty(&c->erasing_list)?"yes":"no", list_empty(&c->erase_pending_list)?"yes":"no")); +- if (!list_empty(&c->erase_pending_list)) { +- D1(printk(KERN_DEBUG "Triggering pending erases\n")); +- jffs2_erase_pending_trigger(c); +- } +- spin_unlock_bh(&c->erase_completion_lock); +- schedule(); +- remove_wait_queue(&c->erase_wait, &wait); +- spin_lock_bh(&c->erase_completion_lock); +- if (signal_pending(current)) { +- return -EINTR; +- } ++ ++ spin_unlock(&c->erase_completion_lock); ++ /* Don't wait for it; just erase one right now */ ++ jffs2_erase_pending_blocks(c, 1); ++ spin_lock(&c->erase_completion_lock); ++ + /* An erase may have failed, decreasing the + amount of free space available. So we must + restart from the beginning */ +@@ -201,7 +257,8 @@ + list_del(next); + c->nextblock = jeb = list_entry(next, struct jffs2_eraseblock, list); + c->nr_free_blocks--; +- if (jeb->free_size != c->sector_size - sizeof(struct jffs2_unknown_node)) { ++ ++ if (jeb->free_size != c->sector_size - c->cleanmarker_size) { + printk(KERN_WARNING "Eep. Block 0x%08x taken from free_list had free_size of 0x%08x!!\n", jeb->offset, jeb->free_size); + goto restart; + } +@@ -210,6 +267,20 @@ + enough space */ + *ofs = jeb->offset + (c->sector_size - jeb->free_size); + *len = jeb->free_size; ++ ++ if (c->cleanmarker_size && jeb->used_size == c->cleanmarker_size && ++ !jeb->first_node->next_in_ino) { ++ /* Only node in it beforehand was a CLEANMARKER node (we think). ++ So mark it obsolete now that there's going to be another node ++ in the block. This will reduce used_size to zero but We've ++ already set c->nextblock so that jffs2_mark_node_obsolete() ++ won't try to refile it to the dirty_list. ++ */ ++ spin_unlock(&c->erase_completion_lock); ++ jffs2_mark_node_obsolete(c, jeb->first_node); ++ spin_lock(&c->erase_completion_lock); ++ } ++ + D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs)); + return 0; + } +@@ -217,9 +288,9 @@ + /** + * jffs2_add_physical_node_ref - add a physical node reference to the list + * @c: superblock info +- * @ofs: physical location of this physical node ++ * @new: new node reference to add + * @len: length of this physical node +- * @ino: inode number with which this physical node is associated ++ * @dirty: dirty flag for new node + * + * Should only be used to report nodes for which space has been allocated + * by jffs2_reserve_space. +@@ -227,47 +298,58 @@ + * Must be called with the alloc_sem held. + */ + +-int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new, __u32 len, int dirty) ++int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *new) + { + struct jffs2_eraseblock *jeb; ++ uint32_t len; ++ ++ jeb = &c->blocks[new->flash_offset / c->sector_size]; ++ len = ref_totlen(c, jeb, new); + +- len = PAD(len); +- jeb = &c->blocks[(new->flash_offset & ~3) / c->sector_size]; +- D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", new->flash_offset & ~3, len)); ++ D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x(%d), size 0x%x\n", ref_offset(new), ref_flags(new), len)); + #if 1 +- if (jeb != c->nextblock || (new->flash_offset & ~3) != jeb->offset + (c->sector_size - jeb->free_size)) { ++ if (jeb != c->nextblock || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) { + printk(KERN_WARNING "argh. node added in wrong place\n"); + jffs2_free_raw_node_ref(new); + return -EINVAL; + } + #endif ++ spin_lock(&c->erase_completion_lock); ++ + if (!jeb->first_node) + jeb->first_node = new; + if (jeb->last_node) + jeb->last_node->next_phys = new; + jeb->last_node = new; + +- spin_lock_bh(&c->erase_completion_lock); + jeb->free_size -= len; + c->free_size -= len; +- if (dirty) { +- new->flash_offset |= 1; ++ if (ref_obsolete(new)) { + jeb->dirty_size += len; + c->dirty_size += len; + } else { + jeb->used_size += len; + c->used_size += len; + } +- spin_unlock_bh(&c->erase_completion_lock); ++ + if (!jeb->free_size && !jeb->dirty_size) { + /* If it lives on the dirty_list, jffs2_reserve_space will put it there */ + D1(printk(KERN_DEBUG "Adding full erase block at 0x%08x to clean_list (free 0x%08x, dirty 0x%08x, used 0x%08x\n", + jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); ++ if (jffs2_wbuf_dirty(c)) { ++ /* Flush the last write in the block if it's outstanding */ ++ spin_unlock(&c->erase_completion_lock); ++ jffs2_flush_wbuf_pad(c); ++ spin_lock(&c->erase_completion_lock); ++ } ++ + list_add_tail(&jeb->list, &c->clean_list); + c->nextblock = NULL; + } + ACCT_SANITY_CHECK(c,jeb); +- ACCT_PARANOIA_CHECK(jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ spin_unlock(&c->erase_completion_lock); + + return 0; + } +@@ -280,20 +362,34 @@ + up(&c->alloc_sem); + } + ++static inline int on_list(struct list_head *obj, struct list_head *head) ++{ ++ struct list_head *this; ++ ++ list_for_each(this, head) { ++ if (this == obj) { ++ D1(printk("%p is on list at %p\n", obj, head)); ++ return 1; ++ ++ } ++ } ++ return 0; ++} ++ + void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref *ref) + { + struct jffs2_eraseblock *jeb; + int blocknr; + struct jffs2_unknown_node n; +- int ret; +- ssize_t retlen; ++ int ret, addedsize; ++ size_t retlen; + + if(!ref) { + printk(KERN_NOTICE "EEEEEK. jffs2_mark_node_obsolete called with NULL node\n"); + return; + } +- if (ref->flash_offset & 1) { +- D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref->flash_offset &~3)); ++ if (ref_obsolete(ref)) { ++ D1(printk(KERN_DEBUG "jffs2_mark_node_obsolete called with already obsolete node at 0x%08x\n", ref_offset(ref))); + return; + } + blocknr = ref->flash_offset / c->sector_size; +@@ -302,22 +398,63 @@ + BUG(); + } + jeb = &c->blocks[blocknr]; +- if (jeb->used_size < ref->totlen) { ++ ++ spin_lock(&c->erase_completion_lock); ++ ++ if (ref_flags(ref) == REF_UNCHECKED) { ++ D1(if (unlikely(jeb->unchecked_size < ref_totlen(c, jeb, ref))) { ++ printk(KERN_NOTICE "raw unchecked node of size 0x%08x freed from erase block %d at 0x%08x, but unchecked_size was already 0x%08x\n", ++ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); ++ BUG(); ++ }) ++ D1(printk(KERN_DEBUG "Obsoleting previously unchecked node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); ++ jeb->unchecked_size -= ref_totlen(c, jeb, ref); ++ c->unchecked_size -= ref_totlen(c, jeb, ref); ++ } else { ++ D1(if (unlikely(jeb->used_size < ref_totlen(c, jeb, ref))) { + printk(KERN_NOTICE "raw node of size 0x%08x freed from erase block %d at 0x%08x, but used_size was already 0x%08x\n", +- ref->totlen, blocknr, ref->flash_offset, jeb->used_size); ++ ref_totlen(c, jeb, ref), blocknr, ref->flash_offset, jeb->used_size); + BUG(); ++ }) ++ D1(printk(KERN_DEBUG "Obsoleting node at 0x%08x of len %x: ", ref_offset(ref), ref_totlen(c, jeb, ref))); ++ jeb->used_size -= ref_totlen(c, jeb, ref); ++ c->used_size -= ref_totlen(c, jeb, ref); ++ } ++ ++ // Take care, that wasted size is taken into concern ++ if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref_totlen(c, jeb, ref))) && jeb != c->nextblock) { ++ D1(printk("Dirtying\n")); ++ addedsize = ref_totlen(c, jeb, ref); ++ jeb->dirty_size += ref_totlen(c, jeb, ref); ++ c->dirty_size += ref_totlen(c, jeb, ref); ++ ++ /* Convert wasted space to dirty, if not a bad block */ ++ if (jeb->wasted_size) { ++ if (on_list(&jeb->list, &c->bad_used_list)) { ++ D1(printk(KERN_DEBUG "Leaving block at %08x on the bad_used_list\n", ++ jeb->offset)); ++ addedsize = 0; /* To fool the refiling code later */ ++ } else { ++ D1(printk(KERN_DEBUG "Converting %d bytes of wasted space to dirty in block at %08x\n", ++ jeb->wasted_size, jeb->offset)); ++ addedsize += jeb->wasted_size; ++ jeb->dirty_size += jeb->wasted_size; ++ c->dirty_size += jeb->wasted_size; ++ c->wasted_size -= jeb->wasted_size; ++ jeb->wasted_size = 0; + } +- +- spin_lock_bh(&c->erase_completion_lock); +- jeb->used_size -= ref->totlen; +- jeb->dirty_size += ref->totlen; +- c->used_size -= ref->totlen; +- c->dirty_size += ref->totlen; +- ref->flash_offset |= 1; ++ } ++ } else { ++ D1(printk("Wasting\n")); ++ addedsize = 0; ++ jeb->wasted_size += ref_totlen(c, jeb, ref); ++ c->wasted_size += ref_totlen(c, jeb, ref); ++ } ++ ref->flash_offset = ref_offset(ref) | REF_OBSOLETE; + + ACCT_SANITY_CHECK(c, jeb); + +- ACCT_PARANOIA_CHECK(jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); + + if (c->flags & JFFS2_SB_FLAG_MOUNTING) { + /* Mount in progress. Don't muck about with the block +@@ -325,68 +462,280 @@ + obliterate nodes that look obsolete. If they weren't + marked obsolete on the flash at the time they _became_ + obsolete, there was probably a reason for that. */ +- spin_unlock_bh(&c->erase_completion_lock); ++ spin_unlock(&c->erase_completion_lock); + return; + } ++ + if (jeb == c->nextblock) { + D2(printk(KERN_DEBUG "Not moving nextblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); +- } else if (jeb == c->gcblock) { +- D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty/erase_pending list\n", jeb->offset)); +-#if 0 /* We no longer do this here. It can screw the wear levelling. If you have a lot of static +- data and a few blocks free, and you just create new files and keep deleting/overwriting +- them, then you'd keep erasing and reusing those blocks without ever moving stuff around. +- So we leave completely obsoleted blocks on the dirty_list and let the GC delete them +- when it finds them there. That way, we still get the 'once in a while, take a clean block' +- to spread out the flash usage */ +- } else if (!jeb->used_size) { ++ } else if (!jeb->used_size && !jeb->unchecked_size) { ++ if (jeb == c->gcblock) { ++ D1(printk(KERN_DEBUG "gcblock at 0x%08x completely dirtied. Clearing gcblock...\n", jeb->offset)); ++ c->gcblock = NULL; ++ } else { + D1(printk(KERN_DEBUG "Eraseblock at 0x%08x completely dirtied. Removing from (dirty?) list...\n", jeb->offset)); + list_del(&jeb->list); ++ } ++ if (jffs2_wbuf_dirty(c)) { ++ D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); ++ list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list); ++ } else { ++ if (jiffies & 127) { ++ /* Most of the time, we just erase it immediately. Otherwise we ++ spend ages scanning it on mount, etc. */ + D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); + list_add_tail(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + jffs2_erase_pending_trigger(c); +- // OFNI_BS_2SFFJ(c)->s_dirt = 1; ++ } else { ++ /* Sometimes, however, we leave it elsewhere so it doesn't get ++ immediately reused, and we spread the load a bit. */ ++ D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); ++ list_add_tail(&jeb->list, &c->erasable_list); ++ } ++ } + D1(printk(KERN_DEBUG "Done OK\n")); +-#endif +- } else if (jeb->dirty_size == ref->totlen) { ++ } else if (jeb == c->gcblock) { ++ D2(printk(KERN_DEBUG "Not moving gcblock 0x%08x to dirty_list\n", jeb->offset)); ++ } else if (ISDIRTY(jeb->dirty_size) && !ISDIRTY(jeb->dirty_size - addedsize)) { + D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is freshly dirtied. Removing from clean list...\n", jeb->offset)); + list_del(&jeb->list); + D1(printk(KERN_DEBUG "...and adding to dirty_list\n")); + list_add_tail(&jeb->list, &c->dirty_list); ++ } else if (VERYDIRTY(c, jeb->dirty_size) && ++ !VERYDIRTY(c, jeb->dirty_size - addedsize)) { ++ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x is now very dirty. Removing from dirty list...\n", jeb->offset)); ++ list_del(&jeb->list); ++ D1(printk(KERN_DEBUG "...and adding to very_dirty_list\n")); ++ list_add_tail(&jeb->list, &c->very_dirty_list); ++ } else { ++ D1(printk(KERN_DEBUG "Eraseblock at 0x%08x not moved anywhere. (free 0x%08x, dirty 0x%08x, used 0x%08x)\n", ++ jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); + } +- spin_unlock_bh(&c->erase_completion_lock); + +- if (c->mtd->type != MTD_NORFLASH && c->mtd->type != MTD_RAM) ++ spin_unlock(&c->erase_completion_lock); ++ ++ if (!jffs2_can_mark_obsolete(c)) + return; +- if (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) ++ if (jffs2_is_readonly(c)) + return; + +- D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref->flash_offset &~3)); +- ret = c->mtd->read(c->mtd, ref->flash_offset &~3, sizeof(n), &retlen, (char *)&n); ++ D1(printk(KERN_DEBUG "obliterating obsoleted node at 0x%08x\n", ref_offset(ref))); ++ ret = jffs2_flash_read(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); + if (ret) { +- printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret); ++ printk(KERN_WARNING "Read error reading from obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); + return; + } + if (retlen != sizeof(n)) { +- printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen); ++ printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); + return; + } +- if (PAD(n.totlen) != PAD(ref->totlen)) { +- printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen in node ref (0x%08x)\n", n.totlen, ref->totlen); ++ if (PAD(je32_to_cpu(n.totlen)) != PAD(ref_totlen(c, jeb, ref))) { ++ printk(KERN_WARNING "Node totlen on flash (0x%08x) != totlen from node ref (0x%08x)\n", je32_to_cpu(n.totlen), ref_totlen(c, jeb, ref)); + return; + } +- if (!(n.nodetype & JFFS2_NODE_ACCURATE)) { +- D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x\n", ref->flash_offset &~3, n.nodetype)); ++ if (!(je16_to_cpu(n.nodetype) & JFFS2_NODE_ACCURATE)) { ++ D1(printk(KERN_DEBUG "Node at 0x%08x was already marked obsolete (nodetype 0x%04x)\n", ref_offset(ref), je16_to_cpu(n.nodetype))); + return; + } +- n.nodetype &= ~JFFS2_NODE_ACCURATE; +- ret = c->mtd->write(c->mtd, ref->flash_offset&~3, sizeof(n), &retlen, (char *)&n); ++ /* XXX FIXME: This is ugly now */ ++ n.nodetype = cpu_to_je16(je16_to_cpu(n.nodetype) & ~JFFS2_NODE_ACCURATE); ++ ret = jffs2_flash_write(c, ref_offset(ref), sizeof(n), &retlen, (char *)&n); + if (ret) { +- printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, ret); ++ printk(KERN_WARNING "Write error in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), ret); + return; + } + if (retlen != sizeof(n)) { +- printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref->flash_offset &~3, retlen); ++ printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); + return; + } + } ++ ++#if CONFIG_JFFS2_FS_DEBUG > 0 ++void jffs2_dump_block_lists(struct jffs2_sb_info *c) ++{ ++ ++ ++ printk(KERN_DEBUG "jffs2_dump_block_lists:\n"); ++ printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size); ++ printk(KERN_DEBUG "used_size: %08x\n", c->used_size); ++ printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size); ++ printk(KERN_DEBUG "wasted_size: %08x\n", c->wasted_size); ++ printk(KERN_DEBUG "unchecked_size: %08x\n", c->unchecked_size); ++ printk(KERN_DEBUG "free_size: %08x\n", c->free_size); ++ printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); ++ printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); ++ printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size); ++ printk(KERN_DEBUG "jffs2_reserved_blocks size: %08x\n",c->sector_size * c->resv_blocks_write); ++ ++ if (c->nextblock) { ++ printk(KERN_DEBUG "nextblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ c->nextblock->offset, c->nextblock->used_size, c->nextblock->dirty_size, c->nextblock->wasted_size, c->nextblock->unchecked_size, c->nextblock->free_size); ++ } else { ++ printk(KERN_DEBUG "nextblock: NULL\n"); ++ } ++ if (c->gcblock) { ++ printk(KERN_DEBUG "gcblock: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ c->gcblock->offset, c->gcblock->used_size, c->gcblock->dirty_size, c->gcblock->wasted_size, c->gcblock->unchecked_size, c->gcblock->free_size); ++ } else { ++ printk(KERN_DEBUG "gcblock: NULL\n"); ++ } ++ if (list_empty(&c->clean_list)) { ++ printk(KERN_DEBUG "clean_list: empty\n"); ++ } else { ++ struct list_head *this; ++ int numblocks = 0; ++ uint32_t dirty = 0; ++ ++ list_for_each(this, &c->clean_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ numblocks ++; ++ dirty += jeb->wasted_size; ++ printk(KERN_DEBUG "clean_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ printk (KERN_DEBUG "Contains %d blocks with total wasted size %u, average wasted size: %u\n", numblocks, dirty, dirty / numblocks); ++ } ++ if (list_empty(&c->very_dirty_list)) { ++ printk(KERN_DEBUG "very_dirty_list: empty\n"); ++ } else { ++ struct list_head *this; ++ int numblocks = 0; ++ uint32_t dirty = 0; ++ ++ list_for_each(this, &c->very_dirty_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ numblocks ++; ++ dirty += jeb->dirty_size; ++ printk(KERN_DEBUG "very_dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", ++ numblocks, dirty, dirty / numblocks); ++ } ++ if (list_empty(&c->dirty_list)) { ++ printk(KERN_DEBUG "dirty_list: empty\n"); ++ } else { ++ struct list_head *this; ++ int numblocks = 0; ++ uint32_t dirty = 0; ++ ++ list_for_each(this, &c->dirty_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ numblocks ++; ++ dirty += jeb->dirty_size; ++ printk(KERN_DEBUG "dirty_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ printk (KERN_DEBUG "Contains %d blocks with total dirty size %u, average dirty size: %u\n", ++ numblocks, dirty, dirty / numblocks); ++ } ++ if (list_empty(&c->erasable_list)) { ++ printk(KERN_DEBUG "erasable_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->erasable_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "erasable_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->erasing_list)) { ++ printk(KERN_DEBUG "erasing_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->erasing_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "erasing_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->erase_pending_list)) { ++ printk(KERN_DEBUG "erase_pending_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->erase_pending_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "erase_pending_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->erasable_pending_wbuf_list)) { ++ printk(KERN_DEBUG "erasable_pending_wbuf_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->erasable_pending_wbuf_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "erasable_pending_wbuf_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->free_list)) { ++ printk(KERN_DEBUG "free_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->free_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "free_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->bad_list)) { ++ printk(KERN_DEBUG "bad_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->bad_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "bad_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++ if (list_empty(&c->bad_used_list)) { ++ printk(KERN_DEBUG "bad_used_list: empty\n"); ++ } else { ++ struct list_head *this; ++ ++ list_for_each(this, &c->bad_used_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ printk(KERN_DEBUG "bad_used_list: %08x (used %08x, dirty %08x, wasted %08x, unchecked %08x, free %08x)\n", ++ jeb->offset, jeb->used_size, jeb->dirty_size, jeb->wasted_size, jeb->unchecked_size, jeb->free_size); ++ } ++ } ++} ++#endif /* CONFIG_JFFS2_FS_DEBUG */ ++ ++int jffs2_thread_should_wake(struct jffs2_sb_info *c) ++{ ++ int ret = 0; ++ uint32_t dirty; ++ ++ if (c->unchecked_size) { ++ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): unchecked_size %d, checked_ino #%d\n", ++ c->unchecked_size, c->checked_ino)); ++ return 1; ++ } ++ ++ /* dirty_size contains blocks on erase_pending_list ++ * those blocks are counted in c->nr_erasing_blocks. ++ * If one block is actually erased, it is not longer counted as dirty_space ++ * but it is counted in c->nr_erasing_blocks, so we add it and subtract it ++ * with c->nr_erasing_blocks * c->sector_size again. ++ * Blocks on erasable_list are counted as dirty_size, but not in c->nr_erasing_blocks ++ * This helps us to force gc and pick eventually a clean block to spread the load. ++ */ ++ dirty = c->dirty_size + c->erasing_size - c->nr_erasing_blocks * c->sector_size; ++ ++ if (c->nr_free_blocks + c->nr_erasing_blocks < c->resv_blocks_gctrigger && ++ (dirty > c->nospc_dirty_size)) ++ ret = 1; ++ ++ D1(printk(KERN_DEBUG "jffs2_thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", ++ c->nr_free_blocks, c->nr_erasing_blocks, c->dirty_size, ret?"yes":"no")); ++ ++ return ret; ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/os-linux.h linux/fs/jffs2/os-linux.h +--- linux-mips-2.4.27/fs/jffs2/os-linux.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/os-linux.h 2004-11-19 10:25:12.116166808 +0100 +@@ -0,0 +1,212 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2002-2003 Red Hat, Inc. ++ * ++ * Created by David Woodhouse <dwmw2@redhat.com> ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: os-linux.h,v 1.41 2003/11/26 13:02:46 dwmw2 Exp $ ++ * ++ */ ++ ++#ifndef __JFFS2_OS_LINUX_H__ ++#define __JFFS2_OS_LINUX_H__ ++#include <linux/version.h> ++ ++/* JFFS2 uses Linux mode bits natively -- no need for conversion */ ++#define os_to_jffs2_mode(x) (x) ++#define jffs2_to_os_mode(x) (x) ++ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73) ++#define kstatfs statfs ++#endif ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) ++#define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode)) ++#define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode) ++#define JFFS2_SB_INFO(sb) (sb->s_fs_info) ++#define OFNI_BS_2SFFJ(c) ((struct super_block *)c->os_priv) ++#elif defined(JFFS2_OUT_OF_KERNEL) ++#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u) ++#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) ) ++#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u) ++#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) ) ++#else ++#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i) ++#define OFNI_EDONI_2SFFJ(f) ((struct inode *) ( ((char *)f) - ((char *)(&((struct inode *)NULL)->u)) ) ) ++#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb) ++#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) ) ++#endif ++ ++ ++#define JFFS2_F_I_SIZE(f) (OFNI_EDONI_2SFFJ(f)->i_size) ++#define JFFS2_F_I_MODE(f) (OFNI_EDONI_2SFFJ(f)->i_mode) ++#define JFFS2_F_I_UID(f) (OFNI_EDONI_2SFFJ(f)->i_uid) ++#define JFFS2_F_I_GID(f) (OFNI_EDONI_2SFFJ(f)->i_gid) ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1) ++#define JFFS2_F_I_RDEV_MIN(f) (iminor(OFNI_EDONI_2SFFJ(f))) ++#define JFFS2_F_I_RDEV_MAJ(f) (imajor(OFNI_EDONI_2SFFJ(f))) ++#else ++#define JFFS2_F_I_RDEV_MIN(f) (MINOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev))) ++#define JFFS2_F_I_RDEV_MAJ(f) (MAJOR(to_kdev_t(OFNI_EDONI_2SFFJ(f)->i_rdev))) ++#endif ++ ++/* Urgh. The things we do to keep the 2.4 build working */ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,47) ++#define ITIME(sec) ((struct timespec){sec, 0}) ++#define I_SEC(tv) ((tv).tv_sec) ++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime.tv_sec) ++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime.tv_sec) ++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime.tv_sec) ++#else ++#define ITIME(x) (x) ++#define I_SEC(x) (x) ++#define JFFS2_F_I_CTIME(f) (OFNI_EDONI_2SFFJ(f)->i_ctime) ++#define JFFS2_F_I_MTIME(f) (OFNI_EDONI_2SFFJ(f)->i_mtime) ++#define JFFS2_F_I_ATIME(f) (OFNI_EDONI_2SFFJ(f)->i_atime) ++#endif ++ ++#define sleep_on_spinunlock(wq, s) \ ++ do { \ ++ DECLARE_WAITQUEUE(__wait, current); \ ++ add_wait_queue((wq), &__wait); \ ++ set_current_state(TASK_UNINTERRUPTIBLE); \ ++ spin_unlock(s); \ ++ schedule(); \ ++ remove_wait_queue((wq), &__wait); \ ++ } while(0) ++ ++static inline void jffs2_init_inode_info(struct jffs2_inode_info *f) ++{ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) ++ f->highest_version = 0; ++ f->fragtree = RB_ROOT; ++ f->metadata = NULL; ++ f->dents = NULL; ++ f->flags = 0; ++ f->usercompr = 0; ++#else ++ memset(f, 0, sizeof(*f)); ++ init_MUTEX_LOCKED(&f->sem); ++#endif ++} ++ ++#define jffs2_is_readonly(c) (OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY) ++ ++#ifndef CONFIG_JFFS2_FS_NAND ++#define jffs2_can_mark_obsolete(c) (1) ++#define jffs2_cleanmarker_oob(c) (0) ++#define jffs2_write_nand_cleanmarker(c,jeb) (-EIO) ++ ++#define jffs2_flash_write(c, ofs, len, retlen, buf) ((c)->mtd->write((c)->mtd, ofs, len, retlen, buf)) ++#define jffs2_flash_read(c, ofs, len, retlen, buf) ((c)->mtd->read((c)->mtd, ofs, len, retlen, buf)) ++#define jffs2_flush_wbuf_pad(c) ({ (void)(c), 0; }) ++#define jffs2_flush_wbuf_gc(c, i) ({ (void)(c), (void) i, 0; }) ++#define jffs2_nand_read_failcnt(c,jeb) do { ; } while(0) ++#define jffs2_write_nand_badblock(c,jeb) do { ; } while(0) ++#define jffs2_nand_flash_setup(c) (0) ++#define jffs2_nand_flash_cleanup(c) do {} while(0) ++#define jffs2_wbuf_dirty(c) (0) ++#define jffs2_flash_writev(a,b,c,d,e,f) jffs2_flash_direct_writev(a,b,c,d,e) ++#define jffs2_wbuf_timeout NULL ++#define jffs2_wbuf_process NULL ++ ++#else /* NAND support present */ ++ ++#define jffs2_can_mark_obsolete(c) (c->mtd->type == MTD_NORFLASH || c->mtd->type == MTD_RAM) ++#define jffs2_cleanmarker_oob(c) (c->mtd->type == MTD_NANDFLASH) ++ ++#define jffs2_flash_write_oob(c, ofs, len, retlen, buf) ((c)->mtd->write_oob((c)->mtd, ofs, len, retlen, buf)) ++#define jffs2_flash_read_oob(c, ofs, len, retlen, buf) ((c)->mtd->read_oob((c)->mtd, ofs, len, retlen, buf)) ++#define jffs2_wbuf_dirty(c) (!!(c)->wbuf_len) ++struct kstatfs; ++ ++/* wbuf.c */ ++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino); ++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf); ++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf); ++int jffs2_check_oob_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb,int mode); ++int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++void jffs2_wbuf_timeout(unsigned long data); ++void jffs2_wbuf_process(void *data); ++int jffs2_nand_flash_setup(struct jffs2_sb_info *c); ++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c); ++#endif /* NAND */ ++ ++/* erase.c */ ++static inline void jffs2_erase_pending_trigger(struct jffs2_sb_info *c) ++{ ++ OFNI_BS_2SFFJ(c)->s_dirt = 1; ++} ++ ++/* background.c */ ++int jffs2_start_garbage_collect_thread(struct jffs2_sb_info *c); ++void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c); ++void jffs2_garbage_collect_trigger(struct jffs2_sb_info *c); ++ ++/* dir.c */ ++extern struct file_operations jffs2_dir_operations; ++extern struct inode_operations jffs2_dir_inode_operations; ++ ++/* file.c */ ++extern struct file_operations jffs2_file_operations; ++extern struct inode_operations jffs2_file_inode_operations; ++extern struct address_space_operations jffs2_file_address_operations; ++int jffs2_fsync(struct file *, struct dentry *, int); ++int jffs2_do_readpage_nolock (struct inode *inode, struct page *pg); ++int jffs2_do_readpage_unlock (struct inode *inode, struct page *pg); ++int jffs2_readpage (struct file *, struct page *); ++int jffs2_prepare_write (struct file *, struct page *, unsigned, unsigned); ++int jffs2_commit_write (struct file *, struct page *, unsigned, unsigned); ++ ++/* ioctl.c */ ++int jffs2_ioctl(struct inode *, struct file *, unsigned int, unsigned long); ++ ++/* symlink.c */ ++extern struct inode_operations jffs2_symlink_inode_operations; ++ ++/* fs.c */ ++int jffs2_setattr (struct dentry *, struct iattr *); ++void jffs2_read_inode (struct inode *); ++void jffs2_clear_inode (struct inode *); ++void jffs2_dirty_inode(struct inode *inode); ++struct inode *jffs2_new_inode (struct inode *dir_i, int mode, ++ struct jffs2_raw_inode *ri); ++int jffs2_statfs (struct super_block *, struct kstatfs *); ++void jffs2_write_super (struct super_block *); ++int jffs2_remount_fs (struct super_block *, int *, char *); ++int jffs2_do_fill_super(struct super_block *sb, void *data, int silent); ++void jffs2_gc_release_inode(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f); ++struct jffs2_inode_info *jffs2_gc_fetch_inode(struct jffs2_sb_info *c, ++ int inum, int nlink); ++ ++unsigned char *jffs2_gc_fetch_page(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f, ++ unsigned long offset, ++ unsigned long *priv); ++void jffs2_gc_release_page(struct jffs2_sb_info *c, ++ unsigned char *pg, ++ unsigned long *priv); ++ ++ ++/* writev.c */ ++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen); ++ ++/* Compression config */ ++#define JFFS2_COMPRESSION ++#undef JFFS2_USE_DYNRUBIN /* Disabled 23/9/1. With zlib it hardly ever gets a look in */ ++#undef JFFS2_USE_RUBINMIPS /* Disabled 26/2/1. Obsoleted by dynrubin */ ++#define JFFS2_USE_ZLIB ++#define JFFS2_USE_RTIME /* rtime does manage to recompress already-compressed data */ ++ ++ ++#endif /* __JFFS2_OS_LINUX_H__ */ ++ ++ +diff -Nurb linux-mips-2.4.27/fs/jffs2/pushpull.h linux/fs/jffs2/pushpull.h +--- linux-mips-2.4.27/fs/jffs2/pushpull.h 2001-11-05 21:16:19.000000000 +0100 ++++ linux/fs/jffs2/pushpull.h 2004-11-19 10:25:12.118166504 +0100 +@@ -1,42 +1,21 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001, 2002 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: pushpull.h,v 1.5 2001/09/23 10:04:15 rmk Exp $ ++ * $Id: pushpull.h,v 1.9 2003/10/04 08:33:06 dwmw2 Exp $ + * + */ + + #ifndef __PUSHPULL_H__ + #define __PUSHPULL_H__ ++ ++#include <linux/errno.h> ++ + struct pushpull { + unsigned char *buf; + unsigned int buflen; +@@ -44,9 +23,36 @@ + unsigned int reserve; + }; + +-void init_pushpull(struct pushpull *, char *, unsigned, unsigned, unsigned); +-int pushbit(struct pushpull *pp, int bit, int use_reserved); +-int pushedbits(struct pushpull *pp); ++ ++static inline void init_pushpull(struct pushpull *pp, char *buf, unsigned buflen, unsigned ofs, unsigned reserve) ++{ ++ pp->buf = buf; ++ pp->buflen = buflen; ++ pp->ofs = ofs; ++ pp->reserve = reserve; ++} ++ ++static inline int pushbit(struct pushpull *pp, int bit, int use_reserved) ++{ ++ if (pp->ofs >= pp->buflen - (use_reserved?0:pp->reserve)) { ++ return -ENOSPC; ++ } ++ ++ if (bit) { ++ pp->buf[pp->ofs >> 3] |= (1<<(7-(pp->ofs &7))); ++ } ++ else { ++ pp->buf[pp->ofs >> 3] &= ~(1<<(7-(pp->ofs &7))); ++ } ++ pp->ofs++; ++ ++ return 0; ++} ++ ++static inline int pushedbits(struct pushpull *pp) ++{ ++ return pp->ofs; ++} + + static inline int pullbit(struct pushpull *pp) + { +diff -Nurb linux-mips-2.4.27/fs/jffs2/rbtree.c linux/fs/jffs2/rbtree.c +--- linux-mips-2.4.27/fs/jffs2/rbtree.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/rbtree.c 2004-11-19 10:25:12.120166200 +0100 +@@ -0,0 +1,363 @@ ++/* ++ Red Black Trees ++ (C) 1999 Andrea Arcangeli <andrea@suse.de> ++ (C) 2002 David Woodhouse <dwmw2@infradead.org> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ $Id: rbtree.c,v 1.3 2003/10/16 08:02:19 dwmw2 Exp $ ++*/ ++ ++#ifdef __ECOS /* This file is _not_ under the eCos licence; it is pure GPL. */ ++#error "Licence problem. eCos has its own rbtree code." ++#endif ++ ++#include <linux/version.h> ++#include <linux/rbtree.h> ++ ++/* This wasn't present till 2.4.11, wasn't exported till 2.4.19 */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,11) || \ ++ (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) && defined(MODULE)) ++static void __rb_rotate_left(struct rb_node * node, struct rb_root * root) ++{ ++ struct rb_node * right = node->rb_right; ++ ++ if ((node->rb_right = right->rb_left)) ++ right->rb_left->rb_parent = node; ++ right->rb_left = node; ++ ++ if ((right->rb_parent = node->rb_parent)) ++ { ++ if (node == node->rb_parent->rb_left) ++ node->rb_parent->rb_left = right; ++ else ++ node->rb_parent->rb_right = right; ++ } ++ else ++ root->rb_node = right; ++ node->rb_parent = right; ++} ++ ++static void __rb_rotate_right(struct rb_node * node, struct rb_root * root) ++{ ++ struct rb_node * left = node->rb_left; ++ ++ if ((node->rb_left = left->rb_right)) ++ left->rb_right->rb_parent = node; ++ left->rb_right = node; ++ ++ if ((left->rb_parent = node->rb_parent)) ++ { ++ if (node == node->rb_parent->rb_right) ++ node->rb_parent->rb_right = left; ++ else ++ node->rb_parent->rb_left = left; ++ } ++ else ++ root->rb_node = left; ++ node->rb_parent = left; ++} ++ ++void rb_insert_color(struct rb_node * node, struct rb_root * root) ++{ ++ struct rb_node * parent, * gparent; ++ ++ while ((parent = node->rb_parent) && parent->rb_color == RB_RED) ++ { ++ gparent = parent->rb_parent; ++ ++ if (parent == gparent->rb_left) ++ { ++ { ++ register struct rb_node * uncle = gparent->rb_right; ++ if (uncle && uncle->rb_color == RB_RED) ++ { ++ uncle->rb_color = RB_BLACK; ++ parent->rb_color = RB_BLACK; ++ gparent->rb_color = RB_RED; ++ node = gparent; ++ continue; ++ } ++ } ++ ++ if (parent->rb_right == node) ++ { ++ register struct rb_node * tmp; ++ __rb_rotate_left(parent, root); ++ tmp = parent; ++ parent = node; ++ node = tmp; ++ } ++ ++ parent->rb_color = RB_BLACK; ++ gparent->rb_color = RB_RED; ++ __rb_rotate_right(gparent, root); ++ } else { ++ { ++ register struct rb_node * uncle = gparent->rb_left; ++ if (uncle && uncle->rb_color == RB_RED) ++ { ++ uncle->rb_color = RB_BLACK; ++ parent->rb_color = RB_BLACK; ++ gparent->rb_color = RB_RED; ++ node = gparent; ++ continue; ++ } ++ } ++ ++ if (parent->rb_left == node) ++ { ++ register struct rb_node * tmp; ++ __rb_rotate_right(parent, root); ++ tmp = parent; ++ parent = node; ++ node = tmp; ++ } ++ ++ parent->rb_color = RB_BLACK; ++ gparent->rb_color = RB_RED; ++ __rb_rotate_left(gparent, root); ++ } ++ } ++ ++ root->rb_node->rb_color = RB_BLACK; ++} ++ ++static void __rb_erase_color(struct rb_node * node, struct rb_node * parent, ++ struct rb_root * root) ++{ ++ struct rb_node * other; ++ ++ while ((!node || node->rb_color == RB_BLACK) && node != root->rb_node) ++ { ++ if (parent->rb_left == node) ++ { ++ other = parent->rb_right; ++ if (other->rb_color == RB_RED) ++ { ++ other->rb_color = RB_BLACK; ++ parent->rb_color = RB_RED; ++ __rb_rotate_left(parent, root); ++ other = parent->rb_right; ++ } ++ if ((!other->rb_left || ++ other->rb_left->rb_color == RB_BLACK) ++ && (!other->rb_right || ++ other->rb_right->rb_color == RB_BLACK)) ++ { ++ other->rb_color = RB_RED; ++ node = parent; ++ parent = node->rb_parent; ++ } ++ else ++ { ++ if (!other->rb_right || ++ other->rb_right->rb_color == RB_BLACK) ++ { ++ register struct rb_node * o_left; ++ if ((o_left = other->rb_left)) ++ o_left->rb_color = RB_BLACK; ++ other->rb_color = RB_RED; ++ __rb_rotate_right(other, root); ++ other = parent->rb_right; ++ } ++ other->rb_color = parent->rb_color; ++ parent->rb_color = RB_BLACK; ++ if (other->rb_right) ++ other->rb_right->rb_color = RB_BLACK; ++ __rb_rotate_left(parent, root); ++ node = root->rb_node; ++ break; ++ } ++ } ++ else ++ { ++ other = parent->rb_left; ++ if (other->rb_color == RB_RED) ++ { ++ other->rb_color = RB_BLACK; ++ parent->rb_color = RB_RED; ++ __rb_rotate_right(parent, root); ++ other = parent->rb_left; ++ } ++ if ((!other->rb_left || ++ other->rb_left->rb_color == RB_BLACK) ++ && (!other->rb_right || ++ other->rb_right->rb_color == RB_BLACK)) ++ { ++ other->rb_color = RB_RED; ++ node = parent; ++ parent = node->rb_parent; ++ } ++ else ++ { ++ if (!other->rb_left || ++ other->rb_left->rb_color == RB_BLACK) ++ { ++ register struct rb_node * o_right; ++ if ((o_right = other->rb_right)) ++ o_right->rb_color = RB_BLACK; ++ other->rb_color = RB_RED; ++ __rb_rotate_left(other, root); ++ other = parent->rb_left; ++ } ++ other->rb_color = parent->rb_color; ++ parent->rb_color = RB_BLACK; ++ if (other->rb_left) ++ other->rb_left->rb_color = RB_BLACK; ++ __rb_rotate_right(parent, root); ++ node = root->rb_node; ++ break; ++ } ++ } ++ } ++ if (node) ++ node->rb_color = RB_BLACK; ++} ++ ++void rb_erase(struct rb_node * node, struct rb_root * root) ++{ ++ struct rb_node * child, * parent; ++ int color; ++ ++ if (!node->rb_left) ++ child = node->rb_right; ++ else if (!node->rb_right) ++ child = node->rb_left; ++ else ++ { ++ struct rb_node * old = node, * left; ++ ++ node = node->rb_right; ++ while ((left = node->rb_left)) ++ node = left; ++ child = node->rb_right; ++ parent = node->rb_parent; ++ color = node->rb_color; ++ ++ if (child) ++ child->rb_parent = parent; ++ if (parent) ++ { ++ if (parent->rb_left == node) ++ parent->rb_left = child; ++ else ++ parent->rb_right = child; ++ } ++ else ++ root->rb_node = child; ++ ++ if (node->rb_parent == old) ++ parent = node; ++ node->rb_parent = old->rb_parent; ++ node->rb_color = old->rb_color; ++ node->rb_right = old->rb_right; ++ node->rb_left = old->rb_left; ++ ++ if (old->rb_parent) ++ { ++ if (old->rb_parent->rb_left == old) ++ old->rb_parent->rb_left = node; ++ else ++ old->rb_parent->rb_right = node; ++ } else ++ root->rb_node = node; ++ ++ old->rb_left->rb_parent = node; ++ if (old->rb_right) ++ old->rb_right->rb_parent = node; ++ goto color; ++ } ++ ++ parent = node->rb_parent; ++ color = node->rb_color; ++ ++ if (child) ++ child->rb_parent = parent; ++ if (parent) ++ { ++ if (parent->rb_left == node) ++ parent->rb_left = child; ++ else ++ parent->rb_right = child; ++ } ++ else ++ root->rb_node = child; ++ ++ color: ++ if (color == RB_BLACK) ++ __rb_erase_color(child, parent, root); ++} ++#endif /* Before 2.4.11 */ ++ ++ /* These routines haven't made it into 2.4 (yet) */ ++struct rb_node *rb_next(struct rb_node *node) ++{ ++ /* If we have a right-hand child, go down and then left as far ++ as we can. */ ++ if (node->rb_right) { ++ node = node->rb_right; ++ while (node->rb_left) ++ node=node->rb_left; ++ return node; ++ } ++ ++ /* No right-hand children. Everything down and left is ++ smaller than us, so any 'next' node must be in the general ++ direction of our parent. Go up the tree; any time the ++ ancestor is a right-hand child of its parent, keep going ++ up. First time it's a left-hand child of its parent, said ++ parent is our 'next' node. */ ++ while (node->rb_parent && node == node->rb_parent->rb_right) ++ node = node->rb_parent; ++ ++ return node->rb_parent; ++} ++ ++struct rb_node *rb_prev(struct rb_node *node) ++{ ++ if (node->rb_left) { ++ node = node->rb_left; ++ while (node->rb_right) ++ node=node->rb_right; ++ return node; ++ } ++ while (node->rb_parent && node == node->rb_parent->rb_left) ++ node = node->rb_parent; ++ ++ return node->rb_parent; ++} ++ ++void rb_replace_node(struct rb_node *victim, struct rb_node *new, struct rb_root *root) ++{ ++ struct rb_node *parent = victim->rb_parent; ++ ++ /* Set the surrounding nodes to point to the replacement */ ++ if (parent) { ++ if (victim == parent->rb_left) ++ parent->rb_left = new; ++ else ++ parent->rb_right = new; ++ } else { ++ root->rb_node = new; ++ } ++ if (victim->rb_left) ++ victim->rb_left->rb_parent = new; ++ if (victim->rb_right) ++ victim->rb_right->rb_parent = new; ++ ++ /* Copy the pointers/colour from the victim to the replacement */ ++ *new = *victim; ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/read.c linux/fs/jffs2/read.c +--- linux-mips-2.4.27/fs/jffs2/read.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/read.c 2004-11-19 10:25:12.121166048 +0100 +@@ -1,52 +1,29 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: read.c,v 1.13.2.2 2003/11/02 13:51:18 dwmw2 Exp $ ++ * $Id: read.c,v 1.34 2003/10/04 08:33:06 dwmw2 Exp $ + * + */ + + #include <linux/kernel.h> + #include <linux/slab.h> +-#include <linux/jffs2.h> ++#include <linux/crc32.h> ++#include <linux/pagemap.h> + #include <linux/mtd/mtd.h> ++#include <linux/compiler.h> + #include "nodelist.h" +-#include <linux/crc32.h> + + int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsigned char *buf, int ofs, int len) + { + struct jffs2_raw_inode *ri; + size_t readlen; +- __u32 crc; ++ uint32_t crc; + unsigned char *decomprbuf = NULL; + unsigned char *readbuf = NULL; + int ret = 0; +@@ -55,35 +32,41 @@ + if (!ri) + return -ENOMEM; + +- ret = c->mtd->read(c->mtd, fd->raw->flash_offset & ~3, sizeof(*ri), &readlen, (char *)ri); ++ ret = jffs2_flash_read(c, ref_offset(fd->raw), sizeof(*ri), &readlen, (char *)ri); + if (ret) { + jffs2_free_raw_inode(ri); +- printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", fd->raw->flash_offset & ~3, ret); ++ printk(KERN_WARNING "Error reading node from 0x%08x: %d\n", ref_offset(fd->raw), ret); + return ret; + } + if (readlen != sizeof(*ri)) { + jffs2_free_raw_inode(ri); +- printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%x bytes, got 0x%x\n", +- fd->raw->flash_offset & ~3, sizeof(*ri), readlen); ++ printk(KERN_WARNING "Short read from 0x%08x: wanted 0x%zx bytes, got 0x%zx\n", ++ ref_offset(fd->raw), sizeof(*ri), readlen); + return -EIO; + } + crc = crc32(0, ri, sizeof(*ri)-8); + +- D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", fd->raw->flash_offset & ~3, ri->node_crc, crc, ri->dsize, ri->csize, ri->offset, buf)); +- if (crc != ri->node_crc) { +- printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ri->node_crc, crc, fd->raw->flash_offset & ~3); ++ D1(printk(KERN_DEBUG "Node read from %08x: node_crc %08x, calculated CRC %08x. dsize %x, csize %x, offset %x, buf %p\n", ++ ref_offset(fd->raw), je32_to_cpu(ri->node_crc), ++ crc, je32_to_cpu(ri->dsize), je32_to_cpu(ri->csize), ++ je32_to_cpu(ri->offset), buf)); ++ if (crc != je32_to_cpu(ri->node_crc)) { ++ printk(KERN_WARNING "Node CRC %08x != calculated CRC %08x for node at %08x\n", ++ je32_to_cpu(ri->node_crc), crc, ref_offset(fd->raw)); + ret = -EIO; + goto out_ri; + } + /* There was a bug where we wrote hole nodes out with csize/dsize + swapped. Deal with it */ +- if (ri->compr == JFFS2_COMPR_ZERO && !ri->dsize && ri->csize) { ++ if (ri->compr == JFFS2_COMPR_ZERO && !je32_to_cpu(ri->dsize) && ++ je32_to_cpu(ri->csize)) { + ri->dsize = ri->csize; +- ri->csize = 0; ++ ri->csize = cpu_to_je32(0); + } + +- D1(if(ofs + len > ri->dsize) { +- printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", len, ofs, ri->dsize); ++ D1(if(ofs + len > je32_to_cpu(ri->dsize)) { ++ printk(KERN_WARNING "jffs2_read_dnode() asked for %d bytes at %d from %d-byte node\n", ++ len, ofs, je32_to_cpu(ri->dsize)); + ret = -EINVAL; + goto out_ri; + }); +@@ -100,18 +83,18 @@ + Reading partial node and it's uncompressed - read into readbuf, check CRC, and copy + Reading partial node and it's compressed - read into readbuf, check checksum, decompress to decomprbuf and copy + */ +- if (ri->compr == JFFS2_COMPR_NONE && len == ri->dsize) { ++ if (ri->compr == JFFS2_COMPR_NONE && len == je32_to_cpu(ri->dsize)) { + readbuf = buf; + } else { +- readbuf = kmalloc(ri->csize, GFP_KERNEL); ++ readbuf = kmalloc(je32_to_cpu(ri->csize), GFP_KERNEL); + if (!readbuf) { + ret = -ENOMEM; + goto out_ri; + } + } + if (ri->compr != JFFS2_COMPR_NONE) { +- if (len < ri->dsize) { +- decomprbuf = kmalloc(ri->dsize, GFP_KERNEL); ++ if (len < je32_to_cpu(ri->dsize)) { ++ decomprbuf = kmalloc(je32_to_cpu(ri->dsize), GFP_KERNEL); + if (!decomprbuf) { + ret = -ENOMEM; + goto out_readbuf; +@@ -123,31 +106,35 @@ + decomprbuf = readbuf; + } + +- D2(printk(KERN_DEBUG "Read %d bytes to %p\n", ri->csize, readbuf)); +- ret = c->mtd->read(c->mtd, (fd->raw->flash_offset &~3) + sizeof(*ri), ri->csize, &readlen, readbuf); ++ D2(printk(KERN_DEBUG "Read %d bytes to %p\n", je32_to_cpu(ri->csize), ++ readbuf)); ++ ret = jffs2_flash_read(c, (ref_offset(fd->raw)) + sizeof(*ri), ++ je32_to_cpu(ri->csize), &readlen, readbuf); + +- if (!ret && readlen != ri->csize) ++ if (!ret && readlen != je32_to_cpu(ri->csize)) + ret = -EIO; + if (ret) + goto out_decomprbuf; + +- crc = crc32(0, readbuf, ri->csize); +- if (crc != ri->data_crc) { +- printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ri->data_crc, crc, fd->raw->flash_offset & ~3); ++ crc = crc32(0, readbuf, je32_to_cpu(ri->csize)); ++ if (crc != je32_to_cpu(ri->data_crc)) { ++ printk(KERN_WARNING "Data CRC %08x != calculated CRC %08x for node at %08x\n", ++ je32_to_cpu(ri->data_crc), crc, ref_offset(fd->raw)); + ret = -EIO; + goto out_decomprbuf; + } + D2(printk(KERN_DEBUG "Data CRC matches calculated CRC %08x\n", crc)); + if (ri->compr != JFFS2_COMPR_NONE) { +- D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ri->csize, readbuf, ri->dsize, decomprbuf)); +- ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, ri->csize, ri->dsize); ++ D2(printk(KERN_DEBUG "Decompress %d bytes from %p to %d bytes at %p\n", ++ je32_to_cpu(ri->csize), readbuf, je32_to_cpu(ri->dsize), decomprbuf)); ++ ret = jffs2_decompress(ri->compr, readbuf, decomprbuf, je32_to_cpu(ri->csize), je32_to_cpu(ri->dsize)); + if (ret) { + printk(KERN_WARNING "Error: jffs2_decompress returned %d\n", ret); + goto out_decomprbuf; + } + } + +- if (len < ri->dsize) { ++ if (len < je32_to_cpu(ri->dsize)) { + memcpy(buf, decomprbuf+ofs, len); + } + out_decomprbuf: +@@ -161,3 +148,96 @@ + + return ret; + } ++ ++int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ unsigned char *buf, uint32_t offset, uint32_t len) ++{ ++ uint32_t end = offset + len; ++ struct jffs2_node_frag *frag; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2_read_inode_range: ino #%u, range 0x%08x-0x%08x\n", ++ f->inocache->ino, offset, offset+len)); ++ ++ frag = jffs2_lookup_node_frag(&f->fragtree, offset); ++ ++ /* XXX FIXME: Where a single physical node actually shows up in two ++ frags, we read it twice. Don't do that. */ ++ /* Now we're pointing at the first frag which overlaps our page */ ++ while(offset < end) { ++ D2(printk(KERN_DEBUG "jffs2_read_inode_range: offset %d, end %d\n", offset, end)); ++ if (unlikely(!frag || frag->ofs > offset)) { ++ uint32_t holesize = end - offset; ++ if (frag) { ++ D1(printk(KERN_NOTICE "Eep. Hole in ino #%u fraglist. frag->ofs = 0x%08x, offset = 0x%08x\n", f->inocache->ino, frag->ofs, offset)); ++ holesize = min(holesize, frag->ofs - offset); ++ D1(jffs2_print_frag_list(f)); ++ } ++ D1(printk(KERN_DEBUG "Filling non-frag hole from %d-%d\n", offset, offset+holesize)); ++ memset(buf, 0, holesize); ++ buf += holesize; ++ offset += holesize; ++ continue; ++ } else if (unlikely(!frag->node)) { ++ uint32_t holeend = min(end, frag->ofs + frag->size); ++ D1(printk(KERN_DEBUG "Filling frag hole from %d-%d (frag 0x%x 0x%x)\n", offset, holeend, frag->ofs, frag->ofs + frag->size)); ++ memset(buf, 0, holeend - offset); ++ buf += holeend - offset; ++ offset = holeend; ++ frag = frag_next(frag); ++ continue; ++ } else { ++ uint32_t readlen; ++ uint32_t fragofs; /* offset within the frag to start reading */ ++ ++ fragofs = offset - frag->ofs; ++ readlen = min(frag->size - fragofs, end - offset); ++ D1(printk(KERN_DEBUG "Reading %d-%d from node at 0x%08x (%d)\n", ++ frag->ofs+fragofs, frag->ofs+fragofs+readlen, ++ ref_offset(frag->node->raw), ref_flags(frag->node->raw))); ++ ret = jffs2_read_dnode(c, frag->node, buf, fragofs + frag->ofs - frag->node->ofs, readlen); ++ D2(printk(KERN_DEBUG "node read done\n")); ++ if (ret) { ++ D1(printk(KERN_DEBUG"jffs2_read_inode_range error %d\n",ret)); ++ memset(buf, 0, readlen); ++ return ret; ++ } ++ buf += readlen; ++ offset += readlen; ++ frag = frag_next(frag); ++ D2(printk(KERN_DEBUG "node read was OK. Looping\n")); ++ } ++ } ++ return 0; ++} ++ ++/* Core function to read symlink target. */ ++char *jffs2_getlink(struct jffs2_sb_info *c, struct jffs2_inode_info *f) ++{ ++ char *buf; ++ int ret; ++ ++ down(&f->sem); ++ ++ if (!f->metadata) { ++ printk(KERN_NOTICE "No metadata for symlink inode #%u\n", f->inocache->ino); ++ up(&f->sem); ++ return ERR_PTR(-EINVAL); ++ } ++ buf = kmalloc(f->metadata->size+1, GFP_USER); ++ if (!buf) { ++ up(&f->sem); ++ return ERR_PTR(-ENOMEM); ++ } ++ buf[f->metadata->size]=0; ++ ++ ret = jffs2_read_dnode(c, f->metadata, buf, 0, f->metadata->size); ++ ++ up(&f->sem); ++ ++ if (ret) { ++ kfree(buf); ++ return ERR_PTR(ret); ++ } ++ return buf; ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/readinode.c linux/fs/jffs2/readinode.c +--- linux-mips-2.4.27/fs/jffs2/readinode.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/readinode.c 2004-11-19 10:25:12.123165744 +0100 +@@ -1,79 +1,122 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: readinode.c,v 1.58.2.8 2003/11/02 13:54:20 dwmw2 Exp $ ++ * $Id: readinode.c,v 1.113 2003/11/03 13:20:33 dwmw2 Exp $ + * + */ + +-/* Given an inode, probably with existing list of fragments, add the new node +- * to the fragment list. +- */ + #include <linux/kernel.h> + #include <linux/slab.h> + #include <linux/fs.h> ++#include <linux/crc32.h> ++#include <linux/pagemap.h> + #include <linux/mtd/mtd.h> +-#include <linux/jffs2.h> ++#include <linux/compiler.h> + #include "nodelist.h" +-#include <linux/crc32.h> + ++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag); + +-D1(void jffs2_print_frag_list(struct jffs2_inode_info *f) ++#if CONFIG_JFFS2_FS_DEBUG >= 1 ++static void jffs2_print_fragtree(struct rb_root *list, int permitbug) + { +- struct jffs2_node_frag *this = f->fraglist; ++ struct jffs2_node_frag *this = frag_first(list); ++ uint32_t lastofs = 0; ++ int buggy = 0; + + while(this) { + if (this->node) +- printk(KERN_DEBUG "frag %04x-%04x: 0x%08x on flash (*%p->%p)\n", this->ofs, this->ofs+this->size, this->node->raw->flash_offset &~3, this, this->next); ++ printk(KERN_DEBUG "frag %04x-%04x: 0x%08x(%d) on flash (*%p). left (%p), right (%p), parent (%p)\n", ++ this->ofs, this->ofs+this->size, ref_offset(this->node->raw), ref_flags(this->node->raw), ++ this, frag_left(this), frag_right(this), frag_parent(this)); + else +- printk(KERN_DEBUG "frag %04x-%04x: hole (*%p->%p)\n", this->ofs, this->ofs+this->size, this, this->next); +- this = this->next; ++ printk(KERN_DEBUG "frag %04x-%04x: hole (*%p). left (%p} right (%p), parent (%p)\n", this->ofs, ++ this->ofs+this->size, this, frag_left(this), frag_right(this), frag_parent(this)); ++ if (this->ofs != lastofs) ++ buggy = 1; ++ lastofs = this->ofs+this->size; ++ this = frag_next(this); ++ } ++ if (buggy && !permitbug) { ++ printk(KERN_CRIT "Frag tree got a hole in it\n"); ++ BUG(); + } ++} ++ ++void jffs2_print_frag_list(struct jffs2_inode_info *f) ++{ ++ jffs2_print_fragtree(&f->fragtree, 0); ++ + if (f->metadata) { +- printk(KERN_DEBUG "metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3); ++ printk(KERN_DEBUG "metadata at 0x%08x\n", ref_offset(f->metadata->raw)); + } +-}) +- ++} + +-int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) ++static int jffs2_sanitycheck_fragtree(struct jffs2_inode_info *f) + { +- int ret; +- D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn)); ++ struct jffs2_node_frag *frag; ++ int bitched = 0; + +- ret = jffs2_add_full_dnode_to_fraglist(c, &f->fraglist, fn); ++ for (frag = frag_first(&f->fragtree); frag; frag = frag_next(frag)) { + +- D2(jffs2_print_frag_list(f)); +- return ret; ++ struct jffs2_full_dnode *fn = frag->node; ++ if (!fn || !fn->raw) ++ continue; ++ ++ if (ref_flags(fn->raw) == REF_PRISTINE) { ++ ++ if (fn->frags > 1) { ++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(fn->raw), fn->frags); ++ bitched = 1; ++ } ++ /* A hole node which isn't multi-page should be garbage-collected ++ and merged anyway, so we just check for the frag size here, ++ rather than mucking around with actually reading the node ++ and checking the compression type, which is the real way ++ to tell a hole node. */ ++ if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->size < PAGE_CACHE_SIZE && frag_prev(frag)->node) { ++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n", ++ ref_offset(fn->raw)); ++ bitched = 1; ++ } ++ ++ if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE && frag_next(frag)->node) { ++ printk(KERN_WARNING "REF_PRISTINE node at 0x%08x (%08x-%08x) had a following non-hole frag in the same page. Tell dwmw2\n", ++ ref_offset(fn->raw), frag->ofs, frag->ofs+frag->size); ++ bitched = 1; ++ } ++ } ++ } ++ ++ if (bitched) { ++ struct jffs2_node_frag *thisfrag; ++ ++ printk(KERN_WARNING "Inode is #%u\n", f->inocache->ino); ++ thisfrag = frag_first(&f->fragtree); ++ while (thisfrag) { ++ if (!thisfrag->node) { ++ printk("Frag @0x%x-0x%x; node-less hole\n", ++ thisfrag->ofs, thisfrag->size + thisfrag->ofs); ++ } else if (!thisfrag->node->raw) { ++ printk("Frag @0x%x-0x%x; raw-less hole\n", ++ thisfrag->ofs, thisfrag->size + thisfrag->ofs); ++ } else { ++ printk("Frag @0x%x-0x%x; raw at 0x%08x(%d) (0x%x-0x%x)\n", ++ thisfrag->ofs, thisfrag->size + thisfrag->ofs, ++ ref_offset(thisfrag->node->raw), ref_flags(thisfrag->node->raw), ++ thisfrag->node->ofs, thisfrag->node->ofs+thisfrag->node->size); ++ } ++ thisfrag = frag_next(thisfrag); ++ } ++ } ++ return bitched; + } ++#endif /* D1 */ + + static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) + { +@@ -82,42 +125,38 @@ + if (!this->node->frags) { + /* The node has no valid frags left. It's totally obsoleted */ + D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) obsolete\n", +- this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size)); ++ ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size)); + jffs2_mark_node_obsolete(c, this->node->raw); + jffs2_free_full_dnode(this->node); + } else { +- D2(printk(KERN_DEBUG "Not marking old node @0x%08x (0x%04x-0x%04x) obsolete. frags is %d\n", +- this->node->raw->flash_offset &~3, this->node->ofs, this->node->ofs+this->node->size, ++ D2(printk(KERN_DEBUG "Marking old node @0x%08x (0x%04x-0x%04x) REF_NORMAL. frags is %d\n", ++ ref_offset(this->node->raw), this->node->ofs, this->node->ofs+this->node->size, + this->node->frags)); ++ mark_ref_normal(this->node->raw); + } + + } + jffs2_free_node_frag(this); + } + +-/* Doesn't set inode->i_size */ +-int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct jffs2_node_frag **list, struct jffs2_full_dnode *fn) ++/* Given an inode, probably with existing list of fragments, add the new node ++ * to the fragment list. ++ */ ++int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn) + { ++ int ret; ++ struct jffs2_node_frag *newfrag; + +- struct jffs2_node_frag *this, **prev, *old; +- struct jffs2_node_frag *newfrag, *newfrag2; +- __u32 lastend = 0; +- ++ D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn)); + + newfrag = jffs2_alloc_node_frag(); +- if (!newfrag) { ++ if (unlikely(!newfrag)) + return -ENOMEM; +- } +- +- D2(if (fn->raw) +- printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, fn->raw->flash_offset &~3, newfrag); +- else +- printk(KERN_DEBUG "adding hole node %04x-%04x on flash, newfrag *%p\n", fn->ofs, fn->ofs+fn->size, newfrag)); + +- prev = list; +- this = *list; ++ D2(printk(KERN_DEBUG "adding node %04x-%04x @0x%08x on flash, newfrag *%p\n", ++ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag)); + +- if (!fn->size) { ++ if (unlikely(!fn->size)) { + jffs2_free_node_frag(newfrag); + return 0; + } +@@ -126,176 +165,358 @@ + newfrag->size = fn->size; + newfrag->node = fn; + newfrag->node->frags = 1; +- newfrag->next = (void *)0xdeadbeef; ++ ++ ret = jffs2_add_frag_to_fragtree(c, &f->fragtree, newfrag); ++ if (ret) ++ return ret; ++ ++ /* If we now share a page with other nodes, mark either previous ++ or next node REF_NORMAL, as appropriate. */ ++ if (newfrag->ofs & (PAGE_CACHE_SIZE-1)) { ++ struct jffs2_node_frag *prev = frag_prev(newfrag); ++ ++ mark_ref_normal(fn->raw); ++ /* If we don't start at zero there's _always_ a previous */ ++ if (prev->node) ++ mark_ref_normal(prev->node->raw); ++ } ++ ++ if ((newfrag->ofs+newfrag->size) & (PAGE_CACHE_SIZE-1)) { ++ struct jffs2_node_frag *next = frag_next(newfrag); ++ ++ if (next) { ++ mark_ref_normal(fn->raw); ++ if (next->node) ++ mark_ref_normal(next->node->raw); ++ } ++ } ++ D2(if (jffs2_sanitycheck_fragtree(f)) { ++ printk(KERN_WARNING "Just added node %04x-%04x @0x%08x on flash, newfrag *%p\n", ++ fn->ofs, fn->ofs+fn->size, ref_offset(fn->raw), newfrag); ++ return 0; ++ }) ++ D2(jffs2_print_frag_list(f)); ++ return 0; ++} ++ ++/* Doesn't set inode->i_size */ ++static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag) ++{ ++ struct jffs2_node_frag *this; ++ uint32_t lastend; + + /* Skip all the nodes which are completed before this one starts */ +- while(this && fn->ofs >= this->ofs+this->size) { +- lastend = this->ofs + this->size; ++ this = jffs2_lookup_node_frag(list, newfrag->node->ofs); + +- D2(printk(KERN_DEBUG "j_a_f_d_t_f: skipping frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", +- this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next)); +- prev = &this->next; +- this = this->next; ++ if (this) { ++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", ++ this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this)); ++ lastend = this->ofs + this->size; ++ } else { ++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave no frag\n")); ++ lastend = 0; + } + + /* See if we ran off the end of the list */ +- if (!this) { ++ if (lastend <= newfrag->ofs) { + /* We did */ +- if (lastend < fn->ofs) { ++ ++ /* Check if 'this' node was on the same page as the new node. ++ If so, both 'this' and the new node get marked REF_NORMAL so ++ the GC can take a look. ++ */ ++ if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { ++ if (this->node) ++ mark_ref_normal(this->node->raw); ++ mark_ref_normal(newfrag->node->raw); ++ } ++ ++ if (lastend < newfrag->node->ofs) { + /* ... and we need to put a hole in before the new node */ + struct jffs2_node_frag *holefrag = jffs2_alloc_node_frag(); +- if (!holefrag) ++ if (!holefrag) { ++ jffs2_free_node_frag(newfrag); + return -ENOMEM; ++ } + holefrag->ofs = lastend; +- holefrag->size = fn->ofs - lastend; +- holefrag->next = NULL; ++ holefrag->size = newfrag->node->ofs - lastend; + holefrag->node = NULL; +- *prev = holefrag; +- prev = &holefrag->next; ++ if (this) { ++ /* By definition, the 'this' node has no right-hand child, ++ because there are no frags with offset greater than it. ++ So that's where we want to put the hole */ ++ D2(printk(KERN_DEBUG "Adding hole frag (%p) on right of node at (%p)\n", holefrag, this)); ++ rb_link_node(&holefrag->rb, &this->rb, &this->rb.rb_right); ++ } else { ++ D2(printk(KERN_DEBUG "Adding hole frag (%p) at root of tree\n", holefrag)); ++ rb_link_node(&holefrag->rb, NULL, &list->rb_node); ++ } ++ rb_insert_color(&holefrag->rb, list); ++ this = holefrag; + } +- newfrag->next = NULL; +- *prev = newfrag; ++ if (this) { ++ /* By definition, the 'this' node has no right-hand child, ++ because there are no frags with offset greater than it. ++ So that's where we want to put the hole */ ++ D2(printk(KERN_DEBUG "Adding new frag (%p) on right of node at (%p)\n", newfrag, this)); ++ rb_link_node(&newfrag->rb, &this->rb, &this->rb.rb_right); ++ } else { ++ D2(printk(KERN_DEBUG "Adding new frag (%p) at root of tree\n", newfrag)); ++ rb_link_node(&newfrag->rb, NULL, &list->rb_node); ++ } ++ rb_insert_color(&newfrag->rb, list); + return 0; + } + +- D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p->%p)\n", +- this->ofs, this->ofs+this->size, this->node?(this->node->raw->flash_offset &~3):0xffffffff, this, this->next)); ++ D2(printk(KERN_DEBUG "j_a_f_d_t_f: dealing with frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", ++ this->ofs, this->ofs+this->size, this->node?(ref_offset(this->node->raw)):0xffffffff, this)); + +- /* OK. 'this' is pointing at the first frag that fn->ofs at least partially obsoletes, +- * - i.e. fn->ofs < this->ofs+this->size && fn->ofs >= this->ofs ++ /* OK. 'this' is pointing at the first frag that newfrag->ofs at least partially obsoletes, ++ * - i.e. newfrag->ofs < this->ofs+this->size && newfrag->ofs >= this->ofs + */ +- if (fn->ofs > this->ofs) { ++ if (newfrag->ofs > this->ofs) { + /* This node isn't completely obsoleted. The start of it remains valid */ +- if (this->ofs + this->size > fn->ofs + fn->size) { ++ ++ /* Mark the new node and the partially covered node REF_NORMAL -- let ++ the GC take a look at them */ ++ mark_ref_normal(newfrag->node->raw); ++ if (this->node) ++ mark_ref_normal(this->node->raw); ++ ++ if (this->ofs + this->size > newfrag->ofs + newfrag->size) { + /* The new node splits 'this' frag into two */ +- newfrag2 = jffs2_alloc_node_frag(); ++ struct jffs2_node_frag *newfrag2 = jffs2_alloc_node_frag(); + if (!newfrag2) { + jffs2_free_node_frag(newfrag); + return -ENOMEM; + } +- D1(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); ++ D2(printk(KERN_DEBUG "split old frag 0x%04x-0x%04x -->", this->ofs, this->ofs+this->size); + if (this->node) +- printk("phys 0x%08x\n", this->node->raw->flash_offset &~3); ++ printk("phys 0x%08x\n", ref_offset(this->node->raw)); + else + printk("hole\n"); + ) +- newfrag2->ofs = fn->ofs + fn->size; ++ ++ /* New second frag pointing to this's node */ ++ newfrag2->ofs = newfrag->ofs + newfrag->size; + newfrag2->size = (this->ofs+this->size) - newfrag2->ofs; +- newfrag2->next = this->next; + newfrag2->node = this->node; + if (this->node) + this->node->frags++; +- newfrag->next = newfrag2; +- this->next = newfrag; ++ ++ /* Adjust size of original 'this' */ + this->size = newfrag->ofs - this->ofs; ++ ++ /* Now, we know there's no node with offset ++ greater than this->ofs but smaller than ++ newfrag2->ofs or newfrag->ofs, for obvious ++ reasons. So we can do a tree insert from ++ 'this' to insert newfrag, and a tree insert ++ from newfrag to insert newfrag2. */ ++ jffs2_fragtree_insert(newfrag, this); ++ rb_insert_color(&newfrag->rb, list); ++ ++ jffs2_fragtree_insert(newfrag2, newfrag); ++ rb_insert_color(&newfrag2->rb, list); ++ + return 0; + } + /* New node just reduces 'this' frag in size, doesn't split it */ +- this->size = fn->ofs - this->ofs; +- newfrag->next = this->next; +- this->next = newfrag; +- this = newfrag->next; ++ this->size = newfrag->ofs - this->ofs; ++ ++ /* Again, we know it lives down here in the tree */ ++ jffs2_fragtree_insert(newfrag, this); ++ rb_insert_color(&newfrag->rb, list); + } else { +- D2(printk(KERN_DEBUG "Inserting newfrag (*%p) in before 'this' (*%p)\n", newfrag, this)); +- *prev = newfrag; +- newfrag->next = this; ++ /* New frag starts at the same point as 'this' used to. Replace ++ it in the tree without doing a delete and insertion */ ++ D2(printk(KERN_DEBUG "Inserting newfrag (*%p),%d-%d in before 'this' (*%p),%d-%d\n", ++ newfrag, newfrag->ofs, newfrag->ofs+newfrag->size, ++ this, this->ofs, this->ofs+this->size)); ++ ++ rb_replace_node(&this->rb, &newfrag->rb, list); ++ ++ if (newfrag->ofs + newfrag->size >= this->ofs+this->size) { ++ D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x)\n", this, this->ofs, this->ofs+this->size)); ++ jffs2_obsolete_node_frag(c, this); ++ } else { ++ this->ofs += newfrag->size; ++ this->size -= newfrag->size; ++ ++ jffs2_fragtree_insert(this, newfrag); ++ rb_insert_color(&this->rb, list); ++ return 0; + } +- /* OK, now we have newfrag added in the correct place in the list, but +- newfrag->next points to a fragment which may be overlapping it ++ } ++ /* OK, now we have newfrag added in the correct place in the tree, but ++ frag_next(newfrag) may be a fragment which is overlapped by it + */ +- while (this && newfrag->ofs + newfrag->size >= this->ofs + this->size) { +- /* 'this' frag is obsoleted. */ +- old = this; +- this = old->next; +- jffs2_obsolete_node_frag(c, old); ++ while ((this = frag_next(newfrag)) && newfrag->ofs + newfrag->size >= this->ofs + this->size) { ++ /* 'this' frag is obsoleted completely. */ ++ D2(printk(KERN_DEBUG "Obsoleting node frag %p (%x-%x) and removing from tree\n", this, this->ofs, this->ofs+this->size)); ++ rb_erase(&this->rb, list); ++ jffs2_obsolete_node_frag(c, this); + } + /* Now we're pointing at the first frag which isn't totally obsoleted by + the new frag */ +- newfrag->next = this; + + if (!this || newfrag->ofs + newfrag->size == this->ofs) { + return 0; + } +- /* Still some overlap */ ++ /* Still some overlap but we don't need to move it in the tree */ + this->size = (this->ofs + this->size) - (newfrag->ofs + newfrag->size); + this->ofs = newfrag->ofs + newfrag->size; ++ ++ /* And mark them REF_NORMAL so the GC takes a look at them */ ++ if (this->node) ++ mark_ref_normal(this->node->raw); ++ mark_ref_normal(newfrag->node->raw); ++ + return 0; + } + +-void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct jffs2_node_frag **list, __u32 size) ++void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size) + { ++ struct jffs2_node_frag *frag = jffs2_lookup_node_frag(list, size); ++ + D1(printk(KERN_DEBUG "Truncating fraglist to 0x%08x bytes\n", size)); + +- while (*list) { +- if ((*list)->ofs >= size) { +- struct jffs2_node_frag *this = *list; +- *list = this->next; +- D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", this->ofs, this->ofs+this->size)); +- jffs2_obsolete_node_frag(c, this); +- continue; +- } else if ((*list)->ofs + (*list)->size > size) { +- D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", (*list)->ofs, (*list)->ofs + (*list)->size)); +- (*list)->size = size - (*list)->ofs; +- } +- list = &(*list)->next; ++ /* We know frag->ofs <= size. That's what lookup does for us */ ++ if (frag && frag->ofs != size) { ++ if (frag->ofs+frag->size >= size) { ++ D1(printk(KERN_DEBUG "Truncating frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size)); ++ frag->size = size - frag->ofs; ++ } ++ frag = frag_next(frag); ++ } ++ while (frag && frag->ofs >= size) { ++ struct jffs2_node_frag *next = frag_next(frag); ++ ++ D1(printk(KERN_DEBUG "Removing frag 0x%08x-0x%08x\n", frag->ofs, frag->ofs+frag->size)); ++ frag_erase(frag, list); ++ jffs2_obsolete_node_frag(c, frag); ++ frag = next; + } + } + + /* Scan the list of all nodes present for this ino, build map of versions, etc. */ + +-void jffs2_read_inode (struct inode *inode) ++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f, ++ struct jffs2_raw_inode *latest_node); ++ ++int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ uint32_t ino, struct jffs2_raw_inode *latest_node) + { +- struct jffs2_tmp_dnode_info *tn_list, *tn; +- struct jffs2_full_dirent *fd_list; +- struct jffs2_inode_info *f; +- struct jffs2_full_dnode *fn = NULL; +- struct jffs2_sb_info *c; +- struct jffs2_raw_inode latest_node; +- __u32 latest_mctime, mctime_ver; +- __u32 mdata_ver = 0; +- int ret; +- ssize_t retlen; ++ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n")); + +- D1(printk(KERN_DEBUG "jffs2_read_inode(): inode->i_ino == %lu\n", inode->i_ino)); ++ retry_inocache: ++ spin_lock(&c->inocache_lock); ++ f->inocache = jffs2_get_ino_cache(c, ino); ++ ++ D2(printk(KERN_DEBUG "jffs2_do_read_inode(): Got inocache at %p\n", f->inocache)); ++ ++ if (f->inocache) { ++ /* Check its state. We may need to wait before we can use it */ ++ switch(f->inocache->state) { ++ case INO_STATE_UNCHECKED: ++ case INO_STATE_CHECKEDABSENT: ++ f->inocache->state = INO_STATE_READING; ++ break; + +- f = JFFS2_INODE_INFO(inode); +- c = JFFS2_SB_INFO(inode->i_sb); ++ case INO_STATE_CHECKING: ++ case INO_STATE_GC: ++ /* If it's in either of these states, we need ++ to wait for whoever's got it to finish and ++ put it back. */ ++ D1(printk(KERN_DEBUG "jffs2_get_ino_cache_read waiting for ino #%u in state %d\n", ++ ino, f->inocache->state)); ++ sleep_on_spinunlock(&c->inocache_wq, &c->inocache_lock); ++ goto retry_inocache; ++ ++ case INO_STATE_READING: ++ case INO_STATE_PRESENT: ++ /* Eep. This should never happen. It can ++ happen if Linux calls read_inode() again ++ before clear_inode() has finished though. */ ++ printk(KERN_WARNING "Eep. Trying to read_inode #%u when it's already in state %d!\n", ino, f->inocache->state); ++ /* Fail. That's probably better than allowing it to succeed */ ++ f->inocache = NULL; ++ break; + +- memset(f, 0, sizeof(*f)); +- D2(printk(KERN_DEBUG "getting inocache\n")); +- init_MUTEX(&f->sem); +- f->inocache = jffs2_get_ino_cache(c, inode->i_ino); +- D2(printk(KERN_DEBUG "jffs2_read_inode(): Got inocache at %p\n", f->inocache)); ++ default: ++ BUG(); ++ } ++ } ++ spin_unlock(&c->inocache_lock); + +- if (!f->inocache && inode->i_ino == 1) { ++ if (!f->inocache && ino == 1) { + /* Special case - no root inode on medium */ + f->inocache = jffs2_alloc_inode_cache(); + if (!f->inocache) { +- printk(KERN_CRIT "jffs2_read_inode(): Cannot allocate inocache for root inode\n"); +- make_bad_inode(inode); +- return; ++ printk(KERN_CRIT "jffs2_do_read_inode(): Cannot allocate inocache for root inode\n"); ++ return -ENOMEM; + } +- D1(printk(KERN_DEBUG "jffs2_read_inode(): Creating inocache for root inode\n")); ++ D1(printk(KERN_DEBUG "jffs2_do_read_inode(): Creating inocache for root inode\n")); + memset(f->inocache, 0, sizeof(struct jffs2_inode_cache)); + f->inocache->ino = f->inocache->nlink = 1; + f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; ++ f->inocache->state = INO_STATE_READING; + jffs2_add_ino_cache(c, f->inocache); + } + if (!f->inocache) { +- printk(KERN_WARNING "jffs2_read_inode() on nonexistent ino %lu\n", (unsigned long)inode->i_ino); +- make_bad_inode(inode); +- return; ++ printk(KERN_WARNING "jffs2_do_read_inode() on nonexistent ino %u\n", ino); ++ return -ENOENT; + } +- D1(printk(KERN_DEBUG "jffs2_read_inode(): ino #%lu nlink is %d\n", (unsigned long)inode->i_ino, f->inocache->nlink)); +- inode->i_nlink = f->inocache->nlink; ++ ++ return jffs2_do_read_inode_internal(c, f, latest_node); ++} ++ ++int jffs2_do_crccheck_inode(struct jffs2_sb_info *c, struct jffs2_inode_cache *ic) ++{ ++ struct jffs2_raw_inode n; ++ struct jffs2_inode_info *f = kmalloc(sizeof(*f), GFP_KERNEL); ++ int ret; ++ ++ if (!f) ++ return -ENOMEM; ++ ++ memset(f, 0, sizeof(*f)); ++ init_MUTEX_LOCKED(&f->sem); ++ f->inocache = ic; ++ ++ ret = jffs2_do_read_inode_internal(c, f, &n); ++ if (!ret) { ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ } ++ kfree (f); ++ return ret; ++} ++ ++static int jffs2_do_read_inode_internal(struct jffs2_sb_info *c, ++ struct jffs2_inode_info *f, ++ struct jffs2_raw_inode *latest_node) ++{ ++ struct jffs2_tmp_dnode_info *tn_list, *tn; ++ struct jffs2_full_dirent *fd_list; ++ struct jffs2_full_dnode *fn = NULL; ++ uint32_t crc; ++ uint32_t latest_mctime, mctime_ver; ++ uint32_t mdata_ver = 0; ++ size_t retlen; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2_do_read_inode_internal(): ino #%u nlink is %d\n", f->inocache->ino, f->inocache->nlink)); + + /* Grab all nodes relevant to this ino */ +- ret = jffs2_get_inode_nodes(c, inode->i_ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); ++ ret = jffs2_get_inode_nodes(c, f->inocache->ino, f, &tn_list, &fd_list, &f->highest_version, &latest_mctime, &mctime_ver); + + if (ret) { +- printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %lu returned %d\n", inode->i_ino, ret); +- make_bad_inode(inode); +- return; ++ printk(KERN_CRIT "jffs2_get_inode_nodes() for ino %u returned %d\n", f->inocache->ino, ret); ++ if (f->inocache->state == INO_STATE_READING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); ++ return ret; + } + f->dents = fd_list; + +@@ -304,205 +525,169 @@ + + fn = tn->fn; + +- if (f->metadata && tn->version > mdata_ver) { +- D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", f->metadata->raw->flash_offset &~3)); ++ if (f->metadata) { ++ if (likely(tn->version >= mdata_ver)) { ++ D1(printk(KERN_DEBUG "Obsoleting old metadata at 0x%08x\n", ref_offset(f->metadata->raw))); + jffs2_mark_node_obsolete(c, f->metadata->raw); + jffs2_free_full_dnode(f->metadata); + f->metadata = NULL; + + mdata_ver = 0; ++ } else { ++ /* This should never happen. */ ++ printk(KERN_WARNING "Er. New metadata at 0x%08x with ver %d is actually older than previous ver %d at 0x%08x\n", ++ ref_offset(fn->raw), tn->version, mdata_ver, ref_offset(f->metadata->raw)); ++ jffs2_mark_node_obsolete(c, fn->raw); ++ jffs2_free_full_dnode(fn); ++ /* Fill in latest_node from the metadata, not this one we're about to free... */ ++ fn = f->metadata; ++ goto next_tn; ++ } + } + + if (fn->size) { + jffs2_add_full_dnode_to_inode(c, f, fn); + } else { + /* Zero-sized node at end of version list. Just a metadata update */ +- D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", fn->raw->flash_offset &~3, tn->version)); ++ D1(printk(KERN_DEBUG "metadata @%08x: ver %d\n", ref_offset(fn->raw), tn->version)); + f->metadata = fn; + mdata_ver = tn->version; + } ++ next_tn: + tn_list = tn->next; + jffs2_free_tmp_dnode_info(tn); + } ++ D1(jffs2_sanitycheck_fragtree(f)); ++ + if (!fn) { + /* No data nodes for this inode. */ +- if (inode->i_ino != 1) { +- printk(KERN_WARNING "jffs2_read_inode(): No data nodes found for ino #%lu\n", inode->i_ino); ++ if (f->inocache->ino != 1) { ++ printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", f->inocache->ino); + if (!fd_list) { +- make_bad_inode(inode); +- return; +- } +- printk(KERN_WARNING "jffs2_read_inode(): But it has children so we fake some modes for it\n"); ++ if (f->inocache->state == INO_STATE_READING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); ++ return -EIO; ++ } ++ printk(KERN_WARNING "jffs2_do_read_inode(): But it has children so we fake some modes for it\n"); ++ } ++ latest_node->mode = cpu_to_jemode(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); ++ latest_node->version = cpu_to_je32(0); ++ latest_node->atime = latest_node->ctime = latest_node->mtime = cpu_to_je32(0); ++ latest_node->isize = cpu_to_je32(0); ++ latest_node->gid = cpu_to_je16(0); ++ latest_node->uid = cpu_to_je16(0); ++ if (f->inocache->state == INO_STATE_READING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); ++ return 0; + } +- inode->i_mode = S_IFDIR | S_IRUGO | S_IWUSR | S_IXUGO; +- latest_node.version = 0; +- inode->i_atime = inode->i_ctime = inode->i_mtime = CURRENT_TIME; +- inode->i_nlink = f->inocache->nlink; +- inode->i_size = 0; +- } else { +- __u32 crc; + +- ret = c->mtd->read(c->mtd, fn->raw->flash_offset & ~3, sizeof(latest_node), &retlen, (void *)&latest_node); +- if (ret || retlen != sizeof(latest_node)) { +- printk(KERN_NOTICE "MTD read in jffs2_read_inode() failed: Returned %d, %ld of %d bytes read\n", +- ret, (long)retlen, sizeof(latest_node)); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- +- crc = crc32(0, &latest_node, sizeof(latest_node)-8); +- if (crc != latest_node.node_crc) { +- printk(KERN_NOTICE "CRC failed for read_inode of inode %ld at physical location 0x%x\n", inode->i_ino, fn->raw->flash_offset & ~3); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- +- inode->i_mode = latest_node.mode; +- inode->i_uid = latest_node.uid; +- inode->i_gid = latest_node.gid; +- inode->i_size = latest_node.isize; +- if (S_ISREG(inode->i_mode)) +- jffs2_truncate_fraglist(c, &f->fraglist, latest_node.isize); +- inode->i_atime = latest_node.atime; +- inode->i_mtime = latest_node.mtime; +- inode->i_ctime = latest_node.ctime; +- } +- +- /* OK, now the special cases. Certain inode types should +- have only one data node, and it's kept as the metadata +- node */ +- if (S_ISBLK(inode->i_mode) || S_ISCHR(inode->i_mode) || +- S_ISLNK(inode->i_mode)) { +- if (f->metadata) { +- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had metadata node\n", inode->i_ino, inode->i_mode); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- if (!f->fraglist) { +- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o has no fragments\n", inode->i_ino, inode->i_mode); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- /* ASSERT: f->fraglist != NULL */ +- if (f->fraglist->next) { +- printk(KERN_WARNING "Argh. Special inode #%lu with mode 0%o had more than one node\n", inode->i_ino, inode->i_mode); +- /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; +- } +- /* OK. We're happy */ +- f->metadata = f->fraglist->node; +- jffs2_free_node_frag(f->fraglist); +- f->fraglist = NULL; ++ ret = jffs2_flash_read(c, ref_offset(fn->raw), sizeof(*latest_node), &retlen, (void *)latest_node); ++ if (ret || retlen != sizeof(*latest_node)) { ++ printk(KERN_NOTICE "MTD read in jffs2_do_read_inode() failed: Returned %d, %zd of %zd bytes read\n", ++ ret, retlen, sizeof(*latest_node)); ++ /* FIXME: If this fails, there seems to be a memory leak. Find it. */ ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return ret?ret:-EIO; + } + +- inode->i_blksize = PAGE_SIZE; +- inode->i_blocks = (inode->i_size + 511) >> 9; +- +- switch (inode->i_mode & S_IFMT) { +- unsigned short rdev; +- +- case S_IFLNK: +- inode->i_op = &jffs2_symlink_inode_operations; +- /* Hack to work around broken isize in old symlink code. +- Remove this when dwmw2 comes to his senses and stops +- symlinks from being an entirely gratuitous special +- case. */ +- if (!inode->i_size) +- inode->i_size = latest_node.dsize; +- break; ++ crc = crc32(0, latest_node, sizeof(*latest_node)-8); ++ if (crc != je32_to_cpu(latest_node->node_crc)) { ++ printk(KERN_NOTICE "CRC failed for read_inode of inode %u at physical location 0x%x\n", f->inocache->ino, ref_offset(fn->raw)); ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return -EIO; ++ } + ++ switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { + case S_IFDIR: +- if (mctime_ver > latest_node.version) { ++ if (mctime_ver > je32_to_cpu(latest_node->version)) { + /* The times in the latest_node are actually older than + mctime in the latest dirent. Cheat. */ +- inode->i_mtime = inode->i_ctime = inode->i_atime = +- latest_mctime; ++ latest_node->ctime = latest_node->mtime = cpu_to_je32(latest_mctime); + } +- inode->i_op = &jffs2_dir_inode_operations; +- inode->i_fop = &jffs2_dir_operations; + break; + ++ + case S_IFREG: +- inode->i_op = &jffs2_file_inode_operations; +- inode->i_fop = &jffs2_file_operations; +- inode->i_mapping->a_ops = &jffs2_file_address_operations; +- inode->i_mapping->nrpages = 0; ++ /* If it was a regular file, truncate it to the latest node's isize */ ++ jffs2_truncate_fraglist(c, &f->fragtree, je32_to_cpu(latest_node->isize)); + break; + ++ case S_IFLNK: ++ /* Hack to work around broken isize in old symlink code. ++ Remove this when dwmw2 comes to his senses and stops ++ symlinks from being an entirely gratuitous special ++ case. */ ++ if (!je32_to_cpu(latest_node->isize)) ++ latest_node->isize = latest_node->dsize; ++ /* fall through... */ ++ + case S_IFBLK: + case S_IFCHR: +- /* Read the device numbers from the media */ +- D1(printk(KERN_DEBUG "Reading device numbers from flash\n")); +- if (jffs2_read_dnode(c, f->metadata, (char *)&rdev, 0, sizeof(rdev)) < 0) { +- /* Eep */ +- printk(KERN_NOTICE "Read device numbers for inode %lu failed\n", (unsigned long)inode->i_ino); +- jffs2_clear_inode(inode); +- make_bad_inode(inode); +- return; ++ /* Certain inode types should have only one data node, and it's ++ kept as the metadata node */ ++ if (f->metadata) { ++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o had metadata node\n", ++ f->inocache->ino, jemode_to_cpu(latest_node->mode)); ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return -EIO; + } +- +- case S_IFSOCK: +- case S_IFIFO: +- inode->i_op = &jffs2_file_inode_operations; +- init_special_inode(inode, inode->i_mode, kdev_t_to_nr(MKDEV(rdev>>8, rdev&0xff))); ++ if (!frag_first(&f->fragtree)) { ++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", ++ f->inocache->ino, jemode_to_cpu(latest_node->mode)); ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return -EIO; ++ } ++ /* ASSERT: f->fraglist != NULL */ ++ if (frag_next(frag_first(&f->fragtree))) { ++ printk(KERN_WARNING "Argh. Special inode #%u with mode 0x%x had more than one node\n", ++ f->inocache->ino, jemode_to_cpu(latest_node->mode)); ++ /* FIXME: Deal with it - check crc32, check for duplicate node, check times and discard the older one */ ++ up(&f->sem); ++ jffs2_do_clear_inode(c, f); ++ return -EIO; ++ } ++ /* OK. We're happy */ ++ f->metadata = frag_first(&f->fragtree)->node; ++ jffs2_free_node_frag(frag_first(&f->fragtree)); ++ f->fragtree = RB_ROOT; + break; +- +- default: +- printk(KERN_WARNING "jffs2_read_inode(): Bogus imode %o for ino %lu", inode->i_mode, (unsigned long)inode->i_ino); + } +- D1(printk(KERN_DEBUG "jffs2_read_inode() returning\n")); ++ if (f->inocache->state == INO_STATE_READING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); ++ ++ return 0; + } + +-void jffs2_clear_inode (struct inode *inode) ++void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) + { +- /* We can forget about this inode for now - drop all +- * the nodelists associated with it, etc. +- */ +- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_node_frag *frag, *frags; + struct jffs2_full_dirent *fd, *fds; +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + int deleted; + +- D1(printk(KERN_DEBUG "jffs2_clear_inode(): ino #%lu mode %o\n", inode->i_ino, inode->i_mode)); +- + down(&f->sem); + deleted = f->inocache && !f->inocache->nlink; + +- frags = f->fraglist; +- fds = f->dents; + if (f->metadata) { + if (deleted) + jffs2_mark_node_obsolete(c, f->metadata->raw); + jffs2_free_full_dnode(f->metadata); + } + +- while (frags) { +- frag = frags; +- frags = frag->next; +- D2(printk(KERN_DEBUG "jffs2_clear_inode: frag at 0x%x-0x%x: node %p, frags %d--\n", frag->ofs, frag->ofs+frag->size, frag->node, frag->node?frag->node->frags:0)); ++ jffs2_kill_fragtree(&f->fragtree, deleted?c:NULL); + +- if (frag->node && !(--frag->node->frags)) { +- /* Not a hole, and it's the final remaining frag of this node. Free the node */ +- if (deleted) +- jffs2_mark_node_obsolete(c, frag->node->raw); ++ fds = f->dents; + +- jffs2_free_full_dnode(frag->node); +- } +- jffs2_free_node_frag(frag); +- } + while(fds) { + fd = fds; + fds = fd->next; + jffs2_free_full_dirent(fd); + } + +- up(&f->sem); +-}; ++ if (f->inocache && f->inocache->state != INO_STATE_CHECKING) ++ jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); + ++ up(&f->sem); ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/scan.c linux/fs/jffs2/scan.c +--- linux-mips-2.4.27/fs/jffs2/scan.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/scan.c 2004-11-19 10:25:12.124165592 +0100 +@@ -1,47 +1,25 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: scan.c,v 1.51.2.4 2003/11/02 13:51:18 dwmw2 Exp $ ++ * $Id: scan.c,v 1.106 2003/10/28 17:01:13 dwmw2 Exp $ + * + */ + #include <linux/kernel.h> ++#include <linux/sched.h> + #include <linux/slab.h> +-#include <linux/jffs2.h> + #include <linux/mtd/mtd.h> + #include <linux/pagemap.h> +-#include "nodelist.h" + #include <linux/crc32.h> ++#include <linux/compiler.h> ++#include "nodelist.h" + ++#define EMPTY_SCAN_SIZE 1024 + + #define DIRTY_SPACE(x) do { typeof(x) _x = (x); \ + c->free_size -= _x; c->dirty_size += _x; \ +@@ -51,6 +29,10 @@ + c->free_size -= _x; c->used_size += _x; \ + jeb->free_size -= _x ; jeb->used_size += _x; \ + }while(0) ++#define UNCHECKED_SPACE(x) do { typeof(x) _x = (x); \ ++ c->free_size -= _x; c->unchecked_size += _x; \ ++ jeb->free_size -= _x ; jeb->unchecked_size += _x; \ ++ }while(0) + + #define noisy_printk(noise, args...) do { \ + if (*(noise)) { \ +@@ -63,39 +45,84 @@ + } while(0) + + static uint32_t pseudo_random; +-static void jffs2_rotate_lists(struct jffs2_sb_info *c); + +-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb); ++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ unsigned char *buf, uint32_t buf_size); + + /* These helper functions _must_ increase ofs and also do the dirty/used space accounting. + * Returning an error will abort the mount - bad checksums etc. should just mark the space + * as dirty. + */ +-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs, int *noise); +-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs); +-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs); ++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_inode *ri, uint32_t ofs); ++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_dirent *rd, uint32_t ofs); ++ ++#define BLK_STATE_ALLFF 0 ++#define BLK_STATE_CLEAN 1 ++#define BLK_STATE_PARTDIRTY 2 ++#define BLK_STATE_CLEANMARKER 3 ++#define BLK_STATE_ALLDIRTY 4 ++#define BLK_STATE_BADBLOCK 5 + ++static inline int min_free(struct jffs2_sb_info *c) ++{ ++ uint32_t min = 2 * sizeof(struct jffs2_raw_inode); ++#ifdef CONFIG_JFFS2_FS_NAND ++ if (!jffs2_can_mark_obsolete(c) && min < c->wbuf_pagesize) ++ return c->wbuf_pagesize; ++#endif ++ return min; + ++} + int jffs2_scan_medium(struct jffs2_sb_info *c) + { + int i, ret; +- __u32 empty_blocks = 0; +- +- if (!c->blocks) { +- printk(KERN_WARNING "EEEK! c->blocks is NULL!\n"); +- return -EINVAL; ++ uint32_t empty_blocks = 0, bad_blocks = 0; ++ unsigned char *flashbuf = NULL; ++ uint32_t buf_size = 0; ++#ifndef __ECOS ++ size_t pointlen; ++ ++ if (c->mtd->point) { ++ ret = c->mtd->point (c->mtd, 0, c->mtd->size, &pointlen, &flashbuf); ++ if (!ret && pointlen < c->mtd->size) { ++ /* Don't muck about if it won't let us point to the whole flash */ ++ D1(printk(KERN_DEBUG "MTD point returned len too short: 0x%zx\n", pointlen)); ++ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); ++ flashbuf = NULL; ++ } ++ if (ret) ++ D1(printk(KERN_DEBUG "MTD point failed %d\n", ret)); ++ } ++#endif ++ if (!flashbuf) { ++ /* For NAND it's quicker to read a whole eraseblock at a time, ++ apparently */ ++ if (jffs2_cleanmarker_oob(c)) ++ buf_size = c->sector_size; ++ else ++ buf_size = PAGE_SIZE; ++ ++ D1(printk(KERN_DEBUG "Allocating readbuf of %d bytes\n", buf_size)); ++ flashbuf = kmalloc(buf_size, GFP_KERNEL); ++ if (!flashbuf) ++ return -ENOMEM; + } ++ + for (i=0; i<c->nr_blocks; i++) { + struct jffs2_eraseblock *jeb = &c->blocks[i]; + +- ret = jffs2_scan_eraseblock(c, jeb); ++ ret = jffs2_scan_eraseblock(c, jeb, buf_size?flashbuf:(flashbuf+jeb->offset), buf_size); ++ + if (ret < 0) +- return ret; ++ goto out; + + ACCT_PARANOIA_CHECK(jeb); + + /* Now decide which list to put it on */ +- if (ret == 1) { ++ switch(ret) { ++ case BLK_STATE_ALLFF: + /* + * Empty block. Since we can't be sure it + * was entirely erased, we just queue it for erase +@@ -103,10 +130,12 @@ + * is complete. Meanwhile we still count it as empty + * for later checks. + */ +- list_add(&jeb->list, &c->erase_pending_list); + empty_blocks++; ++ list_add(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; +- } else if (jeb->used_size == PAD(sizeof(struct jffs2_unknown_node)) && !jeb->first_node->next_in_ino) { ++ break; ++ ++ case BLK_STATE_CLEANMARKER: + /* Only a CLEANMARKER node is valid */ + if (!jeb->dirty_size) { + /* It's actually free */ +@@ -118,74 +147,227 @@ + list_add(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; + } +- } else if (jeb->used_size > c->sector_size - (2*sizeof(struct jffs2_raw_inode))) { ++ break; ++ ++ case BLK_STATE_CLEAN: + /* Full (or almost full) of clean data. Clean list */ + list_add(&jeb->list, &c->clean_list); +- } else if (jeb->used_size) { ++ break; ++ ++ case BLK_STATE_PARTDIRTY: + /* Some data, but not full. Dirty list. */ + /* Except that we want to remember the block with most free space, + and stick it in the 'nextblock' position to start writing to it. + Later when we do snapshots, this must be the most recent block, + not the one with most free space. + */ +- if (jeb->free_size > 2*sizeof(struct jffs2_raw_inode) && ++ if (jeb->free_size > min_free(c) && + (!c->nextblock || c->nextblock->free_size < jeb->free_size)) { + /* Better candidate for the next writes to go to */ +- if (c->nextblock) ++ if (c->nextblock) { ++ c->nextblock->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; ++ c->dirty_size += c->nextblock->free_size + c->nextblock->wasted_size; ++ c->free_size -= c->nextblock->free_size; ++ c->wasted_size -= c->nextblock->wasted_size; ++ c->nextblock->free_size = c->nextblock->wasted_size = 0; ++ if (VERYDIRTY(c, c->nextblock->dirty_size)) { ++ list_add(&c->nextblock->list, &c->very_dirty_list); ++ } else { + list_add(&c->nextblock->list, &c->dirty_list); ++ } ++ } + c->nextblock = jeb; + } else { ++ jeb->dirty_size += jeb->free_size + jeb->wasted_size; ++ c->dirty_size += jeb->free_size + jeb->wasted_size; ++ c->free_size -= jeb->free_size; ++ c->wasted_size -= jeb->wasted_size; ++ jeb->free_size = jeb->wasted_size = 0; ++ if (VERYDIRTY(c, jeb->dirty_size)) { ++ list_add(&jeb->list, &c->very_dirty_list); ++ } else { + list_add(&jeb->list, &c->dirty_list); + } +- } else { ++ } ++ break; ++ ++ case BLK_STATE_ALLDIRTY: + /* Nothing valid - not even a clean marker. Needs erasing. */ + /* For now we just put it on the erasing list. We'll start the erases later */ +- printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset); ++ D1(printk(KERN_NOTICE "JFFS2: Erase block at 0x%08x is not formatted. It will be erased\n", jeb->offset)); + list_add(&jeb->list, &c->erase_pending_list); + c->nr_erasing_blocks++; ++ break; ++ ++ case BLK_STATE_BADBLOCK: ++ D1(printk(KERN_NOTICE "JFFS2: Block at 0x%08x is bad\n", jeb->offset)); ++ list_add(&jeb->list, &c->bad_list); ++ c->bad_size += c->sector_size; ++ c->free_size -= c->sector_size; ++ bad_blocks++; ++ break; ++ default: ++ printk(KERN_WARNING "jffs2_scan_medium(): unknown block state\n"); ++ BUG(); + } + } +- /* Rotate the lists by some number to ensure wear levelling */ +- jffs2_rotate_lists(c); + ++ /* Nextblock dirty is always seen as wasted, because we cannot recycle it now */ ++ if (c->nextblock && (c->nextblock->dirty_size)) { ++ c->nextblock->wasted_size += c->nextblock->dirty_size; ++ c->wasted_size += c->nextblock->dirty_size; ++ c->dirty_size -= c->nextblock->dirty_size; ++ c->nextblock->dirty_size = 0; ++ } ++#ifdef CONFIG_JFFS2_FS_NAND ++ if (!jffs2_can_mark_obsolete(c) && c->nextblock && (c->nextblock->free_size & (c->wbuf_pagesize-1))) { ++ /* If we're going to start writing into a block which already ++ contains data, and the end of the data isn't page-aligned, ++ skip a little and align it. */ ++ ++ uint32_t skip = c->nextblock->free_size & (c->wbuf_pagesize-1); ++ ++ D1(printk(KERN_DEBUG "jffs2_scan_medium(): Skipping %d bytes in nextblock to ensure page alignment\n", ++ skip)); ++ c->nextblock->wasted_size += skip; ++ c->wasted_size += skip; ++ ++ c->nextblock->free_size -= skip; ++ c->free_size -= skip; ++ } ++#endif + if (c->nr_erasing_blocks) { +- if (!c->used_size && empty_blocks != c->nr_blocks) { ++ if ( !c->used_size && ((empty_blocks+bad_blocks)!= c->nr_blocks || bad_blocks == c->nr_blocks) ) { + printk(KERN_NOTICE "Cowardly refusing to erase blocks on filesystem with no valid JFFS2 nodes\n"); +- return -EIO; ++ printk(KERN_NOTICE "empty_blocks %d, bad_blocks %d, c->nr_blocks %d\n",empty_blocks,bad_blocks,c->nr_blocks); ++ ret = -EIO; ++ goto out; + } + jffs2_erase_pending_trigger(c); + } ++ ret = 0; ++ out: ++ if (buf_size) ++ kfree(flashbuf); ++#ifndef __ECOS ++ else ++ c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); ++#endif ++ return ret; ++} ++ ++static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf, ++ uint32_t ofs, uint32_t len) ++{ ++ int ret; ++ size_t retlen; ++ ++ ret = jffs2_flash_read(c, ofs, len, &retlen, buf); ++ if (ret) { ++ D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", len, ofs, ret)); ++ return ret; ++ } ++ if (retlen < len) { ++ D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%zx bytes\n", ofs, retlen)); ++ return -EIO; ++ } ++ D2(printk(KERN_DEBUG "Read 0x%x bytes from 0x%08x into buf\n", len, ofs)); ++ D2(printk(KERN_DEBUG "000: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", ++ buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15])); + return 0; + } + +-static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { +- struct jffs2_unknown_node node; +- __u32 ofs, prevofs; +- __u32 hdr_crc, nodetype; ++static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ unsigned char *buf, uint32_t buf_size) { ++ struct jffs2_unknown_node *node; ++ struct jffs2_unknown_node crcnode; ++ uint32_t ofs, prevofs; ++ uint32_t hdr_crc, buf_ofs, buf_len; + int err; + int noise = 0; ++ int wasempty = 0; ++ uint32_t empty_start = 0; ++#ifdef CONFIG_JFFS2_FS_NAND ++ int cleanmarkerfound = 0; ++#endif + + ofs = jeb->offset; + prevofs = jeb->offset - 1; + + D1(printk(KERN_DEBUG "jffs2_scan_eraseblock(): Scanning block at 0x%x\n", ofs)); + +- err = jffs2_scan_empty(c, jeb, &ofs, &noise); +- if (err) return err; +- if (ofs == jeb->offset + c->sector_size) { ++#ifdef CONFIG_JFFS2_FS_NAND ++ if (jffs2_cleanmarker_oob(c)) { ++ int ret = jffs2_check_nand_cleanmarker(c, jeb); ++ D2(printk(KERN_NOTICE "jffs_check_nand_cleanmarker returned %d\n",ret)); ++ /* Even if it's not found, we still scan to see ++ if the block is empty. We use this information ++ to decide whether to erase it or not. */ ++ switch (ret) { ++ case 0: cleanmarkerfound = 1; break; ++ case 1: break; ++ case 2: return BLK_STATE_BADBLOCK; ++ case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ ++ default: return ret; ++ } ++ } ++#endif ++ buf_ofs = jeb->offset; ++ ++ if (!buf_size) { ++ buf_len = c->sector_size; ++ } else { ++ buf_len = EMPTY_SCAN_SIZE; ++ err = jffs2_fill_scan_buf(c, buf, buf_ofs, buf_len); ++ if (err) ++ return err; ++ } ++ ++ /* We temporarily use 'ofs' as a pointer into the buffer/jeb */ ++ ofs = 0; ++ ++ /* Scan only 4KiB of 0xFF before declaring it's empty */ ++ while(ofs < EMPTY_SCAN_SIZE && *(uint32_t *)(&buf[ofs]) == 0xFFFFFFFF) ++ ofs += 4; ++ ++ if (ofs == EMPTY_SCAN_SIZE) { ++#ifdef CONFIG_JFFS2_FS_NAND ++ if (jffs2_cleanmarker_oob(c)) { ++ /* scan oob, take care of cleanmarker */ ++ int ret = jffs2_check_oob_empty(c, jeb, cleanmarkerfound); ++ D2(printk(KERN_NOTICE "jffs2_check_oob_empty returned %d\n",ret)); ++ switch (ret) { ++ case 0: return cleanmarkerfound ? BLK_STATE_CLEANMARKER : BLK_STATE_ALLFF; ++ case 1: return BLK_STATE_ALLDIRTY; ++ case 2: return BLK_STATE_BADBLOCK; /* case 2/3 are paranoia checks */ ++ case 3: return BLK_STATE_ALLDIRTY; /* Block has failed to erase min. once */ ++ default: return ret; ++ } ++ } ++#endif + D1(printk(KERN_DEBUG "Block at 0x%08x is empty (erased)\n", jeb->offset)); +- return 1; /* special return code */ ++ return BLK_STATE_ALLFF; /* OK to erase if all blocks are like this */ ++ } ++ if (ofs) { ++ D1(printk(KERN_DEBUG "Free space at %08x ends at %08x\n", jeb->offset, ++ jeb->offset + ofs)); ++ DIRTY_SPACE(ofs); + } + ++ /* Now ofs is a complete physical flash offset as it always was... */ ++ ofs += jeb->offset; ++ + noise = 10; + + while(ofs < jeb->offset + c->sector_size) { +- ssize_t retlen; +- ACCT_PARANOIA_CHECK(jeb); ++ ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ cond_resched(); + + if (ofs & 3) { + printk(KERN_WARNING "Eep. ofs 0x%08x not word-aligned!\n", ofs); +- ofs = (ofs+3)&~3; ++ ofs = PAD(ofs); + continue; + } + if (ofs == prevofs) { +@@ -196,102 +378,173 @@ + } + prevofs = ofs; + +- if (jeb->offset + c->sector_size < ofs + sizeof(node)) { +- D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. Not reading\n", sizeof(struct jffs2_unknown_node))); ++ if (jeb->offset + c->sector_size < ofs + sizeof(*node)) { ++ D1(printk(KERN_DEBUG "Fewer than %zd bytes left to end of block. (%x+%x<%x+%zx) Not reading\n", sizeof(struct jffs2_unknown_node), ++ jeb->offset, c->sector_size, ofs, sizeof(*node))); + DIRTY_SPACE((jeb->offset + c->sector_size)-ofs); + break; + } + +- err = c->mtd->read(c->mtd, ofs, sizeof(node), &retlen, (char *)&node); +- +- if (err) { +- D1(printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%x) returned %d\n", sizeof(node), ofs, err)); ++ if (buf_ofs + buf_len < ofs + sizeof(*node)) { ++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); ++ D1(printk(KERN_DEBUG "Fewer than %zd bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n", ++ sizeof(struct jffs2_unknown_node), buf_len, ofs)); ++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); ++ if (err) + return err; ++ buf_ofs = ofs; + } +- if (retlen < sizeof(node)) { +- D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen)); +- DIRTY_SPACE(retlen); +- ofs += retlen; +- continue; ++ ++ node = (struct jffs2_unknown_node *)&buf[ofs-buf_ofs]; ++ ++ if (*(uint32_t *)(&buf[ofs-buf_ofs]) == 0xffffffff) { ++ uint32_t inbuf_ofs = ofs - buf_ofs + 4; ++ uint32_t scanend; ++ ++ empty_start = ofs; ++ ofs += 4; ++ ++ /* If scanning empty space after only a cleanmarker, don't ++ bother scanning the whole block */ ++ if (unlikely(empty_start == jeb->offset + c->cleanmarker_size && ++ jeb->offset + EMPTY_SCAN_SIZE < buf_ofs + buf_len)) ++ scanend = jeb->offset + EMPTY_SCAN_SIZE - buf_ofs; ++ else ++ scanend = buf_len; ++ ++ D1(printk(KERN_DEBUG "Found empty flash at 0x%08x\n", ofs)); ++ while (inbuf_ofs < scanend) { ++ if (*(uint32_t *)(&buf[inbuf_ofs]) != 0xffffffff) ++ goto emptyends; ++ ++ inbuf_ofs+=4; ++ ofs += 4; + } ++ /* Ran off end. */ ++ D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs)); + +- if (node.magic == JFFS2_EMPTY_BITMASK && node.nodetype == JFFS2_EMPTY_BITMASK) { +- D1(printk(KERN_DEBUG "Found empty flash at 0x%x\n", ofs)); +- err = jffs2_scan_empty(c, jeb, &ofs, &noise); +- if (err) return err; ++ if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && ++ c->cleanmarker_size && !jeb->first_node->next_in_ino && !jeb->dirty_size) ++ return BLK_STATE_CLEANMARKER; ++ wasempty = 1; ++ continue; ++ } else if (wasempty) { ++ emptyends: ++ printk(KERN_WARNING "Empty flash at 0x%08x ends at 0x%08x\n", empty_start, ofs); ++ DIRTY_SPACE(ofs-empty_start); ++ wasempty = 0; + continue; + } + +- if (ofs == jeb->offset && node.magic == KSAMTIB_CIGAM_2SFFJ) { ++ if (ofs == jeb->offset && je16_to_cpu(node->magic) == KSAMTIB_CIGAM_2SFFJ) { + printk(KERN_WARNING "Magic bitmask is backwards at offset 0x%08x. Wrong endian filesystem?\n", ofs); + DIRTY_SPACE(4); + ofs += 4; + continue; + } +- if (node.magic == JFFS2_DIRTY_BITMASK) { +- D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs)); ++ if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) { ++ D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs)); + DIRTY_SPACE(4); + ofs += 4; + continue; + } +- if (node.magic == JFFS2_OLD_MAGIC_BITMASK) { ++ if (je16_to_cpu(node->magic) == JFFS2_OLD_MAGIC_BITMASK) { + printk(KERN_WARNING "Old JFFS2 bitmask found at 0x%08x\n", ofs); + printk(KERN_WARNING "You cannot use older JFFS2 filesystems with newer kernels\n"); + DIRTY_SPACE(4); + ofs += 4; + continue; + } +- if (node.magic != JFFS2_MAGIC_BITMASK) { ++ if (je16_to_cpu(node->magic) != JFFS2_MAGIC_BITMASK) { + /* OK. We're out of possibilities. Whinge and move on */ +- noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", JFFS2_MAGIC_BITMASK, ofs, node.magic); ++ noisy_printk(&noise, "jffs2_scan_eraseblock(): Magic bitmask 0x%04x not found at 0x%08x: 0x%04x instead\n", ++ JFFS2_MAGIC_BITMASK, ofs, ++ je16_to_cpu(node->magic)); + DIRTY_SPACE(4); + ofs += 4; + continue; + } + /* We seem to have a node of sorts. Check the CRC */ +- nodetype = node.nodetype; +- node.nodetype |= JFFS2_NODE_ACCURATE; +- hdr_crc = crc32(0, &node, sizeof(node)-4); +- node.nodetype = nodetype; +- if (hdr_crc != node.hdr_crc) { ++ crcnode.magic = node->magic; ++ crcnode.nodetype = cpu_to_je16( je16_to_cpu(node->nodetype) | JFFS2_NODE_ACCURATE); ++ crcnode.totlen = node->totlen; ++ hdr_crc = crc32(0, &crcnode, sizeof(crcnode)-4); ++ ++ if (hdr_crc != je32_to_cpu(node->hdr_crc)) { + noisy_printk(&noise, "jffs2_scan_eraseblock(): Node at 0x%08x {0x%04x, 0x%04x, 0x%08x) has invalid CRC 0x%08x (calculated 0x%08x)\n", +- ofs, node.magic, node.nodetype, node.totlen, node.hdr_crc, hdr_crc); ++ ofs, je16_to_cpu(node->magic), ++ je16_to_cpu(node->nodetype), ++ je32_to_cpu(node->totlen), ++ je32_to_cpu(node->hdr_crc), ++ hdr_crc); + DIRTY_SPACE(4); + ofs += 4; + continue; + } + +- if (ofs + node.totlen > jeb->offset + c->sector_size) { ++ if (ofs + je32_to_cpu(node->totlen) > ++ jeb->offset + c->sector_size) { + /* Eep. Node goes over the end of the erase block. */ + printk(KERN_WARNING "Node at 0x%08x with length 0x%08x would run over the end of the erase block\n", +- ofs, node.totlen); ++ ofs, je32_to_cpu(node->totlen)); + printk(KERN_WARNING "Perhaps the file system was created with the wrong erase size?\n"); + DIRTY_SPACE(4); + ofs += 4; + continue; + } + +- switch(node.nodetype | JFFS2_NODE_ACCURATE) { ++ if (!(je16_to_cpu(node->nodetype) & JFFS2_NODE_ACCURATE)) { ++ /* Wheee. This is an obsoleted node */ ++ D2(printk(KERN_DEBUG "Node at 0x%08x is obsolete. Skipping\n", ofs)); ++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); ++ continue; ++ } ++ ++ switch(je16_to_cpu(node->nodetype)) { + case JFFS2_NODETYPE_INODE: +- err = jffs2_scan_inode_node(c, jeb, &ofs); ++ if (buf_ofs + buf_len < ofs + sizeof(struct jffs2_raw_inode)) { ++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); ++ D1(printk(KERN_DEBUG "Fewer than %zd bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n", ++ sizeof(struct jffs2_raw_inode), buf_len, ofs)); ++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); ++ if (err) ++ return err; ++ buf_ofs = ofs; ++ node = (void *)buf; ++ } ++ err = jffs2_scan_inode_node(c, jeb, (void *)node, ofs); + if (err) return err; ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + + case JFFS2_NODETYPE_DIRENT: +- err = jffs2_scan_dirent_node(c, jeb, &ofs); ++ if (buf_ofs + buf_len < ofs + je32_to_cpu(node->totlen)) { ++ buf_len = min_t(uint32_t, buf_size, jeb->offset + c->sector_size - ofs); ++ D1(printk(KERN_DEBUG "Fewer than %d bytes (dirent node) left to end of buf. Reading 0x%x at 0x%08x\n", ++ je32_to_cpu(node->totlen), buf_len, ofs)); ++ err = jffs2_fill_scan_buf(c, buf, ofs, buf_len); ++ if (err) ++ return err; ++ buf_ofs = ofs; ++ node = (void *)buf; ++ } ++ err = jffs2_scan_dirent_node(c, jeb, (void *)node, ofs); + if (err) return err; ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + + case JFFS2_NODETYPE_CLEANMARKER: +- if (node.totlen != sizeof(struct jffs2_unknown_node)) { ++ D1(printk(KERN_DEBUG "CLEANMARKER node found at 0x%08x\n", ofs)); ++ if (je32_to_cpu(node->totlen) != c->cleanmarker_size) { + printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x has totlen 0x%x != normal 0x%x\n", +- ofs, node.totlen, sizeof(struct jffs2_unknown_node)); ++ ofs, je32_to_cpu(node->totlen), c->cleanmarker_size); + DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ++ ofs += PAD(sizeof(struct jffs2_unknown_node)); + } else if (jeb->first_node) { + printk(KERN_NOTICE "CLEANMARKER node found at 0x%08x, not first node in block (0x%08x)\n", ofs, jeb->offset); + DIRTY_SPACE(PAD(sizeof(struct jffs2_unknown_node))); + ofs += PAD(sizeof(struct jffs2_unknown_node)); +- continue; + } else { + struct jffs2_raw_node_ref *marker_ref = jffs2_alloc_raw_node_ref(); + if (!marker_ref) { +@@ -300,98 +553,80 @@ + } + marker_ref->next_in_ino = NULL; + marker_ref->next_phys = NULL; +- marker_ref->flash_offset = ofs; +- marker_ref->totlen = sizeof(struct jffs2_unknown_node); ++ marker_ref->flash_offset = ofs | REF_NORMAL; ++ marker_ref->__totlen = c->cleanmarker_size; + jeb->first_node = jeb->last_node = marker_ref; + +- USED_SPACE(PAD(sizeof(struct jffs2_unknown_node))); ++ USED_SPACE(PAD(c->cleanmarker_size)); ++ ofs += PAD(c->cleanmarker_size); + } +- ofs += PAD(sizeof(struct jffs2_unknown_node)); ++ break; ++ ++ case JFFS2_NODETYPE_PADDING: ++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + + default: +- switch (node.nodetype & JFFS2_COMPAT_MASK) { ++ switch (je16_to_cpu(node->nodetype) & JFFS2_COMPAT_MASK) { + case JFFS2_FEATURE_ROCOMPAT: +- printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); ++ printk(KERN_NOTICE "Read-only compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); + c->flags |= JFFS2_SB_FLAG_RO; +- if (!(OFNI_BS_2SFFJ(c)->s_flags & MS_RDONLY)) ++ if (!(jffs2_is_readonly(c))) + return -EROFS; +- DIRTY_SPACE(PAD(node.totlen)); +- ofs += PAD(node.totlen); +- continue; ++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); ++ break; + + case JFFS2_FEATURE_INCOMPAT: +- printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); ++ printk(KERN_NOTICE "Incompatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs); + return -EINVAL; + + case JFFS2_FEATURE_RWCOMPAT_DELETE: +- printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); +- DIRTY_SPACE(PAD(node.totlen)); +- ofs += PAD(node.totlen); ++ D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); ++ DIRTY_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + + case JFFS2_FEATURE_RWCOMPAT_COPY: +- printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", node.nodetype, ofs); +- USED_SPACE(PAD(node.totlen)); +- ofs += PAD(node.totlen); ++ D1(printk(KERN_NOTICE "Unknown but compatible feature node (0x%04x) found at offset 0x%08x\n", je16_to_cpu(node->nodetype), ofs)); ++ USED_SPACE(PAD(je32_to_cpu(node->totlen))); ++ ofs += PAD(je32_to_cpu(node->totlen)); + break; + } + } + } +- D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, used 0x%08x\n", jeb->offset, +- jeb->free_size, jeb->dirty_size, jeb->used_size)); +- return 0; +-} + +-/* We're pointing at the first empty word on the flash. Scan and account for the whole dirty region */ +-static int jffs2_scan_empty(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *startofs, int *noise) +-{ +- __u32 *buf; +- __u32 scanlen = (jeb->offset + c->sector_size) - *startofs; +- __u32 curofs = *startofs; +- +- buf = kmalloc(min((__u32)PAGE_SIZE, scanlen), GFP_KERNEL); +- if (!buf) { +- printk(KERN_WARNING "Scan buffer allocation failed\n"); +- return -ENOMEM; +- } +- while(scanlen) { +- ssize_t retlen; +- int ret, i; +- +- ret = c->mtd->read(c->mtd, curofs, min((__u32)PAGE_SIZE, scanlen), &retlen, (char *)buf); +- if(ret) { +- D1(printk(KERN_WARNING "jffs2_scan_empty(): Read 0x%x bytes at 0x%08x returned %d\n", min((__u32)PAGE_SIZE, scanlen), curofs, ret)); +- kfree(buf); +- return ret; +- } +- if (retlen < 4) { +- D1(printk(KERN_WARNING "Eep. too few bytes read in scan_empty()\n")); +- kfree(buf); +- return -EIO; +- } +- for (i=0; i<(retlen / 4); i++) { +- if (buf[i] != 0xffffffff) { +- curofs += i*4; +- +- noisy_printk(noise, "jffs2_scan_empty(): Empty block at 0x%08x ends at 0x%08x (with 0x%08x)! Marking dirty\n", *startofs, curofs, buf[i]); +- DIRTY_SPACE(curofs - (*startofs)); +- *startofs = curofs; +- kfree(buf); +- return 0; +- } +- } +- scanlen -= retlen&~3; +- curofs += retlen&~3; +- } + +- D1(printk(KERN_DEBUG "Empty flash detected from 0x%08x to 0x%08x\n", *startofs, curofs)); +- kfree(buf); +- *startofs = curofs; +- return 0; ++ D1(printk(KERN_DEBUG "Block at 0x%08x: free 0x%08x, dirty 0x%08x, unchecked 0x%08x, used 0x%08x\n", jeb->offset, ++ jeb->free_size, jeb->dirty_size, jeb->unchecked_size, jeb->used_size)); ++ ++ /* mark_node_obsolete can add to wasted !! */ ++ if (jeb->wasted_size) { ++ jeb->dirty_size += jeb->wasted_size; ++ c->dirty_size += jeb->wasted_size; ++ c->wasted_size -= jeb->wasted_size; ++ jeb->wasted_size = 0; ++ } ++ ++ if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && !jeb->dirty_size ++ && (!jeb->first_node || jeb->first_node->next_in_ino) ) ++ return BLK_STATE_CLEANMARKER; ++ ++ /* move blocks with max 4 byte dirty space to cleanlist */ ++ else if (!ISDIRTY(c->sector_size - (jeb->used_size + jeb->unchecked_size))) { ++ c->dirty_size -= jeb->dirty_size; ++ c->wasted_size += jeb->dirty_size; ++ jeb->wasted_size += jeb->dirty_size; ++ jeb->dirty_size = 0; ++ return BLK_STATE_CLEAN; ++ } else if (jeb->used_size || jeb->unchecked_size) ++ return BLK_STATE_PARTDIRTY; ++ else ++ return BLK_STATE_ALLDIRTY; + } + +-static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, __u32 ino) ++static struct jffs2_inode_cache *jffs2_scan_make_ino_cache(struct jffs2_sb_info *c, uint32_t ino) + { + struct jffs2_inode_cache *ic; + +@@ -399,137 +634,77 @@ + if (ic) + return ic; + ++ if (ino > c->highest_ino) ++ c->highest_ino = ino; ++ + ic = jffs2_alloc_inode_cache(); + if (!ic) { + printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of inode cache failed\n"); + return NULL; + } + memset(ic, 0, sizeof(*ic)); +- ic->scan = kmalloc(sizeof(struct jffs2_scan_info), GFP_KERNEL); +- if (!ic->scan) { +- printk(KERN_NOTICE "jffs2_scan_make_inode_cache(): allocation of scan info for inode cache failed\n"); +- jffs2_free_inode_cache(ic); +- return NULL; +- } +- memset(ic->scan, 0, sizeof(*ic->scan)); ++ + ic->ino = ino; + ic->nodes = (void *)ic; + jffs2_add_ino_cache(c, ic); + if (ino == 1) +- ic->nlink=1; ++ ic->nlink = 1; + return ic; + } + +-static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs) ++static int jffs2_scan_inode_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_inode *ri, uint32_t ofs) + { + struct jffs2_raw_node_ref *raw; +- struct jffs2_full_dnode *fn; +- struct jffs2_tmp_dnode_info *tn, **tn_list; + struct jffs2_inode_cache *ic; +- struct jffs2_raw_inode ri; +- __u32 crc; +- __u16 oldnodetype; +- int ret; +- ssize_t retlen; +- +- D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", *ofs)); +- +- ret = c->mtd->read(c->mtd, *ofs, sizeof(ri), &retlen, (char *)&ri); +- if (ret) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs, ret); +- return ret; +- } +- if (retlen != sizeof(ri)) { +- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", +- retlen, *ofs, sizeof(ri)); +- return -EIO; +- } ++ uint32_t ino = je32_to_cpu(ri->ino); + +- /* We sort of assume that the node was accurate when it was +- first written to the medium :) */ +- oldnodetype = ri.nodetype; +- ri.nodetype |= JFFS2_NODE_ACCURATE; +- crc = crc32(0, &ri, sizeof(ri)-8); +- ri.nodetype = oldnodetype; ++ D1(printk(KERN_DEBUG "jffs2_scan_inode_node(): Node at 0x%08x\n", ofs)); + +- if(crc != ri.node_crc) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", +- *ofs, ri.node_crc, crc); +- /* FIXME: Why do we believe totlen? */ +- DIRTY_SPACE(4); +- *ofs += 4; +- return 0; +- } +- /* There was a bug where we wrote hole nodes out with csize/dsize +- swapped. Deal with it */ +- if (ri.compr == JFFS2_COMPR_ZERO && !ri.dsize && ri.csize) { +- ri.dsize = ri.csize; +- ri.csize = 0; +- } ++ /* We do very little here now. Just check the ino# to which we should attribute ++ this node; we can do all the CRC checking etc. later. There's a tradeoff here -- ++ we used to scan the flash once only, reading everything we want from it into ++ memory, then building all our in-core data structures and freeing the extra ++ information. Now we allow the first part of the mount to complete a lot quicker, ++ but we have to go _back_ to the flash in order to finish the CRC checking, etc. ++ Which means that the _full_ amount of time to get to proper write mode with GC ++ operational may actually be _longer_ than before. Sucks to be me. */ + +- if (ri.csize) { +- /* Check data CRC too */ +- unsigned char *dbuf; +- __u32 crc; +- +- dbuf = kmalloc(PAGE_CACHE_SIZE, GFP_KERNEL); +- if (!dbuf) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of temporary data buffer for CRC check failed\n"); +- return -ENOMEM; +- } +- ret = c->mtd->read(c->mtd, *ofs+sizeof(ri), ri.csize, &retlen, dbuf); +- if (ret) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): Read error at 0x%08x: %d\n", *ofs+sizeof(ri), ret); +- kfree(dbuf); +- return ret; +- } +- if (retlen != ri.csize) { +- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", +- retlen, *ofs+ sizeof(ri), ri.csize); +- kfree(dbuf); +- return -EIO; +- } +- crc = crc32(0, dbuf, ri.csize); +- kfree(dbuf); +- if (crc != ri.data_crc) { +- printk(KERN_NOTICE "jffs2_scan_inode_node(): Data CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", +- *ofs, ri.data_crc, crc); +- DIRTY_SPACE(PAD(ri.totlen)); +- *ofs += PAD(ri.totlen); +- return 0; +- } +- } +- +- /* Wheee. It worked */ + raw = jffs2_alloc_raw_node_ref(); + if (!raw) { + printk(KERN_NOTICE "jffs2_scan_inode_node(): allocation of node reference failed\n"); + return -ENOMEM; + } +- tn = jffs2_alloc_tmp_dnode_info(); +- if (!tn) { +- jffs2_free_raw_node_ref(raw); +- return -ENOMEM; +- } +- fn = jffs2_alloc_full_dnode(); +- if (!fn) { +- jffs2_free_tmp_dnode_info(tn); ++ ++ ic = jffs2_get_ino_cache(c, ino); ++ if (!ic) { ++ /* Inocache get failed. Either we read a bogus ino# or it's just genuinely the ++ first node we found for this inode. Do a CRC check to protect against the former ++ case */ ++ uint32_t crc = crc32(0, ri, sizeof(*ri)-8); ++ ++ if (crc != je32_to_cpu(ri->node_crc)) { ++ printk(KERN_NOTICE "jffs2_scan_inode_node(): CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", ++ ofs, je32_to_cpu(ri->node_crc), crc); ++ /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ ++ DIRTY_SPACE(PAD(je32_to_cpu(ri->totlen))); + jffs2_free_raw_node_ref(raw); +- return -ENOMEM; ++ return 0; + } +- ic = jffs2_scan_make_ino_cache(c, ri.ino); ++ ic = jffs2_scan_make_ino_cache(c, ino); + if (!ic) { +- jffs2_free_full_dnode(fn); +- jffs2_free_tmp_dnode_info(tn); + jffs2_free_raw_node_ref(raw); + return -ENOMEM; + } ++ } + +- /* Build the data structures and file them for later */ +- raw->flash_offset = *ofs; +- raw->totlen = PAD(ri.totlen); ++ /* Wheee. It worked */ ++ ++ raw->flash_offset = ofs | REF_UNCHECKED; ++ raw->__totlen = PAD(je32_to_cpu(ri->totlen)); + raw->next_phys = NULL; + raw->next_in_ino = ic->nodes; ++ + ic->nodes = raw; + if (!jeb->first_node) + jeb->first_node = raw; +@@ -538,134 +713,56 @@ + jeb->last_node = raw; + + D1(printk(KERN_DEBUG "Node is ino #%u, version %d. Range 0x%x-0x%x\n", +- ri.ino, ri.version, ri.offset, ri.offset+ri.dsize)); +- +- pseudo_random += ri.version; +- +- for (tn_list = &ic->scan->tmpnodes; *tn_list; tn_list = &((*tn_list)->next)) { +- if ((*tn_list)->version < ri.version) +- continue; +- if ((*tn_list)->version > ri.version) +- break; +- /* Wheee. We've found another instance of the same version number. +- We should obsolete one of them. +- */ +- D1(printk(KERN_DEBUG "Duplicate version %d found in ino #%u. Previous one is at 0x%08x\n", ri.version, ic->ino, (*tn_list)->fn->raw->flash_offset &~3)); +- if (!jeb->used_size) { +- D1(printk(KERN_DEBUG "No valid nodes yet found in this eraseblock 0x%08x, so obsoleting the new instance at 0x%08x\n", +- jeb->offset, raw->flash_offset & ~3)); +- ri.nodetype &= ~JFFS2_NODE_ACCURATE; +- /* Perhaps we could also mark it as such on the medium. Maybe later */ +- } +- break; +- } +- +- if (ri.nodetype & JFFS2_NODE_ACCURATE) { +- memset(fn,0,sizeof(*fn)); +- +- fn->ofs = ri.offset; +- fn->size = ri.dsize; +- fn->frags = 0; +- fn->raw = raw; +- +- tn->next = NULL; +- tn->fn = fn; +- tn->version = ri.version; ++ je32_to_cpu(ri->ino), je32_to_cpu(ri->version), ++ je32_to_cpu(ri->offset), ++ je32_to_cpu(ri->offset)+je32_to_cpu(ri->dsize))); + +- USED_SPACE(PAD(ri.totlen)); +- jffs2_add_tn_to_list(tn, &ic->scan->tmpnodes); +- /* Make sure the one we just added is the _last_ in the list +- with this version number, so the older ones get obsoleted */ +- while (tn->next && tn->next->version == tn->version) { ++ pseudo_random += je32_to_cpu(ri->version); + +- D1(printk(KERN_DEBUG "Shifting new node at 0x%08x after other node at 0x%08x for version %d in list\n", +- fn->raw->flash_offset&~3, tn->next->fn->raw->flash_offset &~3, ri.version)); +- +- if(tn->fn != fn) +- BUG(); +- tn->fn = tn->next->fn; +- tn->next->fn = fn; +- tn = tn->next; +- } +- } else { +- jffs2_free_full_dnode(fn); +- jffs2_free_tmp_dnode_info(tn); +- raw->flash_offset |= 1; +- DIRTY_SPACE(PAD(ri.totlen)); +- } +- *ofs += PAD(ri.totlen); ++ UNCHECKED_SPACE(PAD(je32_to_cpu(ri->totlen))); + return 0; + } + +-static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, __u32 *ofs) ++static int jffs2_scan_dirent_node(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, ++ struct jffs2_raw_dirent *rd, uint32_t ofs) + { + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd; + struct jffs2_inode_cache *ic; +- struct jffs2_raw_dirent rd; +- __u16 oldnodetype; +- int ret; +- __u32 crc; +- ssize_t retlen; +- +- D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", *ofs)); ++ uint32_t crc; + +- ret = c->mtd->read(c->mtd, *ofs, sizeof(rd), &retlen, (char *)&rd); +- if (ret) { +- printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", *ofs, ret); +- return ret; +- } +- if (retlen != sizeof(rd)) { +- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", +- retlen, *ofs, sizeof(rd)); +- return -EIO; +- } ++ D1(printk(KERN_DEBUG "jffs2_scan_dirent_node(): Node at 0x%08x\n", ofs)); + +- /* We sort of assume that the node was accurate when it was +- first written to the medium :) */ +- oldnodetype = rd.nodetype; +- rd.nodetype |= JFFS2_NODE_ACCURATE; +- crc = crc32(0, &rd, sizeof(rd)-8); +- rd.nodetype = oldnodetype; ++ /* We don't get here unless the node is still valid, so we don't have to ++ mask in the ACCURATE bit any more. */ ++ crc = crc32(0, rd, sizeof(*rd)-8); + +- if (crc != rd.node_crc) { ++ if (crc != je32_to_cpu(rd->node_crc)) { + printk(KERN_NOTICE "jffs2_scan_dirent_node(): Node CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", +- *ofs, rd.node_crc, crc); +- /* FIXME: Why do we believe totlen? */ +- DIRTY_SPACE(4); +- *ofs += 4; ++ ofs, je32_to_cpu(rd->node_crc), crc); ++ /* We believe totlen because the CRC on the node _header_ was OK, just the node itself failed. */ ++ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); + return 0; + } + +- pseudo_random += rd.version; ++ pseudo_random += je32_to_cpu(rd->version); + +- fd = jffs2_alloc_full_dirent(rd.nsize+1); ++ fd = jffs2_alloc_full_dirent(rd->nsize+1); + if (!fd) { + return -ENOMEM; +-} +- ret = c->mtd->read(c->mtd, *ofs + sizeof(rd), rd.nsize, &retlen, &fd->name[0]); +- if (ret) { +- jffs2_free_full_dirent(fd); +- printk(KERN_NOTICE "jffs2_scan_dirent_node(): Read error at 0x%08x: %d\n", +- *ofs + sizeof(rd), ret); +- return ret; +- } +- if (retlen != rd.nsize) { +- jffs2_free_full_dirent(fd); +- printk(KERN_NOTICE "Short read: 0x%x bytes at 0x%08x instead of requested %x\n", +- retlen, *ofs + sizeof(rd), rd.nsize); +- return -EIO; + } +- crc = crc32(0, fd->name, rd.nsize); +- if (crc != rd.name_crc) { ++ memcpy(&fd->name, rd->name, rd->nsize); ++ fd->name[rd->nsize] = 0; ++ ++ crc = crc32(0, fd->name, rd->nsize); ++ if (crc != je32_to_cpu(rd->name_crc)) { + printk(KERN_NOTICE "jffs2_scan_dirent_node(): Name CRC failed on node at 0x%08x: Read 0x%08x, calculated 0x%08x\n", +- *ofs, rd.name_crc, crc); +- fd->name[rd.nsize]=0; +- D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, rd.ino)); ++ ofs, je32_to_cpu(rd->name_crc), crc); ++ D1(printk(KERN_NOTICE "Name for which CRC failed is (now) '%s', ino #%d\n", fd->name, je32_to_cpu(rd->ino))); + jffs2_free_full_dirent(fd); + /* FIXME: Why do we believe totlen? */ +- DIRTY_SPACE(PAD(rd.totlen)); +- *ofs += PAD(rd.totlen); ++ /* We believe totlen because the CRC on the node _header_ was OK, just the name failed. */ ++ DIRTY_SPACE(PAD(je32_to_cpu(rd->totlen))); + return 0; + } + raw = jffs2_alloc_raw_node_ref(); +@@ -674,15 +771,15 @@ + printk(KERN_NOTICE "jffs2_scan_dirent_node(): allocation of node reference failed\n"); + return -ENOMEM; + } +- ic = jffs2_scan_make_ino_cache(c, rd.pino); ++ ic = jffs2_scan_make_ino_cache(c, je32_to_cpu(rd->pino)); + if (!ic) { + jffs2_free_full_dirent(fd); + jffs2_free_raw_node_ref(raw); + return -ENOMEM; + } + +- raw->totlen = PAD(rd.totlen); +- raw->flash_offset = *ofs; ++ raw->__totlen = PAD(je32_to_cpu(rd->totlen)); ++ raw->flash_offset = ofs | REF_PRISTINE; + raw->next_phys = NULL; + raw->next_in_ino = ic->nodes; + ic->nodes = raw; +@@ -692,24 +789,15 @@ + jeb->last_node->next_phys = raw; + jeb->last_node = raw; + +- if (rd.nodetype & JFFS2_NODE_ACCURATE) { + fd->raw = raw; + fd->next = NULL; +- fd->version = rd.version; +- fd->ino = rd.ino; +- fd->name[rd.nsize]=0; +- fd->nhash = full_name_hash(fd->name, rd.nsize); +- fd->type = rd.type; +- +- USED_SPACE(PAD(rd.totlen)); +- jffs2_add_fd_to_list(c, fd, &ic->scan->dents); +- } else { +- raw->flash_offset |= 1; +- jffs2_free_full_dirent(fd); ++ fd->version = je32_to_cpu(rd->version); ++ fd->ino = je32_to_cpu(rd->ino); ++ fd->nhash = full_name_hash(fd->name, rd->nsize); ++ fd->type = rd->type; ++ USED_SPACE(PAD(je32_to_cpu(rd->totlen))); ++ jffs2_add_fd_to_list(c, fd, &ic->scan_dents); + +- DIRTY_SPACE(PAD(rd.totlen)); +- } +- *ofs += PAD(rd.totlen); + return 0; + } + +@@ -731,26 +819,90 @@ + struct list_head *n = head->next; + + list_del(head); +- while(count--) ++ while(count--) { + n = n->next; ++ } + list_add(head, n); + } + +-static void jffs2_rotate_lists(struct jffs2_sb_info *c) ++void jffs2_rotate_lists(struct jffs2_sb_info *c) + { + uint32_t x; ++ uint32_t rotateby; + + x = count_list(&c->clean_list); +- if (x) +- rotate_list((&c->clean_list), pseudo_random % x); ++ if (x) { ++ rotateby = pseudo_random % x; ++ D1(printk(KERN_DEBUG "Rotating clean_list by %d\n", rotateby)); ++ ++ rotate_list((&c->clean_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of clean_list is at %08x\n", ++ list_entry(c->clean_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty clean_list\n")); ++ } ++ ++ x = count_list(&c->very_dirty_list); ++ if (x) { ++ rotateby = pseudo_random % x; ++ D1(printk(KERN_DEBUG "Rotating very_dirty_list by %d\n", rotateby)); ++ ++ rotate_list((&c->very_dirty_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of very_dirty_list is at %08x\n", ++ list_entry(c->very_dirty_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty very_dirty_list\n")); ++ } + + x = count_list(&c->dirty_list); +- if (x) +- rotate_list((&c->dirty_list), pseudo_random % x); ++ if (x) { ++ rotateby = pseudo_random % x; ++ D1(printk(KERN_DEBUG "Rotating dirty_list by %d\n", rotateby)); ++ ++ rotate_list((&c->dirty_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of dirty_list is at %08x\n", ++ list_entry(c->dirty_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty dirty_list\n")); ++ } ++ ++ x = count_list(&c->erasable_list); ++ if (x) { ++ rotateby = pseudo_random % x; ++ D1(printk(KERN_DEBUG "Rotating erasable_list by %d\n", rotateby)); + +- if (c->nr_erasing_blocks) +- rotate_list((&c->erase_pending_list), pseudo_random % c->nr_erasing_blocks); ++ rotate_list((&c->erasable_list), rotateby); + +- if (c->nr_free_blocks) /* Not that it should ever be zero */ +- rotate_list((&c->free_list), pseudo_random % c->nr_free_blocks); ++ D1(printk(KERN_DEBUG "Erase block at front of erasable_list is at %08x\n", ++ list_entry(c->erasable_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty erasable_list\n")); ++ } ++ ++ if (c->nr_erasing_blocks) { ++ rotateby = pseudo_random % c->nr_erasing_blocks; ++ D1(printk(KERN_DEBUG "Rotating erase_pending_list by %d\n", rotateby)); ++ ++ rotate_list((&c->erase_pending_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of erase_pending_list is at %08x\n", ++ list_entry(c->erase_pending_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty erase_pending_list\n")); ++ } ++ ++ if (c->nr_free_blocks) { ++ rotateby = pseudo_random % c->nr_free_blocks; ++ D1(printk(KERN_DEBUG "Rotating free_list by %d\n", rotateby)); ++ ++ rotate_list((&c->free_list), rotateby); ++ ++ D1(printk(KERN_DEBUG "Erase block at front of free_list is at %08x\n", ++ list_entry(c->free_list.next, struct jffs2_eraseblock, list)->offset)); ++ } else { ++ D1(printk(KERN_DEBUG "Not rotating empty free_list\n")); ++ } + } +diff -Nurb linux-mips-2.4.27/fs/jffs2/super-v24.c linux/fs/jffs2/super-v24.c +--- linux-mips-2.4.27/fs/jffs2/super-v24.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/super-v24.c 2004-11-19 10:25:12.126165288 +0100 +@@ -0,0 +1,167 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001-2003 Red Hat, Inc. ++ * ++ * Created by David Woodhouse <dwmw2@redhat.com> ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: super-v24.c,v 1.75 2003/10/06 12:52:29 dwmw2 Exp $ ++ * ++ */ ++ ++#include <linux/config.h> ++#include <linux/kernel.h> ++#include <linux/module.h> ++#include <linux/version.h> ++#include <linux/slab.h> ++#include <linux/init.h> ++#include <linux/list.h> ++#include <linux/fs.h> ++#include <linux/jffs2.h> ++#include <linux/pagemap.h> ++#include <linux/mtd/mtd.h> ++#include "nodelist.h" ++ ++#ifndef MTD_BLOCK_MAJOR ++#define MTD_BLOCK_MAJOR 31 ++#endif ++ ++static void jffs2_put_super (struct super_block *); ++ ++static struct super_operations jffs2_super_operations = ++{ ++ .read_inode = jffs2_read_inode, ++ .put_super = jffs2_put_super, ++ .write_super = jffs2_write_super, ++ .statfs = jffs2_statfs, ++ .remount_fs = jffs2_remount_fs, ++ .clear_inode = jffs2_clear_inode, ++ .dirty_inode = jffs2_dirty_inode, ++}; ++ ++ ++static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent) ++{ ++ struct jffs2_sb_info *c; ++ int ret; ++ ++ D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev))); ++ ++ if (major(sb->s_dev) != MTD_BLOCK_MAJOR) { ++ if (!silent) ++ printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev)); ++ return NULL; ++ } ++ ++ c = JFFS2_SB_INFO(sb); ++ memset(c, 0, sizeof(*c)); ++ ++ sb->s_op = &jffs2_super_operations; ++ ++ c->mtd = get_mtd_device(NULL, minor(sb->s_dev)); ++ if (!c->mtd) { ++ D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", minor(sb->s_dev))); ++ return NULL; ++ } ++ ++ ret = jffs2_do_fill_super(sb, data, silent); ++ if (ret) { ++ put_mtd_device(c->mtd); ++ return NULL; ++ } ++ ++ return sb; ++} ++ ++static void jffs2_put_super (struct super_block *sb) ++{ ++ struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); ++ ++ D2(printk(KERN_DEBUG "jffs2: jffs2_put_super()\n")); ++ ++ ++ if (!(sb->s_flags & MS_RDONLY)) ++ jffs2_stop_garbage_collect_thread(c); ++ down(&c->alloc_sem); ++ jffs2_flush_wbuf_pad(c); ++ up(&c->alloc_sem); ++ jffs2_free_ino_caches(c); ++ jffs2_free_raw_node_refs(c); ++ kfree(c->blocks); ++ jffs2_nand_flash_cleanup(c); ++ kfree(c->inocache_list); ++ if (c->mtd->sync) ++ c->mtd->sync(c->mtd); ++ put_mtd_device(c->mtd); ++ ++ D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); ++} ++ ++static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super); ++ ++static int __init init_jffs2_fs(void) ++{ ++ int ret; ++ ++ printk(KERN_INFO "JFFS2 version 2.2." ++#ifdef CONFIG_FS_JFFS2_NAND ++ " (NAND)" ++#endif ++ " (C) 2001-2003 Red Hat, Inc.\n"); ++ ++#ifdef JFFS2_OUT_OF_KERNEL ++ /* sanity checks. Could we do these at compile time? */ ++ if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) { ++ printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", ++ sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u)); ++ return -EIO; ++ } ++ ++ if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) { ++ printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", ++ sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u)); ++ return -EIO; ++ } ++#endif ++ ret = jffs2_zlib_init(); ++ if (ret) { ++ printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n"); ++ goto out; ++ } ++ ret = jffs2_create_slab_caches(); ++ if (ret) { ++ printk(KERN_ERR "JFFS2 error: Failed to initialise slab caches\n"); ++ goto out_zlib; ++ } ++ ret = register_filesystem(&jffs2_fs_type); ++ if (ret) { ++ printk(KERN_ERR "JFFS2 error: Failed to register filesystem\n"); ++ goto out_slab; ++ } ++ return 0; ++ ++ out_slab: ++ jffs2_destroy_slab_caches(); ++ out_zlib: ++ jffs2_zlib_exit(); ++ out: ++ ++ return ret; ++} ++ ++static void __exit exit_jffs2_fs(void) ++{ ++ jffs2_destroy_slab_caches(); ++ jffs2_zlib_exit(); ++ unregister_filesystem(&jffs2_fs_type); ++} ++ ++module_init(init_jffs2_fs); ++module_exit(exit_jffs2_fs); ++ ++MODULE_DESCRIPTION("The Journalling Flash File System, v2"); ++MODULE_AUTHOR("Red Hat, Inc."); ++MODULE_LICENSE("GPL"); // Actually dual-licensed, but it doesn't matter for ++ // the sake of this tag. It's Free Software. +diff -Nurb linux-mips-2.4.27/fs/jffs2/super.c linux/fs/jffs2/super.c +--- linux-mips-2.4.27/fs/jffs2/super.c 2003-01-11 18:53:17.000000000 +0100 ++++ linux/fs/jffs2/super.c 2004-11-19 10:25:12.127165136 +0100 +@@ -1,291 +1,257 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: super.c,v 1.48.2.3 2002/10/11 09:04:44 dwmw2 Exp $ ++ * $Id: super.c,v 1.90 2003/10/11 11:47:23 dwmw2 Exp $ + * + */ + + #include <linux/config.h> + #include <linux/kernel.h> + #include <linux/module.h> +-#include <linux/version.h> + #include <linux/slab.h> + #include <linux/init.h> + #include <linux/list.h> + #include <linux/fs.h> ++#include <linux/mount.h> + #include <linux/jffs2.h> + #include <linux/pagemap.h> + #include <linux/mtd/mtd.h> +-#include <linux/interrupt.h> ++#include <linux/ctype.h> ++#include <linux/namei.h> + #include "nodelist.h" + +-#ifndef MTD_BLOCK_MAJOR +-#define MTD_BLOCK_MAJOR 31 +-#endif ++static void jffs2_put_super(struct super_block *); ++ ++static kmem_cache_t *jffs2_inode_cachep; ++ ++static struct inode *jffs2_alloc_inode(struct super_block *sb) ++{ ++ struct jffs2_inode_info *ei; ++ ei = (struct jffs2_inode_info *)kmem_cache_alloc(jffs2_inode_cachep, SLAB_KERNEL); ++ if (!ei) ++ return NULL; ++ return &ei->vfs_inode; ++} ++ ++static void jffs2_destroy_inode(struct inode *inode) ++{ ++ kmem_cache_free(jffs2_inode_cachep, JFFS2_INODE_INFO(inode)); ++} ++ ++static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long flags) ++{ ++ struct jffs2_inode_info *ei = (struct jffs2_inode_info *) foo; + +-extern void jffs2_read_inode (struct inode *); +-void jffs2_put_super (struct super_block *); +-void jffs2_write_super (struct super_block *); +-static int jffs2_statfs (struct super_block *, struct statfs *); +-int jffs2_remount_fs (struct super_block *, int *, char *); +-extern void jffs2_clear_inode (struct inode *); ++ if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == ++ SLAB_CTOR_CONSTRUCTOR) { ++ init_MUTEX_LOCKED(&ei->sem); ++ inode_init_once(&ei->vfs_inode); ++ } ++} + + static struct super_operations jffs2_super_operations = + { +- read_inode: jffs2_read_inode, +-// delete_inode: jffs2_delete_inode, +- put_super: jffs2_put_super, +- write_super: jffs2_write_super, +- statfs: jffs2_statfs, +- remount_fs: jffs2_remount_fs, +- clear_inode: jffs2_clear_inode ++ .alloc_inode = jffs2_alloc_inode, ++ .destroy_inode =jffs2_destroy_inode, ++ .read_inode = jffs2_read_inode, ++ .put_super = jffs2_put_super, ++ .write_super = jffs2_write_super, ++ .statfs = jffs2_statfs, ++ .remount_fs = jffs2_remount_fs, ++ .clear_inode = jffs2_clear_inode, ++ .dirty_inode = jffs2_dirty_inode, + }; + +-static int jffs2_statfs(struct super_block *sb, struct statfs *buf) ++static int jffs2_sb_compare(struct super_block *sb, void *data) + { ++ struct jffs2_sb_info *p = data; + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); +- unsigned long avail; + +- buf->f_type = JFFS2_SUPER_MAGIC; +- buf->f_bsize = 1 << PAGE_SHIFT; +- buf->f_blocks = c->flash_size >> PAGE_SHIFT; +- buf->f_files = 0; +- buf->f_ffree = 0; +- buf->f_namelen = JFFS2_MAX_NAME_LEN; +- +- spin_lock_bh(&c->erase_completion_lock); +- +- avail = c->dirty_size + c->free_size; +- if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE) +- avail -= c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE; +- else +- avail = 0; +- +- buf->f_bavail = buf->f_bfree = avail >> PAGE_SHIFT; +- +-#if CONFIG_JFFS2_FS_DEBUG > 0 +- printk(KERN_DEBUG "STATFS:\n"); +- printk(KERN_DEBUG "flash_size: %08x\n", c->flash_size); +- printk(KERN_DEBUG "used_size: %08x\n", c->used_size); +- printk(KERN_DEBUG "dirty_size: %08x\n", c->dirty_size); +- printk(KERN_DEBUG "free_size: %08x\n", c->free_size); +- printk(KERN_DEBUG "erasing_size: %08x\n", c->erasing_size); +- printk(KERN_DEBUG "bad_size: %08x\n", c->bad_size); +- printk(KERN_DEBUG "sector_size: %08x\n", c->sector_size); +- +- if (c->nextblock) { +- printk(KERN_DEBUG "nextblock: 0x%08x\n", c->nextblock->offset); +- } else { +- printk(KERN_DEBUG "nextblock: NULL\n"); +- } +- if (c->gcblock) { +- printk(KERN_DEBUG "gcblock: 0x%08x\n", c->gcblock->offset); ++ /* The superblocks are considered to be equivalent if the underlying MTD ++ device is the same one */ ++ if (c->mtd == p->mtd) { ++ D1(printk(KERN_DEBUG "jffs2_sb_compare: match on device %d (\"%s\")\n", p->mtd->index, p->mtd->name)); ++ return 1; + } else { +- printk(KERN_DEBUG "gcblock: NULL\n"); ++ D1(printk(KERN_DEBUG "jffs2_sb_compare: No match, device %d (\"%s\"), device %d (\"%s\")\n", ++ c->mtd->index, c->mtd->name, p->mtd->index, p->mtd->name)); ++ return 0; + } +- if (list_empty(&c->clean_list)) { +- printk(KERN_DEBUG "clean_list: empty\n"); +- } else { +- struct list_head *this; ++} + +- list_for_each(this, &c->clean_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "clean_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->dirty_list)) { +- printk(KERN_DEBUG "dirty_list: empty\n"); +- } else { +- struct list_head *this; ++static int jffs2_sb_set(struct super_block *sb, void *data) ++{ ++ struct jffs2_sb_info *p = data; + +- list_for_each(this, &c->dirty_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "dirty_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->erasing_list)) { +- printk(KERN_DEBUG "erasing_list: empty\n"); +- } else { +- struct list_head *this; ++ /* For persistence of NFS exports etc. we use the same s_dev ++ each time we mount the device, don't just use an anonymous ++ device */ ++ sb->s_fs_info = p; ++ p->os_priv = sb; ++ sb->s_dev = MKDEV(MTD_BLOCK_MAJOR, p->mtd->index); + +- list_for_each(this, &c->erasing_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "erasing_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->erase_pending_list)) { +- printk(KERN_DEBUG "erase_pending_list: empty\n"); +- } else { +- struct list_head *this; ++ return 0; ++} + +- list_for_each(this, &c->erase_pending_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "erase_pending_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->free_list)) { +- printk(KERN_DEBUG "free_list: empty\n"); +- } else { +- struct list_head *this; ++static struct super_block *jffs2_get_sb_mtd(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *data, struct mtd_info *mtd) ++{ ++ struct super_block *sb; ++ struct jffs2_sb_info *c; ++ int ret; + +- list_for_each(this, &c->free_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "free_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->bad_list)) { +- printk(KERN_DEBUG "bad_list: empty\n"); +- } else { +- struct list_head *this; ++ c = kmalloc(sizeof(*c), GFP_KERNEL); ++ if (!c) ++ return ERR_PTR(-ENOMEM); ++ memset(c, 0, sizeof(*c)); ++ c->mtd = mtd; + +- list_for_each(this, &c->bad_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "bad_list: %08x\n", jeb->offset); +- } +- } +- if (list_empty(&c->bad_used_list)) { +- printk(KERN_DEBUG "bad_used_list: empty\n"); +- } else { +- struct list_head *this; ++ sb = sget(fs_type, jffs2_sb_compare, jffs2_sb_set, c); ++ ++ if (IS_ERR(sb)) ++ goto out_put; + +- list_for_each(this, &c->bad_used_list) { +- struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); +- printk(KERN_DEBUG "bad_used_list: %08x\n", jeb->offset); ++ if (sb->s_root) { ++ /* New mountpoint for JFFS2 which is already mounted */ ++ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): Device %d (\"%s\") is already mounted\n", ++ mtd->index, mtd->name)); ++ goto out_put; + } ++ ++ D1(printk(KERN_DEBUG "jffs2_get_sb_mtd(): New superblock for device %d (\"%s\")\n", ++ mtd->index, mtd->name)); ++ ++ sb->s_op = &jffs2_super_operations; ++ ++ ret = jffs2_do_fill_super(sb, data, (flags&MS_VERBOSE)?1:0); ++ ++ if (ret) { ++ /* Failure case... */ ++ up_write(&sb->s_umount); ++ deactivate_super(sb); ++ return ERR_PTR(ret); + } +-#endif /* CONFIG_JFFS2_FS_DEBUG */ + +- spin_unlock_bh(&c->erase_completion_lock); ++ sb->s_flags |= MS_ACTIVE; ++ return sb; + ++ out_put: ++ kfree(c); ++ put_mtd_device(mtd); + +- return 0; ++ return sb; + } + +-static struct super_block *jffs2_read_super(struct super_block *sb, void *data, int silent) ++static struct super_block *jffs2_get_sb_mtdnr(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *data, int mtdnr) + { +- struct jffs2_sb_info *c; +- struct inode *root_i; +- int i; +- +- D1(printk(KERN_DEBUG "jffs2: read_super for device %s\n", kdevname(sb->s_dev))); ++ struct mtd_info *mtd; + +- if (MAJOR(sb->s_dev) != MTD_BLOCK_MAJOR) { +- if (!silent) +- printk(KERN_DEBUG "jffs2: attempt to mount non-MTD device %s\n", kdevname(sb->s_dev)); +- return NULL; ++ mtd = get_mtd_device(NULL, mtdnr); ++ if (!mtd) { ++ D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", mtdnr)); ++ return ERR_PTR(-EINVAL); + } + +- c = JFFS2_SB_INFO(sb); +- memset(c, 0, sizeof(*c)); ++ return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); ++} + +- c->mtd = get_mtd_device(NULL, MINOR(sb->s_dev)); +- if (!c->mtd) { +- D1(printk(KERN_DEBUG "jffs2: MTD device #%u doesn't appear to exist\n", MINOR(sb->s_dev))); +- return NULL; ++static struct super_block *jffs2_get_sb(struct file_system_type *fs_type, ++ int flags, const char *dev_name, ++ void *data) ++{ ++ int err; ++ struct nameidata nd; ++ int mtdnr; ++ ++ if (!dev_name) ++ return ERR_PTR(-EINVAL); ++ ++ D1(printk(KERN_DEBUG "jffs2_get_sb(): dev_name \"%s\"\n", dev_name)); ++ ++ /* The preferred way of mounting in future; especially when ++ CONFIG_BLK_DEV is implemented - we specify the underlying ++ MTD device by number or by name, so that we don't require ++ block device support to be present in the kernel. */ ++ ++ /* FIXME: How to do the root fs this way? */ ++ ++ if (dev_name[0] == 'm' && dev_name[1] == 't' && dev_name[2] == 'd') { ++ /* Probably mounting without the blkdev crap */ ++ if (dev_name[3] == ':') { ++ struct mtd_info *mtd; ++ ++ /* Mount by MTD device name */ ++ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd:%%s, name \"%s\"\n", dev_name+4)); ++ for (mtdnr = 0; mtdnr < MAX_MTD_DEVICES; mtdnr++) { ++ mtd = get_mtd_device(NULL, mtdnr); ++ if (mtd) { ++ if (!strcmp(mtd->name, dev_name+4)) ++ return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); ++ put_mtd_device(mtd); + } +- c->sector_size = c->mtd->erasesize; +- c->free_size = c->flash_size = c->mtd->size; +- c->nr_blocks = c->mtd->size / c->mtd->erasesize; +- c->blocks = kmalloc(sizeof(struct jffs2_eraseblock) * c->nr_blocks, GFP_KERNEL); +- if (!c->blocks) +- goto out_mtd; +- for (i=0; i<c->nr_blocks; i++) { +- INIT_LIST_HEAD(&c->blocks[i].list); +- c->blocks[i].offset = i * c->sector_size; +- c->blocks[i].free_size = c->sector_size; +- c->blocks[i].dirty_size = 0; +- c->blocks[i].used_size = 0; +- c->blocks[i].first_node = NULL; +- c->blocks[i].last_node = NULL; +- } +- +- spin_lock_init(&c->nodelist_lock); +- init_MUTEX(&c->alloc_sem); +- init_waitqueue_head(&c->erase_wait); +- spin_lock_init(&c->erase_completion_lock); +- spin_lock_init(&c->inocache_lock); +- +- INIT_LIST_HEAD(&c->clean_list); +- INIT_LIST_HEAD(&c->dirty_list); +- INIT_LIST_HEAD(&c->erasing_list); +- INIT_LIST_HEAD(&c->erase_pending_list); +- INIT_LIST_HEAD(&c->erase_complete_list); +- INIT_LIST_HEAD(&c->free_list); +- INIT_LIST_HEAD(&c->bad_list); +- INIT_LIST_HEAD(&c->bad_used_list); +- c->highest_ino = 1; +- +- if (jffs2_build_filesystem(c)) { +- D1(printk(KERN_DEBUG "build_fs failed\n")); +- goto out_nodes; + } ++ printk(KERN_NOTICE "jffs2_get_sb(): MTD device with name \"%s\" not found.\n", dev_name+4); ++ } else if (isdigit(dev_name[3])) { ++ /* Mount by MTD device number name */ ++ char *endptr; + +- sb->s_op = &jffs2_super_operations; ++ mtdnr = simple_strtoul(dev_name+3, &endptr, 0); ++ if (!*endptr) { ++ /* It was a valid number */ ++ D1(printk(KERN_DEBUG "jffs2_get_sb(): mtd%%d, mtdnr %d\n", mtdnr)); ++ return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); ++ } ++ } ++ } + +- D1(printk(KERN_DEBUG "jffs2_read_super(): Getting root inode\n")); +- root_i = iget(sb, 1); +- if (is_bad_inode(root_i)) { +- D1(printk(KERN_WARNING "get root inode failed\n")); +- goto out_nodes; ++ /* Try the old way - the hack where we allowed users to mount ++ /dev/mtdblock$(n) but didn't actually _use_ the blkdev */ ++ ++ err = path_lookup(dev_name, LOOKUP_FOLLOW, &nd); ++ ++ D1(printk(KERN_DEBUG "jffs2_get_sb(): path_lookup() returned %d, inode %p\n", ++ err, nd.dentry->d_inode)); ++ ++ if (err) ++ return ERR_PTR(err); ++ ++ err = -EINVAL; ++ ++ if (!S_ISBLK(nd.dentry->d_inode->i_mode)) ++ goto out; ++ ++ if (nd.mnt->mnt_flags & MNT_NODEV) { ++ err = -EACCES; ++ goto out; + } + +- D1(printk(KERN_DEBUG "jffs2_read_super(): d_alloc_root()\n")); +- sb->s_root = d_alloc_root(root_i); +- if (!sb->s_root) +- goto out_root_i; ++ if (imajor(nd.dentry->d_inode) != MTD_BLOCK_MAJOR) { ++ if (!(flags & MS_VERBOSE)) /* Yes I mean this. Strangely */ ++ printk(KERN_NOTICE "Attempt to mount non-MTD device \"%s\" as JFFS2\n", ++ dev_name); ++ goto out; ++ } + +-#if LINUX_VERSION_CODE >= 0x20403 +- sb->s_maxbytes = 0xFFFFFFFF; +-#endif +- sb->s_blocksize = PAGE_CACHE_SIZE; +- sb->s_blocksize_bits = PAGE_CACHE_SHIFT; +- sb->s_magic = JFFS2_SUPER_MAGIC; +- if (!(sb->s_flags & MS_RDONLY)) +- jffs2_start_garbage_collect_thread(c); +- return sb; ++ mtdnr = iminor(nd.dentry->d_inode); ++ path_release(&nd); + +- out_root_i: +- iput(root_i); +- out_nodes: +- jffs2_free_ino_caches(c); +- jffs2_free_raw_node_refs(c); +- kfree(c->blocks); +- out_mtd: +- put_mtd_device(c->mtd); +- return NULL; ++ return jffs2_get_sb_mtdnr(fs_type, flags, dev_name, data, mtdnr); ++ ++out: ++ path_release(&nd); ++ return ERR_PTR(err); + } + +-void jffs2_put_super (struct super_block *sb) ++static void jffs2_put_super (struct super_block *sb) + { + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); + +@@ -293,74 +259,53 @@ + + if (!(sb->s_flags & MS_RDONLY)) + jffs2_stop_garbage_collect_thread(c); ++ down(&c->alloc_sem); ++ jffs2_flush_wbuf_pad(c); ++ up(&c->alloc_sem); + jffs2_free_ino_caches(c); + jffs2_free_raw_node_refs(c); + kfree(c->blocks); ++ jffs2_nand_flash_cleanup(c); ++ kfree(c->inocache_list); + if (c->mtd->sync) + c->mtd->sync(c->mtd); +- put_mtd_device(c->mtd); + + D1(printk(KERN_DEBUG "jffs2_put_super returning\n")); + } + +-int jffs2_remount_fs (struct super_block *sb, int *flags, char *data) ++static void jffs2_kill_sb(struct super_block *sb) + { + struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); +- +- if (c->flags & JFFS2_SB_FLAG_RO && !(sb->s_flags & MS_RDONLY)) +- return -EROFS; +- +- /* We stop if it was running, then restart if it needs to. +- This also catches the case where it was stopped and this +- is just a remount to restart it */ +- if (!(sb->s_flags & MS_RDONLY)) +- jffs2_stop_garbage_collect_thread(c); +- +- if (!(*flags & MS_RDONLY)) +- jffs2_start_garbage_collect_thread(c); +- +- sb->s_flags = (sb->s_flags & ~MS_RDONLY)|(*flags & MS_RDONLY); +- +- return 0; +-} +- +-void jffs2_write_super (struct super_block *sb) +-{ +- struct jffs2_sb_info *c = JFFS2_SB_INFO(sb); +- sb->s_dirt = 0; +- +- if (sb->s_flags & MS_RDONLY) +- return; +- +- jffs2_garbage_collect_trigger(c); +- jffs2_erase_pending_blocks(c); +- jffs2_mark_erased_blocks(c); ++ generic_shutdown_super(sb); ++ put_mtd_device(c->mtd); ++ kfree(c); + } + +- +-static DECLARE_FSTYPE_DEV(jffs2_fs_type, "jffs2", jffs2_read_super); ++static struct file_system_type jffs2_fs_type = { ++ .owner = THIS_MODULE, ++ .name = "jffs2", ++ .get_sb = jffs2_get_sb, ++ .kill_sb = jffs2_kill_sb, ++}; + + static int __init init_jffs2_fs(void) + { + int ret; + +- printk(KERN_NOTICE "JFFS2 version 2.1. (C) 2001 Red Hat, Inc., designed by Axis Communications AB.\n"); +- +-#ifdef JFFS2_OUT_OF_KERNEL +- /* sanity checks. Could we do these at compile time? */ +- if (sizeof(struct jffs2_sb_info) > sizeof (((struct super_block *)NULL)->u)) { +- printk(KERN_ERR "JFFS2 error: struct jffs2_sb_info (%d bytes) doesn't fit in the super_block union (%d bytes)\n", +- sizeof(struct jffs2_sb_info), sizeof (((struct super_block *)NULL)->u)); +- return -EIO; +- } +- +- if (sizeof(struct jffs2_inode_info) > sizeof (((struct inode *)NULL)->u)) { +- printk(KERN_ERR "JFFS2 error: struct jffs2_inode_info (%d bytes) doesn't fit in the inode union (%d bytes)\n", +- sizeof(struct jffs2_inode_info), sizeof (((struct inode *)NULL)->u)); +- return -EIO; +- } ++ printk(KERN_INFO "JFFS2 version 2.2." ++#ifdef CONFIG_FS_JFFS2_NAND ++ " (NAND)" + #endif ++ " (C) 2001-2003 Red Hat, Inc.\n"); + ++ jffs2_inode_cachep = kmem_cache_create("jffs2_i", ++ sizeof(struct jffs2_inode_info), ++ 0, SLAB_HWCACHE_ALIGN|SLAB_RECLAIM_ACCOUNT, ++ jffs2_i_init_once, NULL); ++ if (!jffs2_inode_cachep) { ++ printk(KERN_ERR "JFFS2 error: Failed to initialise inode cache\n"); ++ return -ENOMEM; ++ } + ret = jffs2_zlib_init(); + if (ret) { + printk(KERN_ERR "JFFS2 error: Failed to initialise zlib workspaces\n"); +@@ -388,9 +333,10 @@ + + static void __exit exit_jffs2_fs(void) + { ++ unregister_filesystem(&jffs2_fs_type); + jffs2_destroy_slab_caches(); + jffs2_zlib_exit(); +- unregister_filesystem(&jffs2_fs_type); ++ kmem_cache_destroy(jffs2_inode_cachep); + } + + module_init(init_jffs2_fs); +diff -Nurb linux-mips-2.4.27/fs/jffs2/symlink.c linux/fs/jffs2/symlink.c +--- linux-mips-2.4.27/fs/jffs2/symlink.c 2002-06-27 00:36:20.000000000 +0200 ++++ linux/fs/jffs2/symlink.c 2004-11-19 10:25:12.129164832 +0100 +@@ -3,35 +3,11 @@ + * + * Copyright (C) 2001, 2002 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: symlink.c,v 1.5.2.1 2002/01/15 10:39:06 dwmw2 Exp $ ++ * $Id: symlink.c,v 1.12 2003/10/04 08:33:07 dwmw2 Exp $ + * + */ + +@@ -39,7 +15,6 @@ + #include <linux/kernel.h> + #include <linux/slab.h> + #include <linux/fs.h> +-#include <linux/jffs2.h> + #include "nodelist.h" + + int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen); +@@ -47,45 +22,17 @@ + + struct inode_operations jffs2_symlink_inode_operations = + { +- readlink: jffs2_readlink, +- follow_link: jffs2_follow_link, +- setattr: jffs2_setattr ++ .readlink = jffs2_readlink, ++ .follow_link = jffs2_follow_link, ++ .setattr = jffs2_setattr + }; + +-static char *jffs2_getlink(struct dentry *dentry) +-{ +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(dentry->d_inode); +- char *buf; +- int ret; +- +- down(&f->sem); +- if (!f->metadata) { +- up(&f->sem); +- printk(KERN_NOTICE "No metadata for symlink inode #%lu\n", dentry->d_inode->i_ino); +- return ERR_PTR(-EINVAL); +- } +- buf = kmalloc(f->metadata->size+1, GFP_USER); +- if (!buf) { +- up(&f->sem); +- return ERR_PTR(-ENOMEM); +- } +- buf[f->metadata->size]=0; +- +- ret = jffs2_read_dnode(JFFS2_SB_INFO(dentry->d_inode->i_sb), f->metadata, buf, 0, f->metadata->size); +- up(&f->sem); +- if (ret) { +- kfree(buf); +- return ERR_PTR(ret); +- } +- return buf; +- +-} + int jffs2_readlink(struct dentry *dentry, char *buffer, int buflen) + { + unsigned char *kbuf; + int ret; + +- kbuf = jffs2_getlink(dentry); ++ kbuf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode)); + if (IS_ERR(kbuf)) + return PTR_ERR(kbuf); + +@@ -99,7 +46,7 @@ + unsigned char *buf; + int ret; + +- buf = jffs2_getlink(dentry); ++ buf = jffs2_getlink(JFFS2_SB_INFO(dentry->d_inode->i_sb), JFFS2_INODE_INFO(dentry->d_inode)); + + if (IS_ERR(buf)) + return PTR_ERR(buf); +diff -Nurb linux-mips-2.4.27/fs/jffs2/wbuf.c linux/fs/jffs2/wbuf.c +--- linux-mips-2.4.27/fs/jffs2/wbuf.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/wbuf.c 2004-11-19 10:25:12.131164528 +0100 +@@ -0,0 +1,1156 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001-2003 Red Hat, Inc. ++ * ++ * Created by David Woodhouse <dwmw2@redhat.com> ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: wbuf.c,v 1.58 2003/11/02 09:51:10 dwmw2 Exp $ ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/slab.h> ++#include <linux/mtd/mtd.h> ++#include <linux/crc32.h> ++#include <linux/mtd/nand.h> ++#include "nodelist.h" ++ ++/* For testing write failures */ ++#undef BREAKME ++#undef BREAKMEHEADER ++ ++#ifdef BREAKME ++static unsigned char *brokenbuf; ++#endif ++ ++/* max. erase failures before we mark a block bad */ ++#define MAX_ERASE_FAILURES 5 ++ ++/* two seconds timeout for timed wbuf-flushing */ ++#define WBUF_FLUSH_TIMEOUT 2 * HZ ++ ++struct jffs2_inodirty { ++ uint32_t ino; ++ struct jffs2_inodirty *next; ++}; ++ ++static struct jffs2_inodirty inodirty_nomem; ++ ++static int jffs2_wbuf_pending_for_ino(struct jffs2_sb_info *c, uint32_t ino) ++{ ++ struct jffs2_inodirty *this = c->wbuf_inodes; ++ ++ /* If a malloc failed, consider _everything_ dirty */ ++ if (this == &inodirty_nomem) ++ return 1; ++ ++ /* If ino == 0, _any_ non-GC writes mean 'yes' */ ++ if (this && !ino) ++ return 1; ++ ++ /* Look to see if the inode in question is pending in the wbuf */ ++ while (this) { ++ if (this->ino == ino) ++ return 1; ++ this = this->next; ++ } ++ return 0; ++} ++ ++static void jffs2_clear_wbuf_ino_list(struct jffs2_sb_info *c) ++{ ++ struct jffs2_inodirty *this; ++ ++ this = c->wbuf_inodes; ++ ++ if (this != &inodirty_nomem) { ++ while (this) { ++ struct jffs2_inodirty *next = this->next; ++ kfree(this); ++ this = next; ++ } ++ } ++ c->wbuf_inodes = NULL; ++} ++ ++static void jffs2_wbuf_dirties_inode(struct jffs2_sb_info *c, uint32_t ino) ++{ ++ struct jffs2_inodirty *new; ++ ++ /* Mark the superblock dirty so that kupdated will flush... */ ++ OFNI_BS_2SFFJ(c)->s_dirt = 1; ++ ++ if (jffs2_wbuf_pending_for_ino(c, ino)) ++ return; ++ ++ new = kmalloc(sizeof(*new), GFP_KERNEL); ++ if (!new) { ++ D1(printk(KERN_DEBUG "No memory to allocate inodirty. Fallback to all considered dirty\n")); ++ jffs2_clear_wbuf_ino_list(c); ++ c->wbuf_inodes = &inodirty_nomem; ++ return; ++ } ++ new->ino = ino; ++ new->next = c->wbuf_inodes; ++ c->wbuf_inodes = new; ++ return; ++} ++ ++static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) ++{ ++ struct list_head *this, *next; ++ static int n; ++ ++ if (list_empty(&c->erasable_pending_wbuf_list)) ++ return; ++ ++ list_for_each_safe(this, next, &c->erasable_pending_wbuf_list) { ++ struct jffs2_eraseblock *jeb = list_entry(this, struct jffs2_eraseblock, list); ++ ++ D1(printk(KERN_DEBUG "Removing eraseblock at 0x%08x from erasable_pending_wbuf_list...\n", jeb->offset)); ++ list_del(this); ++ if ((jiffies + (n++)) & 127) { ++ /* Most of the time, we just erase it immediately. Otherwise we ++ spend ages scanning it on mount, etc. */ ++ D1(printk(KERN_DEBUG "...and adding to erase_pending_list\n")); ++ list_add_tail(&jeb->list, &c->erase_pending_list); ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); ++ } else { ++ /* Sometimes, however, we leave it elsewhere so it doesn't get ++ immediately reused, and we spread the load a bit. */ ++ D1(printk(KERN_DEBUG "...and adding to erasable_list\n")); ++ list_add_tail(&jeb->list, &c->erasable_list); ++ } ++ } ++} ++ ++/* Recover from failure to write wbuf. Recover the nodes up to the ++ * wbuf, not the one which we were starting to try to write. */ ++ ++static void jffs2_wbuf_recover(struct jffs2_sb_info *c) ++{ ++ struct jffs2_eraseblock *jeb, *new_jeb; ++ struct jffs2_raw_node_ref **first_raw, **raw; ++ size_t retlen; ++ int ret; ++ unsigned char *buf; ++ uint32_t start, end, ofs, len; ++ ++ spin_lock(&c->erase_completion_lock); ++ ++ jeb = &c->blocks[c->wbuf_ofs / c->sector_size]; ++ ++ D1(printk("About to refile bad block at %08x\n", jeb->offset)); ++ ++ D2(jffs2_dump_block_lists(c)); ++ /* File the existing block on the bad_used_list.... */ ++ if (c->nextblock == jeb) ++ c->nextblock = NULL; ++ else /* Not sure this should ever happen... need more coffee */ ++ list_del(&jeb->list); ++ if (jeb->first_node) { ++ D1(printk("Refiling block at %08x to bad_used_list\n", jeb->offset)); ++ list_add(&jeb->list, &c->bad_used_list); ++ } else { ++ BUG(); ++ /* It has to have had some nodes or we couldn't be here */ ++ D1(printk("Refiling block at %08x to erase_pending_list\n", jeb->offset)); ++ list_add(&jeb->list, &c->erase_pending_list); ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); ++ } ++ D2(jffs2_dump_block_lists(c)); ++ ++ /* Adjust its size counts accordingly */ ++ c->wasted_size += jeb->free_size; ++ c->free_size -= jeb->free_size; ++ jeb->wasted_size += jeb->free_size; ++ jeb->free_size = 0; ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ /* Find the first node to be recovered, by skipping over every ++ node which ends before the wbuf starts, or which is obsolete. */ ++ first_raw = &jeb->first_node; ++ while (*first_raw && ++ (ref_obsolete(*first_raw) || ++ (ref_offset(*first_raw)+ref_totlen(c, jeb, *first_raw)) < c->wbuf_ofs)) { ++ D1(printk(KERN_DEBUG "Skipping node at 0x%08x(%d)-0x%08x which is either before 0x%08x or obsolete\n", ++ ref_offset(*first_raw), ref_flags(*first_raw), ++ (ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw)), ++ c->wbuf_ofs)); ++ first_raw = &(*first_raw)->next_phys; ++ } ++ ++ if (!*first_raw) { ++ /* All nodes were obsolete. Nothing to recover. */ ++ D1(printk(KERN_DEBUG "No non-obsolete nodes to be recovered. Just filing block bad\n")); ++ spin_unlock(&c->erase_completion_lock); ++ return; ++ } ++ ++ start = ref_offset(*first_raw); ++ end = ref_offset(*first_raw) + ref_totlen(c, jeb, *first_raw); ++ ++ /* Find the last node to be recovered */ ++ raw = first_raw; ++ while ((*raw)) { ++ if (!ref_obsolete(*raw)) ++ end = ref_offset(*raw) + ref_totlen(c, jeb, *raw); ++ ++ raw = &(*raw)->next_phys; ++ } ++ spin_unlock(&c->erase_completion_lock); ++ ++ D1(printk(KERN_DEBUG "wbuf recover %08x-%08x\n", start, end)); ++ ++ buf = NULL; ++ if (start < c->wbuf_ofs) { ++ /* First affected node was already partially written. ++ * Attempt to reread the old data into our buffer. */ ++ ++ buf = kmalloc(end - start, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_CRIT "Malloc failure in wbuf recovery. Data loss ensues.\n"); ++ ++ goto read_failed; ++ } ++ ++ /* Do the read... */ ++ ret = c->mtd->read_ecc(c->mtd, start, c->wbuf_ofs - start, &retlen, buf, NULL, c->oobinfo); ++ if (ret == -EIO && retlen == c->wbuf_ofs - start) { ++ /* ECC recovered */ ++ ret = 0; ++ } ++ if (ret || retlen != c->wbuf_ofs - start) { ++ printk(KERN_CRIT "Old data are already lost in wbuf recovery. Data loss ensues.\n"); ++ ++ kfree(buf); ++ buf = NULL; ++ read_failed: ++ first_raw = &(*first_raw)->next_phys; ++ /* If this was the only node to be recovered, give up */ ++ if (!(*first_raw)) ++ return; ++ ++ /* It wasn't. Go on and try to recover nodes complete in the wbuf */ ++ start = ref_offset(*first_raw); ++ } else { ++ /* Read succeeded. Copy the remaining data from the wbuf */ ++ memcpy(buf + (c->wbuf_ofs - start), c->wbuf, end - c->wbuf_ofs); ++ } ++ } ++ /* OK... we're to rewrite (end-start) bytes of data from first_raw onwards. ++ Either 'buf' contains the data, or we find it in the wbuf */ ++ ++ ++ /* ... and get an allocation of space from a shiny new block instead */ ++ ret = jffs2_reserve_space_gc(c, end-start, &ofs, &len); ++ if (ret) { ++ printk(KERN_WARNING "Failed to allocate space for wbuf recovery. Data loss ensues.\n"); ++ if (buf) ++ kfree(buf); ++ return; ++ } ++ if (end-start >= c->wbuf_pagesize) { ++ /* Need to do another write immediately. This, btw, ++ means that we'll be writing from 'buf' and not from ++ the wbuf. Since if we're writing from the wbuf there ++ won't be more than a wbuf full of data, now will ++ there? :) */ ++ ++ uint32_t towrite = (end-start) - ((end-start)%c->wbuf_pagesize); ++ ++ D1(printk(KERN_DEBUG "Write 0x%x bytes at 0x%08x in wbuf recover\n", ++ towrite, ofs)); ++ ++#ifdef BREAKMEHEADER ++ static int breakme; ++ if (breakme++ == 20) { ++ printk(KERN_NOTICE "Faking write error at 0x%08x\n", ofs); ++ breakme = 0; ++ c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, ++ brokenbuf, NULL, c->oobinfo); ++ ret = -EIO; ++ } else ++#endif ++ ret = c->mtd->write_ecc(c->mtd, ofs, towrite, &retlen, ++ buf, NULL, c->oobinfo); ++ ++ if (ret || retlen != towrite) { ++ /* Argh. We tried. Really we did. */ ++ printk(KERN_CRIT "Recovery of wbuf failed due to a second write error\n"); ++ kfree(buf); ++ ++ if (retlen) { ++ struct jffs2_raw_node_ref *raw2; ++ ++ raw2 = jffs2_alloc_raw_node_ref(); ++ if (!raw2) ++ return; ++ ++ raw2->flash_offset = ofs | REF_OBSOLETE; ++ raw2->__totlen = ref_totlen(c, jeb, *first_raw); ++ raw2->next_phys = NULL; ++ raw2->next_in_ino = NULL; ++ ++ jffs2_add_physical_node_ref(c, raw2); ++ } ++ return; ++ } ++ printk(KERN_NOTICE "Recovery of wbuf succeeded to %08x\n", ofs); ++ ++ c->wbuf_len = (end - start) - towrite; ++ c->wbuf_ofs = ofs + towrite; ++ memcpy(c->wbuf, buf + towrite, c->wbuf_len); ++ /* Don't muck about with c->wbuf_inodes. False positives are harmless. */ ++ ++ kfree(buf); ++ } else { ++ /* OK, now we're left with the dregs in whichever buffer we're using */ ++ if (buf) { ++ memcpy(c->wbuf, buf, end-start); ++ kfree(buf); ++ } else { ++ memmove(c->wbuf, c->wbuf + (start - c->wbuf_ofs), end - start); ++ } ++ c->wbuf_ofs = ofs; ++ c->wbuf_len = end - start; ++ } ++ ++ /* Now sort out the jffs2_raw_node_refs, moving them from the old to the next block */ ++ new_jeb = &c->blocks[ofs / c->sector_size]; ++ ++ spin_lock(&c->erase_completion_lock); ++ if (new_jeb->first_node) { ++ /* Odd, but possible with ST flash later maybe */ ++ new_jeb->last_node->next_phys = *first_raw; ++ } else { ++ new_jeb->first_node = *first_raw; ++ } ++ ++ raw = first_raw; ++ while (*raw) { ++ uint32_t rawlen = ref_totlen(c, jeb, *raw); ++ ++ D1(printk(KERN_DEBUG "Refiling block of %08x at %08x(%d) to %08x\n", ++ rawlen, ref_offset(*raw), ref_flags(*raw), ofs)); ++ ++ if (ref_obsolete(*raw)) { ++ /* Shouldn't really happen much */ ++ new_jeb->dirty_size += rawlen; ++ new_jeb->free_size -= rawlen; ++ c->dirty_size += rawlen; ++ } else { ++ new_jeb->used_size += rawlen; ++ new_jeb->free_size -= rawlen; ++ jeb->dirty_size += rawlen; ++ jeb->used_size -= rawlen; ++ c->dirty_size += rawlen; ++ } ++ c->free_size -= rawlen; ++ (*raw)->flash_offset = ofs | ref_flags(*raw); ++ ofs += rawlen; ++ new_jeb->last_node = *raw; ++ ++ raw = &(*raw)->next_phys; ++ } ++ ++ /* Fix up the original jeb now it's on the bad_list */ ++ *first_raw = NULL; ++ if (first_raw == &jeb->first_node) { ++ jeb->last_node = NULL; ++ D1(printk(KERN_DEBUG "Failing block at %08x is now empty. Moving to erase_pending_list\n", jeb->offset)); ++ list_del(&jeb->list); ++ list_add(&jeb->list, &c->erase_pending_list); ++ c->nr_erasing_blocks++; ++ jffs2_erase_pending_trigger(c); ++ } ++ else ++ jeb->last_node = container_of(first_raw, struct jffs2_raw_node_ref, next_phys); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ ACCT_SANITY_CHECK(c,new_jeb); ++ D1(ACCT_PARANOIA_CHECK(new_jeb)); ++ ++ spin_unlock(&c->erase_completion_lock); ++ ++ D1(printk(KERN_DEBUG "wbuf recovery completed OK\n")); ++} ++ ++/* Meaning of pad argument: ++ 0: Do not pad. Probably pointless - we only ever use this when we can't pad anyway. ++ 1: Pad, do not adjust nextblock free_size ++ 2: Pad, adjust nextblock free_size ++*/ ++static int __jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) ++{ ++ int ret; ++ size_t retlen; ++ ++ /* Nothing to do if not NAND flash. In particular, we shouldn't ++ del_timer() the timer we never initialised. */ ++ if (jffs2_can_mark_obsolete(c)) ++ return 0; ++ ++ if (!down_trylock(&c->alloc_sem)) { ++ up(&c->alloc_sem); ++ printk(KERN_CRIT "jffs2_flush_wbuf() called with alloc_sem not locked!\n"); ++ BUG(); ++ } ++ ++ if(!c->wbuf || !c->wbuf_len) ++ return 0; ++ ++ /* claim remaining space on the page ++ this happens, if we have a change to a new block, ++ or if fsync forces us to flush the writebuffer. ++ if we have a switch to next page, we will not have ++ enough remaining space for this. ++ */ ++ if (pad) { ++ c->wbuf_len = PAD(c->wbuf_len); ++ ++ if ( c->wbuf_len + sizeof(struct jffs2_unknown_node) < c->wbuf_pagesize) { ++ struct jffs2_unknown_node *padnode = (void *)(c->wbuf + c->wbuf_len); ++ padnode->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ padnode->nodetype = cpu_to_je16(JFFS2_NODETYPE_PADDING); ++ padnode->totlen = cpu_to_je32(c->wbuf_pagesize - c->wbuf_len); ++ padnode->hdr_crc = cpu_to_je32(crc32(0, padnode, sizeof(*padnode)-4)); ++ } ++ } ++ /* else jffs2_flash_writev has actually filled in the rest of the ++ buffer for us, and will deal with the node refs etc. later. */ ++ ++#ifdef BREAKME ++ static int breakme; ++ if (breakme++ == 20) { ++ printk(KERN_NOTICE "Faking write error at 0x%08x\n", c->wbuf_ofs); ++ breakme = 0; ++ c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, ++ &retlen, brokenbuf, NULL, c->oobinfo); ++ ret = -EIO; ++ } else ++#endif ++ ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, c->oobinfo); ++ ++ ++ if (ret || retlen != c->wbuf_pagesize) { ++ if (ret) ++ printk(KERN_WARNING "jffs2_flush_wbuf(): Write failed with %d\n",ret); ++ else { ++ printk(KERN_WARNING "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", ++ retlen, c->wbuf_pagesize); ++ ret = -EIO; ++ } ++ ++ jffs2_wbuf_recover(c); ++ ++ return ret; ++ } ++ ++ /* Adjusting free size of next block only, if it's called from fsync ! */ ++ if (pad == 2) { ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf() adjusting free_size of c->nextblock\n")); ++ spin_lock(&c->erase_completion_lock); ++ if (!c->nextblock) ++ BUG(); ++ /* wbuf_pagesize - wbuf_len is the amount of space that's to be ++ padded. If there is less free space in the block than that, ++ something screwed up */ ++ if (c->nextblock->free_size < (c->wbuf_pagesize - c->wbuf_len)) { ++ printk(KERN_CRIT "jffs2_flush_wbuf(): Accounting error. wbuf at 0x%08x has 0x%03x bytes, 0x%03x left.\n", ++ c->wbuf_ofs, c->wbuf_len, c->wbuf_pagesize-c->wbuf_len); ++ printk(KERN_CRIT "jffs2_flush_wbuf(): But free_size for block at 0x%08x is only 0x%08x\n", ++ c->nextblock->offset, c->nextblock->free_size); ++ BUG(); ++ } ++ c->nextblock->free_size -= (c->wbuf_pagesize - c->wbuf_len); ++ c->free_size -= (c->wbuf_pagesize - c->wbuf_len); ++ c->nextblock->wasted_size += (c->wbuf_pagesize - c->wbuf_len); ++ c->wasted_size += (c->wbuf_pagesize - c->wbuf_len); ++ spin_unlock(&c->erase_completion_lock); ++ } ++ ++ /* Stick any now-obsoleted blocks on the erase_pending_list */ ++ spin_lock(&c->erase_completion_lock); ++ jffs2_refile_wbuf_blocks(c); ++ jffs2_clear_wbuf_ino_list(c); ++ spin_unlock(&c->erase_completion_lock); ++ ++ memset(c->wbuf,0xff,c->wbuf_pagesize); ++ /* adjust write buffer offset, else we get a non contiguous write bug */ ++ c->wbuf_ofs += c->wbuf_pagesize; ++ c->wbuf_len = 0; ++ return 0; ++} ++ ++/* Trigger garbage collection to flush the write-buffer. ++ If ino arg is zero, do it if _any_ real (i.e. not GC) writes are ++ outstanding. If ino arg non-zero, do it only if a write for the ++ given inode is outstanding. */ ++int jffs2_flush_wbuf_gc(struct jffs2_sb_info *c, uint32_t ino) ++{ ++ uint32_t old_wbuf_ofs; ++ uint32_t old_wbuf_len; ++ int ret = 0; ++ ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() called for ino #%u...\n", ino)); ++ ++ down(&c->alloc_sem); ++ if (!jffs2_wbuf_pending_for_ino(c, ino)) { ++ D1(printk(KERN_DEBUG "Ino #%d not pending in wbuf. Returning\n", ino)); ++ up(&c->alloc_sem); ++ return 0; ++ } ++ ++ old_wbuf_ofs = c->wbuf_ofs; ++ old_wbuf_len = c->wbuf_len; ++ ++ if (c->unchecked_size) { ++ /* GC won't make any progress for a while */ ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() padding. Not finished checking\n")); ++ ret = __jffs2_flush_wbuf(c, 2); ++ } else while (old_wbuf_len && ++ old_wbuf_ofs == c->wbuf_ofs) { ++ ++ up(&c->alloc_sem); ++ ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() calls gc pass\n")); ++ ++ ret = jffs2_garbage_collect_pass(c); ++ if (ret) { ++ /* GC failed. Flush it with padding instead */ ++ down(&c->alloc_sem); ++ ret = __jffs2_flush_wbuf(c, 2); ++ break; ++ } ++ down(&c->alloc_sem); ++ } ++ ++ D1(printk(KERN_DEBUG "jffs2_flush_wbuf_gc() ends...\n")); ++ ++ up(&c->alloc_sem); ++ return ret; ++} ++ ++/* Pad write-buffer to end and write it, wasting space. */ ++int jffs2_flush_wbuf_pad(struct jffs2_sb_info *c) ++{ ++ return __jffs2_flush_wbuf(c, 1); ++} ++ ++ ++#define PAGE_DIV(x) ( (x) & (~(c->wbuf_pagesize - 1)) ) ++#define PAGE_MOD(x) ( (x) & (c->wbuf_pagesize - 1) ) ++int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsigned long count, loff_t to, size_t *retlen, uint32_t ino) ++{ ++ struct iovec outvecs[3]; ++ uint32_t totlen = 0; ++ uint32_t split_ofs = 0; ++ uint32_t old_totlen; ++ int ret, splitvec = -1; ++ int invec, outvec; ++ size_t wbuf_retlen; ++ unsigned char *wbuf_ptr; ++ size_t donelen = 0; ++ uint32_t outvec_to = to; ++ ++ /* If not NAND flash, don't bother */ ++ if (!c->wbuf) ++ return jffs2_flash_direct_writev(c, invecs, count, to, retlen); ++ ++ /* If wbuf_ofs is not initialized, set it to target address */ ++ if (c->wbuf_ofs == 0xFFFFFFFF) { ++ c->wbuf_ofs = PAGE_DIV(to); ++ c->wbuf_len = PAGE_MOD(to); ++ memset(c->wbuf,0xff,c->wbuf_pagesize); ++ } ++ ++ /* Sanity checks on target address. ++ It's permitted to write at PAD(c->wbuf_len+c->wbuf_ofs), ++ and it's permitted to write at the beginning of a new ++ erase block. Anything else, and you die. ++ New block starts at xxx000c (0-b = block header) ++ */ ++ if ( (to & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) { ++ /* It's a write to a new block */ ++ if (c->wbuf_len) { ++ D1(printk(KERN_DEBUG "jffs2_flash_writev() to 0x%lx causes flush of wbuf at 0x%08x\n", (unsigned long)to, c->wbuf_ofs)); ++ ret = jffs2_flush_wbuf_pad(c); ++ if (ret) { ++ /* the underlying layer has to check wbuf_len to do the cleanup */ ++ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); ++ *retlen = 0; ++ return ret; ++ } ++ } ++ /* set pointer to new block */ ++ c->wbuf_ofs = PAGE_DIV(to); ++ c->wbuf_len = PAGE_MOD(to); ++ } ++ ++ if (to != PAD(c->wbuf_ofs + c->wbuf_len)) { ++ /* We're not writing immediately after the writebuffer. Bad. */ ++ printk(KERN_CRIT "jffs2_flash_writev(): Non-contiguous write to %08lx\n", (unsigned long)to); ++ if (c->wbuf_len) ++ printk(KERN_CRIT "wbuf was previously %08x-%08x\n", ++ c->wbuf_ofs, c->wbuf_ofs+c->wbuf_len); ++ BUG(); ++ } ++ ++ /* Note outvecs[3] above. We know count is never greater than 2 */ ++ if (count > 2) { ++ printk(KERN_CRIT "jffs2_flash_writev(): count is %ld\n", count); ++ BUG(); ++ } ++ ++ invec = 0; ++ outvec = 0; ++ ++ ++ /* Fill writebuffer first, if already in use */ ++ if (c->wbuf_len) { ++ uint32_t invec_ofs = 0; ++ ++ /* adjust alignment offset */ ++ if (c->wbuf_len != PAGE_MOD(to)) { ++ c->wbuf_len = PAGE_MOD(to); ++ /* take care of alignment to next page */ ++ if (!c->wbuf_len) ++ c->wbuf_len = c->wbuf_pagesize; ++ } ++ ++ while(c->wbuf_len < c->wbuf_pagesize) { ++ uint32_t thislen; ++ ++ if (invec == count) ++ goto alldone; ++ ++ thislen = c->wbuf_pagesize - c->wbuf_len; ++ ++ if (thislen >= invecs[invec].iov_len) ++ thislen = invecs[invec].iov_len; ++ ++ invec_ofs = thislen; ++ ++ memcpy(c->wbuf + c->wbuf_len, invecs[invec].iov_base, thislen); ++ c->wbuf_len += thislen; ++ donelen += thislen; ++ /* Get next invec, if actual did not fill the buffer */ ++ if (c->wbuf_len < c->wbuf_pagesize) ++ invec++; ++ } ++ ++ /* write buffer is full, flush buffer */ ++ ret = __jffs2_flush_wbuf(c, 0); ++ if (ret) { ++ /* the underlying layer has to check wbuf_len to do the cleanup */ ++ D1(printk(KERN_WARNING "jffs2_flush_wbuf() called from jffs2_flash_writev() failed %d\n", ret)); ++ /* Retlen zero to make sure our caller doesn't mark the space dirty. ++ We've already done everything that's necessary */ ++ *retlen = 0; ++ return ret; ++ } ++ outvec_to += donelen; ++ c->wbuf_ofs = outvec_to; ++ ++ /* All invecs done ? */ ++ if (invec == count) ++ goto alldone; ++ ++ /* Set up the first outvec, containing the remainder of the ++ invec we partially used */ ++ if (invecs[invec].iov_len > invec_ofs) { ++ outvecs[0].iov_base = invecs[invec].iov_base+invec_ofs; ++ totlen = outvecs[0].iov_len = invecs[invec].iov_len-invec_ofs; ++ if (totlen > c->wbuf_pagesize) { ++ splitvec = outvec; ++ split_ofs = outvecs[0].iov_len - PAGE_MOD(totlen); ++ } ++ outvec++; ++ } ++ invec++; ++ } ++ ++ /* OK, now we've flushed the wbuf and the start of the bits ++ we have been asked to write, now to write the rest.... */ ++ ++ /* totlen holds the amount of data still to be written */ ++ old_totlen = totlen; ++ for ( ; invec < count; invec++,outvec++ ) { ++ outvecs[outvec].iov_base = invecs[invec].iov_base; ++ totlen += outvecs[outvec].iov_len = invecs[invec].iov_len; ++ if (PAGE_DIV(totlen) != PAGE_DIV(old_totlen)) { ++ splitvec = outvec; ++ split_ofs = outvecs[outvec].iov_len - PAGE_MOD(totlen); ++ old_totlen = totlen; ++ } ++ } ++ ++ /* Now the outvecs array holds all the remaining data to write */ ++ /* Up to splitvec,split_ofs is to be written immediately. The rest ++ goes into the (now-empty) wbuf */ ++ ++ if (splitvec != -1) { ++ uint32_t remainder; ++ int ret; ++ ++ remainder = outvecs[splitvec].iov_len - split_ofs; ++ outvecs[splitvec].iov_len = split_ofs; ++ ++ /* We did cross a page boundary, so we write some now */ ++ ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, c->oobinfo); ++ if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { ++ /* At this point we have no problem, ++ c->wbuf is empty. ++ */ ++ *retlen = donelen; ++ return ret; ++ } ++ ++ donelen += wbuf_retlen; ++ c->wbuf_ofs = PAGE_DIV(outvec_to) + PAGE_DIV(totlen); ++ ++ if (remainder) { ++ outvecs[splitvec].iov_base += split_ofs; ++ outvecs[splitvec].iov_len = remainder; ++ } else { ++ splitvec++; ++ } ++ ++ } else { ++ splitvec = 0; ++ } ++ ++ /* Now splitvec points to the start of the bits we have to copy ++ into the wbuf */ ++ wbuf_ptr = c->wbuf; ++ ++ for ( ; splitvec < outvec; splitvec++) { ++ /* Don't copy the wbuf into itself */ ++ if (outvecs[splitvec].iov_base == c->wbuf) ++ continue; ++ memcpy(wbuf_ptr, outvecs[splitvec].iov_base, outvecs[splitvec].iov_len); ++ wbuf_ptr += outvecs[splitvec].iov_len; ++ donelen += outvecs[splitvec].iov_len; ++ } ++ c->wbuf_len = wbuf_ptr - c->wbuf; ++ ++ /* If there's a remainder in the wbuf and it's a non-GC write, ++ remember that the wbuf affects this ino */ ++alldone: ++ *retlen = donelen; ++ ++ if (c->wbuf_len && ino) ++ jffs2_wbuf_dirties_inode(c, ino); ++ ++ return 0; ++} ++ ++/* ++ * This is the entry for flash write. ++ * Check, if we work on NAND FLASH, if so build an iovec and write it via vritev ++*/ ++int jffs2_flash_write(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, const u_char *buf) ++{ ++ struct iovec vecs[1]; ++ ++ if (jffs2_can_mark_obsolete(c)) ++ return c->mtd->write(c->mtd, ofs, len, retlen, buf); ++ ++ vecs[0].iov_base = (unsigned char *) buf; ++ vecs[0].iov_len = len; ++ return jffs2_flash_writev(c, vecs, 1, ofs, retlen, 0); ++} ++ ++/* ++ Handle readback from writebuffer and ECC failure return ++*/ ++int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *retlen, u_char *buf) ++{ ++ loff_t orbf = 0, owbf = 0, lwbf = 0; ++ int ret; ++ ++ /* Read flash */ ++ if (!jffs2_can_mark_obsolete(c)) { ++ ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, c->oobinfo); ++ ++ if ( (ret == -EIO) && (*retlen == len) ) { ++ printk(KERN_WARNING "mtd->read(0x%zx bytes from 0x%llx) returned ECC error\n", ++ len, ofs); ++ /* ++ * We have the raw data without ECC correction in the buffer, maybe ++ * we are lucky and all data or parts are correct. We check the node. ++ * If data are corrupted node check will sort it out. ++ * We keep this block, it will fail on write or erase and the we ++ * mark it bad. Or should we do that now? But we should give him a chance. ++ * Maybe we had a system crash or power loss before the ecc write or ++ * a erase was completed. ++ * So we return success. :) ++ */ ++ ret = 0; ++ } ++ } else ++ return c->mtd->read(c->mtd, ofs, len, retlen, buf); ++ ++ /* if no writebuffer available or write buffer empty, return */ ++ if (!c->wbuf_pagesize || !c->wbuf_len) ++ return ret; ++ ++ /* if we read in a different block, return */ ++ if ( (ofs & ~(c->sector_size-1)) != (c->wbuf_ofs & ~(c->sector_size-1)) ) ++ return ret; ++ ++ if (ofs >= c->wbuf_ofs) { ++ owbf = (ofs - c->wbuf_ofs); /* offset in write buffer */ ++ if (owbf > c->wbuf_len) /* is read beyond write buffer ? */ ++ return ret; ++ lwbf = c->wbuf_len - owbf; /* number of bytes to copy */ ++ if (lwbf > len) ++ lwbf = len; ++ } else { ++ orbf = (c->wbuf_ofs - ofs); /* offset in read buffer */ ++ if (orbf > len) /* is write beyond write buffer ? */ ++ return ret; ++ lwbf = len - orbf; /* number of bytes to copy */ ++ if (lwbf > c->wbuf_len) ++ lwbf = c->wbuf_len; ++ } ++ if (lwbf > 0) ++ memcpy(buf+orbf,c->wbuf+owbf,lwbf); ++ ++ return ret; ++} ++ ++/* ++ * Check, if the out of band area is empty ++ */ ++int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, int mode) ++{ ++ unsigned char *buf; ++ int ret = 0; ++ int i,len,page; ++ size_t retlen; ++ int oob_size; ++ ++ oob_size = c->mtd->oobsize; ++ ++ /* allocate a buffer for all oob data in this sector */ ++ len = 4 * oob_size; ++ buf = kmalloc(len, GFP_KERNEL); ++ if (!buf) { ++ printk(KERN_NOTICE "jffs2_check_oob_empty(): allocation of temporary data buffer for oob check failed\n"); ++ return -ENOMEM; ++ } ++ /* ++ * if mode = 0, we scan for a total empty oob area, else we have ++ * to take care of the cleanmarker in the first page of the block ++ */ ++ ret = jffs2_flash_read_oob(c, jeb->offset, len , &retlen, buf); ++ if (ret) { ++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); ++ goto out; ++ } ++ ++ if (retlen < len) { ++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " ++ "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); ++ ret = -EIO; ++ goto out; ++ } ++ ++ /* Special check for first two pages */ ++ for (page = 0; page < 2 * oob_size; page += oob_size) { ++ /* Check for bad block marker */ ++ if (buf[page+c->badblock_pos] != 0xff) { ++ D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Bad or failed block at %08x\n",jeb->offset)); ++ /* Return 2 for bad and 3 for failed block ++ bad goes to list_bad and failed to list_erase */ ++ ret = (!page) ? 2 : 3; ++ goto out; ++ } ++ for(i = 0; i < oob_size ; i++) { ++ /* Yeah, we know about the cleanmarker. */ ++ if (mode && i >= c->fsdata_pos && ++ i < c->fsdata_pos+c->fsdata_len) ++ continue; ++ ++ if (buf[page+i] != 0xFF) { ++ D2(printk(KERN_DEBUG "Found %02x at %x in OOB for %08x\n", ++ buf[page+i], page+i, jeb->offset)); ++ ret = 1; ++ goto out; ++ } ++ } ++ /* only the first page can contain a cleanmarker !*/ ++ mode = 0; ++ } ++ ++ /* we know, we are aligned :) */ ++ for (; page < len; page += sizeof(long)) { ++ unsigned long dat = *(unsigned long *)(&buf[page]); ++ if(dat != -1) { ++ ret = 1; ++ goto out; ++ } ++ } ++ ++out: ++ kfree(buf); ++ ++ return ret; ++} ++ ++/* ++* Scan for a valid cleanmarker and for bad blocks ++* For virtual blocks (concatenated physical blocks) check the cleanmarker ++* only in the first page of the first physical block, but scan for bad blocks in all ++* physical blocks ++*/ ++int jffs2_check_nand_cleanmarker (struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ struct jffs2_unknown_node n; ++ unsigned char buf[32]; ++ unsigned char *p; ++ int ret, i, cnt, retval = 0; ++ size_t retlen, offset; ++ int oob_size; ++ ++ offset = jeb->offset; ++ oob_size = c->mtd->oobsize; ++ ++ /* Loop through the physical blocks */ ++ for (cnt = 0; cnt < (c->sector_size / c->mtd->erasesize); cnt++) { ++ /* ++ * We read oob data from page 0 and 1 of the block. ++ * page 0 contains cleanmarker and badblock info ++ * page 1 contains failure count of this block ++ */ ++ ret = c->mtd->read_oob (c->mtd, offset, oob_size << 1, &retlen, buf); ++ ++ if (ret) { ++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); ++ return ret; ++ } ++ if (retlen < (oob_size << 1)) { ++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size << 1, jeb->offset)); ++ return -EIO; ++ } ++ ++ /* Check for bad block marker */ ++ if (buf[c->badblock_pos] != 0xff) { ++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x (has %02x %02x in badblock_pos %d\n", ++ jeb->offset, buf[c->badblock_pos], buf[c->badblock_pos + oob_size], c->badblock_pos)); ++ return 2; ++ } ++ ++ /* Check for failure counter in the second page */ ++ if (buf[c->badblock_pos + oob_size] != 0xff) { ++ D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Block marked as failed at %08x, fail count:%d\n", jeb->offset, buf[c->badblock_pos + oob_size])); ++ return 3; ++ } ++ ++ /* Check cleanmarker only on the first physical block */ ++ if (!cnt) { ++ n.magic = cpu_to_je16 (JFFS2_MAGIC_BITMASK); ++ n.nodetype = cpu_to_je16 (JFFS2_NODETYPE_CLEANMARKER); ++ n.totlen = cpu_to_je32 (8); ++ p = (unsigned char *) &n; ++ ++ for (i = 0; i < c->fsdata_len; i++) { ++ if (buf[c->fsdata_pos + i] != p[i]) { ++ retval = 1; ++ } ++ } ++ D1(if (retval == 1) { ++ printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset); ++ printk(KERN_WARNING "OOB at %08x was ", offset); ++ for (i=0; i < oob_size; i++) { ++ printk("%02x ", buf[i]); ++ } ++ printk("\n"); ++ }) ++ } ++ offset += c->mtd->erasesize; ++ } ++ return retval; ++} ++ ++int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ struct jffs2_unknown_node n; ++ int ret; ++ size_t retlen; ++ ++ n.magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ n.nodetype = cpu_to_je16(JFFS2_NODETYPE_CLEANMARKER); ++ n.totlen = cpu_to_je32(8); ++ ++ ret = jffs2_flash_write_oob(c, jeb->offset + c->fsdata_pos, c->fsdata_len, &retlen, (unsigned char *)&n); ++ ++ if (ret) { ++ D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); ++ return ret; ++ } ++ if (retlen != c->fsdata_len) { ++ D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, c->fsdata_len)); ++ return ret; ++ } ++ return 0; ++} ++ ++/* ++ * We try to get the failure count of this block. ++ */ ++int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { ++ ++ unsigned char buf[16]; ++ int ret; ++ size_t retlen; ++ int oob_size; ++ ++ oob_size = c->mtd->oobsize; ++ ++ ret = c->mtd->read_oob(c->mtd, jeb->offset + c->mtd->oobblock, oob_size , &retlen, buf); ++ ++ if (ret) { ++ D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB failed %d for block at %08x\n", ret, jeb->offset)); ++ return ret; ++ } ++ ++ if (retlen < oob_size) { ++ D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%zd bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset)); ++ return -EIO; ++ } ++ ++ jeb->bad_count = buf[c->badblock_pos]; ++ return 0; ++} ++ ++/* ++ * On NAND we try to mark this block bad. We try to write how often ++ * the block was erased and mark it finaly bad, if the count ++ * is > MAX_ERASE_FAILURES. We read this information on mount ! ++ * jeb->bad_count contains the count before this erase. ++ * Don't care about failures. This block remains on the erase-pending ++ * or badblock list as long as nobody manipulates the flash with ++ * a bootloader or something like that. ++ */ ++ ++int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) ++{ ++ unsigned char buf = 0x0; ++ int ret; ++ size_t retlen; ++ ++ /* if the count is < max, we try to write the counter to the 2nd page oob area */ ++ if( ++jeb->bad_count < MAX_ERASE_FAILURES) { ++ buf = (unsigned char)jeb->bad_count; ++ c->badblock_pos += c->mtd->oobblock; ++ } ++ ++ ret = jffs2_flash_write_oob(c, jeb->offset + c->badblock_pos, 1, &retlen, &buf); ++ ++ if (ret) { ++ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Write failed for block at %08x: error %d\n", jeb->offset, ret)); ++ return ret; ++ } ++ if (retlen != 1) { ++ D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %zd not 1\n", jeb->offset, retlen)); ++ return ret; ++ } ++ return 0; ++} ++ ++#define JFFS2_OOB_ECCPOS0 0 ++#define JFFS2_OOB_ECCPOS1 1 ++#define JFFS2_OOB_ECCPOS2 2 ++#define JFFS2_OOB_ECCPOS3 3 ++#define JFFS2_OOB_ECCPOS4 6 ++#define JFFS2_OOB_ECCPOS5 7 ++ ++#define NAND_JFFS2_OOB8_FSDAPOS 6 ++#define NAND_JFFS2_OOB16_FSDAPOS 8 ++#define NAND_JFFS2_OOB8_FSDALEN 2 ++#define NAND_JFFS2_OOB16_FSDALEN 8 ++ ++static struct nand_oobinfo jffs2_oobinfo_swecc = { ++ .useecc = 1, ++ .eccpos = {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2, ++ JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5} ++}; ++ ++static struct nand_oobinfo jffs2_oobinfo_docecc = { ++ .useecc = 1, ++ .eccpos = {0,1,2,3,4,5} ++}; ++ ++ ++ ++int jffs2_nand_flash_setup(struct jffs2_sb_info *c) ++{ ++ /* Cleanmarker is out-of-band, so inline size zero */ ++ c->cleanmarker_size = 0; ++ ++ /* Initialise write buffer */ ++ c->wbuf_pagesize = c->mtd->oobblock; ++ c->wbuf_ofs = 0xFFFFFFFF; ++ ++ /* FIXME: If we had a generic way of describing the hardware's ++ use of OOB area, we could perhaps make this generic too. */ ++ switch(c->mtd->ecctype) { ++ case MTD_ECC_SW: ++ D1(printk(KERN_DEBUG "JFFS2 using software ECC\n")); ++ c->oobinfo = &jffs2_oobinfo_swecc; ++ if (c->mtd->oobsize == 8) { ++ c->fsdata_pos = NAND_JFFS2_OOB8_FSDAPOS; ++ c->fsdata_len = NAND_JFFS2_OOB8_FSDALEN; ++ } else { ++ c->fsdata_pos = NAND_JFFS2_OOB16_FSDAPOS; ++ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; ++ } ++ c->badblock_pos = NAND_BADBLOCK_POS; ++ break; ++ ++ case MTD_ECC_RS_DiskOnChip: ++ D1(printk(KERN_DEBUG "JFFS2 using DiskOnChip hardware ECC\n")); ++ c->oobinfo = &jffs2_oobinfo_docecc; ++ c->fsdata_pos = 6; ++ c->fsdata_len = NAND_JFFS2_OOB16_FSDALEN; ++ c->badblock_pos = 15; ++ break; ++ ++ default: ++ printk("JFFS2 doesn't yet know how to handle ECC type %d\n", ++ c->mtd->ecctype); ++ return -EINVAL; ++ } ++ ++ c->wbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); ++ if (!c->wbuf) ++ return -ENOMEM; ++ ++#ifdef BREAKME ++ if (!brokenbuf) ++ brokenbuf = kmalloc(c->wbuf_pagesize, GFP_KERNEL); ++ if (!brokenbuf) { ++ kfree(c->wbuf); ++ return -ENOMEM; ++ } ++ memset(brokenbuf, 0xdb, c->wbuf_pagesize); ++#endif ++ return 0; ++} ++ ++void jffs2_nand_flash_cleanup(struct jffs2_sb_info *c) ++{ ++ kfree(c->wbuf); ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/write.c linux/fs/jffs2/write.c +--- linux-mips-2.4.27/fs/jffs2/write.c 2003-11-17 02:07:44.000000000 +0100 ++++ linux/fs/jffs2/write.c 2004-11-19 10:25:12.132164376 +0100 +@@ -1,154 +1,70 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in this directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: write.c,v 1.30.2.2 2003/11/02 13:51:18 dwmw2 Exp $ ++ * $Id: write.c,v 1.80 2004/01/27 13:21:50 dwmw2 Exp $ + * + */ + + #include <linux/kernel.h> + #include <linux/fs.h> +-#include <linux/jffs2.h> ++#include <linux/crc32.h> ++#include <linux/slab.h> ++#include <linux/pagemap.h> + #include <linux/mtd/mtd.h> + #include "nodelist.h" +-#include <linux/crc32.h> + +-/* jffs2_new_inode: allocate a new inode and inocache, add it to the hash, +- fill in the raw_inode while you're at it. */ +-struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_inode *ri) ++ ++int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint32_t mode, struct jffs2_raw_inode *ri) + { +- struct inode *inode; +- struct super_block *sb = dir_i->i_sb; + struct jffs2_inode_cache *ic; +- struct jffs2_sb_info *c; +- struct jffs2_inode_info *f; +- +- D1(printk(KERN_DEBUG "jffs2_new_inode(): dir_i %ld, mode 0x%x\n", dir_i->i_ino, mode)); +- +- c = JFFS2_SB_INFO(sb); +- memset(ri, 0, sizeof(*ri)); + + ic = jffs2_alloc_inode_cache(); + if (!ic) { +- return ERR_PTR(-ENOMEM); ++ return -ENOMEM; + } +- memset(ic, 0, sizeof(*ic)); +- +- inode = new_inode(sb); + +- if (!inode) { +- jffs2_free_inode_cache(ic); +- return ERR_PTR(-ENOMEM); +- } +- +- /* Alloc jffs2_inode_info when that's split in 2.5 */ ++ memset(ic, 0, sizeof(*ic)); + +- f = JFFS2_INODE_INFO(inode); +- memset(f, 0, sizeof(*f)); + init_MUTEX_LOCKED(&f->sem); + f->inocache = ic; +- inode->i_nlink = f->inocache->nlink = 1; ++ f->inocache->nlink = 1; + f->inocache->nodes = (struct jffs2_raw_node_ref *)f->inocache; +- f->inocache->ino = ri->ino = inode->i_ino = ++c->highest_ino; +- D1(printk(KERN_DEBUG "jffs2_new_inode(): Assigned ino# %d\n", ri->ino)); +- jffs2_add_ino_cache(c, f->inocache); ++ f->inocache->ino = ++c->highest_ino; ++ f->inocache->state = INO_STATE_PRESENT; + +- ri->magic = JFFS2_MAGIC_BITMASK; +- ri->nodetype = JFFS2_NODETYPE_INODE; +- ri->totlen = PAD(sizeof(*ri)); +- ri->hdr_crc = crc32(0, ri, sizeof(struct jffs2_unknown_node)-4); +- ri->mode = mode; +- f->highest_version = ri->version = 1; +- ri->uid = current->fsuid; +- if (dir_i->i_mode & S_ISGID) { +- ri->gid = dir_i->i_gid; +- if (S_ISDIR(mode)) +- ri->mode |= S_ISGID; +- } else { +- ri->gid = current->fsgid; +- } +- inode->i_mode = ri->mode; +- inode->i_gid = ri->gid; +- inode->i_uid = ri->uid; +- inode->i_atime = inode->i_ctime = inode->i_mtime = +- ri->atime = ri->mtime = ri->ctime = CURRENT_TIME; +- inode->i_blksize = PAGE_SIZE; +- inode->i_blocks = 0; +- inode->i_size = 0; ++ ri->ino = cpu_to_je32(f->inocache->ino); + +- insert_inode_hash(inode); ++ D1(printk(KERN_DEBUG "jffs2_do_new_inode(): Assigned ino# %d\n", f->inocache->ino)); ++ jffs2_add_ino_cache(c, f->inocache); + +- return inode; +-} ++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri->totlen = cpu_to_je32(PAD(sizeof(*ri))); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ++ ri->mode = cpu_to_jemode(mode); + +-/* This ought to be in core MTD code. All registered MTD devices +- without writev should have this put in place. Bug the MTD +- maintainer */ +-static int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen) +-{ +- unsigned long i; +- size_t totlen = 0, thislen; +- int ret = 0; ++ f->highest_version = 1; ++ ri->version = cpu_to_je32(f->highest_version); + +- for (i=0; i<count; i++) { +- ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); +- totlen += thislen; +- if (ret || thislen != vecs[i].iov_len) +- break; +- to += vecs[i].iov_len; +- } +- if (retlen) +- *retlen = totlen; +- return ret; +-} +- +- +-static inline int mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen) +-{ +- if (mtd->writev) +- return mtd->writev(mtd,vecs,count,to,retlen); +- else +- return mtd_fake_writev(mtd, vecs, count, to, retlen); ++ return 0; + } + +-static void writecheck(struct mtd_info *mtd, __u32 ofs) ++#if CONFIG_JFFS2_FS_DEBUG > 0 ++static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) + { + unsigned char buf[16]; +- ssize_t retlen; ++ size_t retlen; + int ret, i; + +- ret = mtd->read(mtd, ofs, 16, &retlen, buf); +- if (ret && retlen != 16) { +- D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %d\n", ret, retlen)); ++ ret = jffs2_flash_read(c, ofs, 16, &retlen, buf); ++ if (ret || (retlen != 16)) { ++ D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen)); + return; + } + ret = 0; +@@ -157,32 +73,31 @@ + ret = 1; + } + if (ret) { +- printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there's data already there:\n", ofs); ++ printk(KERN_WARNING "ARGH. About to write node to 0x%08x on flash, but there are data already there:\n", ofs); + printk(KERN_WARNING "0x%08x: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n", + ofs, + buf[0], buf[1], buf[2], buf[3], buf[4], buf[5], buf[6], buf[7], + buf[8], buf[9], buf[10], buf[11], buf[12], buf[13], buf[14], buf[15]); + } + } +- +- ++#endif + + + /* jffs2_write_dnode - given a raw_inode, allocate a full_dnode for it, + write it to the flash, link it into the existing inode/fragment list */ + +-struct jffs2_full_dnode *jffs2_write_dnode(struct inode *inode, struct jffs2_raw_inode *ri, const unsigned char *data, __u32 datalen, __u32 flash_ofs, __u32 *writelen) ++struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const unsigned char *data, uint32_t datalen, uint32_t flash_ofs, int alloc_mode) + + { +- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dnode *fn; +- ssize_t retlen; ++ size_t retlen; + struct iovec vecs[2]; + int ret; ++ int retried = 0; ++ unsigned long cnt = 2; + +- D1(if(ri->hdr_crc != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) { ++ D1(if(je32_to_cpu(ri->hdr_crc) != crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)) { + printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dnode()\n"); + BUG(); + } +@@ -192,10 +107,10 @@ + vecs[1].iov_base = (unsigned char *)data; + vecs[1].iov_len = datalen; + +- writecheck(c->mtd, flash_ofs); ++ D1(writecheck(c, flash_ofs)); + +- if (ri->totlen != sizeof(*ri) + datalen) { +- printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", ri->totlen, sizeof(*ri), datalen); ++ if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) { ++ printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08zx) + datalen (0x%08x)\n", je32_to_cpu(ri->totlen), sizeof(*ri), datalen); + } + raw = jffs2_alloc_raw_node_ref(); + if (!raw) +@@ -206,19 +121,28 @@ + jffs2_free_raw_node_ref(raw); + return ERR_PTR(-ENOMEM); + } +- raw->flash_offset = flash_ofs; +- raw->totlen = PAD(ri->totlen); +- raw->next_phys = NULL; + +- fn->ofs = ri->offset; +- fn->size = ri->dsize; ++ fn->ofs = je32_to_cpu(ri->offset); ++ fn->size = je32_to_cpu(ri->dsize); + fn->frags = 0; ++ ++ /* check number of valid vecs */ ++ if (!datalen || !data) ++ cnt = 1; ++ retry: + fn->raw = raw; + +- ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen); ++ raw->flash_offset = flash_ofs; ++ raw->__totlen = PAD(sizeof(*ri)+datalen); ++ raw->next_phys = NULL; ++ ++ ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen, ++ (alloc_mode==ALLOC_GC)?0:f->inocache->ino); ++ + if (ret || (retlen != sizeof(*ri) + datalen)) { +- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", ++ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + sizeof(*ri)+datalen, flash_ofs, ret, retlen); ++ + /* Mark the space as dirtied */ + if (retlen) { + /* Doesn't belong to any inode */ +@@ -229,48 +153,96 @@ + seem corrupted, in which case the scan would skip over + any node we write before the original intended end of + this node */ +- jffs2_add_physical_node_ref(c, raw, sizeof(*ri)+datalen, 1); ++ raw->flash_offset |= REF_OBSOLETE; ++ jffs2_add_physical_node_ref(c, raw); + jffs2_mark_node_obsolete(c, raw); + } else { + printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); + jffs2_free_raw_node_ref(raw); + } ++ if (!retried && alloc_mode != ALLOC_NORETRY && (raw = jffs2_alloc_raw_node_ref())) { ++ /* Try to reallocate space and retry */ ++ uint32_t dummy; ++ struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; ++ ++ retried = 1; ++ ++ D1(printk(KERN_DEBUG "Retrying failed write.\n")); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ if (alloc_mode == ALLOC_GC) { ++ ret = jffs2_reserve_space_gc(c, sizeof(*ri) + datalen, &flash_ofs, &dummy); ++ } else { ++ /* Locking pain */ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ ++ ret = jffs2_reserve_space(c, sizeof(*ri) + datalen, &flash_ofs, &dummy, alloc_mode); ++ down(&f->sem); ++ } ++ ++ if (!ret) { ++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); + ++ goto retry; ++ } ++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); ++ jffs2_free_raw_node_ref(raw); ++ } + /* Release the full_dnode which is now useless, and return */ + jffs2_free_full_dnode(fn); +- if (writelen) +- *writelen = retlen; + return ERR_PTR(ret?ret:-EIO); + } + /* Mark the space used */ +- jffs2_add_physical_node_ref(c, raw, retlen, 0); ++ /* If node covers at least a whole page, or if it starts at the ++ beginning of a page and runs to the end of the file, or if ++ it's a hole node, mark it REF_PRISTINE, else REF_NORMAL. ++ */ ++ if ((je32_to_cpu(ri->dsize) >= PAGE_CACHE_SIZE) || ++ ( ((je32_to_cpu(ri->offset)&(PAGE_CACHE_SIZE-1))==0) && ++ (je32_to_cpu(ri->dsize)+je32_to_cpu(ri->offset) == je32_to_cpu(ri->isize)))) { ++ raw->flash_offset |= REF_PRISTINE; ++ } else { ++ raw->flash_offset |= REF_NORMAL; ++ } ++ jffs2_add_physical_node_ref(c, raw); + + /* Link into per-inode list */ + raw->next_in_ino = f->inocache->nodes; + f->inocache->nodes = raw; + +- D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", flash_ofs, ri->dsize, ri->csize, ri->node_crc, ri->data_crc, ri->totlen)); +- if (writelen) +- *writelen = retlen; ++ D1(printk(KERN_DEBUG "jffs2_write_dnode wrote node at 0x%08x(%d) with dsize 0x%x, csize 0x%x, node_crc 0x%08x, data_crc 0x%08x, totlen 0x%08x\n", ++ flash_ofs, ref_flags(raw), je32_to_cpu(ri->dsize), ++ je32_to_cpu(ri->csize), je32_to_cpu(ri->node_crc), ++ je32_to_cpu(ri->data_crc), je32_to_cpu(ri->totlen))); ++ ++ if (retried) { ++ ACCT_SANITY_CHECK(c,NULL); ++ } + +- f->inocache->nodes = raw; + return fn; + } + +-struct jffs2_full_dirent *jffs2_write_dirent(struct inode *inode, struct jffs2_raw_dirent *rd, const unsigned char *name, __u32 namelen, __u32 flash_ofs, __u32 *writelen) ++struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_raw_dirent *rd, const unsigned char *name, uint32_t namelen, uint32_t flash_ofs, int alloc_mode) + { +- struct jffs2_sb_info *c = JFFS2_SB_INFO(inode->i_sb); +- struct jffs2_inode_info *f = JFFS2_INODE_INFO(inode); + struct jffs2_raw_node_ref *raw; + struct jffs2_full_dirent *fd; +- ssize_t retlen; ++ size_t retlen; + struct iovec vecs[2]; ++ int retried = 0; + int ret; + +- D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", rd->pino, name, name, rd->ino, rd->name_crc)); +- writecheck(c->mtd, flash_ofs); ++ D1(printk(KERN_DEBUG "jffs2_write_dirent(ino #%u, name at *0x%p \"%s\"->ino #%u, name_crc 0x%08x)\n", ++ je32_to_cpu(rd->pino), name, name, je32_to_cpu(rd->ino), ++ je32_to_cpu(rd->name_crc))); ++ D1(writecheck(c, flash_ofs)); + +- D1(if(rd->hdr_crc != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { ++ D1(if(je32_to_cpu(rd->hdr_crc) != crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)) { + printk(KERN_CRIT "Eep. CRC not correct in jffs2_write_dirent()\n"); + BUG(); + } +@@ -291,44 +263,414 @@ + jffs2_free_raw_node_ref(raw); + return ERR_PTR(-ENOMEM); + } +- raw->flash_offset = flash_ofs; +- raw->totlen = PAD(rd->totlen); +- raw->next_in_ino = f->inocache->nodes; +- f->inocache->nodes = raw; +- raw->next_phys = NULL; + +- fd->version = rd->version; +- fd->ino = rd->ino; ++ fd->version = je32_to_cpu(rd->version); ++ fd->ino = je32_to_cpu(rd->ino); + fd->nhash = full_name_hash(name, strlen(name)); + fd->type = rd->type; + memcpy(fd->name, name, namelen); + fd->name[namelen]=0; ++ ++ retry: + fd->raw = raw; + +- ret = mtd_writev(c->mtd, vecs, 2, flash_ofs, &retlen); ++ raw->flash_offset = flash_ofs; ++ raw->__totlen = PAD(sizeof(*rd)+namelen); ++ raw->next_phys = NULL; ++ ++ ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen, ++ (alloc_mode==ALLOC_GC)?0:je32_to_cpu(rd->pino)); + if (ret || (retlen != sizeof(*rd) + namelen)) { +- printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %d\n", ++ printk(KERN_NOTICE "Write of %zd bytes at 0x%08x failed. returned %d, retlen %zd\n", + sizeof(*rd)+namelen, flash_ofs, ret, retlen); + /* Mark the space as dirtied */ + if (retlen) { +- jffs2_add_physical_node_ref(c, raw, sizeof(*rd)+namelen, 1); ++ raw->next_in_ino = NULL; ++ raw->flash_offset |= REF_OBSOLETE; ++ jffs2_add_physical_node_ref(c, raw); + jffs2_mark_node_obsolete(c, raw); + } else { + printk(KERN_NOTICE "Not marking the space at 0x%08x as dirty because the flash driver returned retlen zero\n", raw->flash_offset); + jffs2_free_raw_node_ref(raw); + } ++ if (!retried && (raw = jffs2_alloc_raw_node_ref())) { ++ /* Try to reallocate space and retry */ ++ uint32_t dummy; ++ struct jffs2_eraseblock *jeb = &c->blocks[flash_ofs / c->sector_size]; ++ ++ retried = 1; ++ ++ D1(printk(KERN_DEBUG "Retrying failed write.\n")); ++ ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ ++ if (alloc_mode == ALLOC_GC) { ++ ret = jffs2_reserve_space_gc(c, sizeof(*rd) + namelen, &flash_ofs, &dummy); ++ } else { ++ /* Locking pain */ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ ++ ret = jffs2_reserve_space(c, sizeof(*rd) + namelen, &flash_ofs, &dummy, alloc_mode); ++ down(&f->sem); ++ } + ++ if (!ret) { ++ D1(printk(KERN_DEBUG "Allocated space at 0x%08x to retry failed write.\n", flash_ofs)); ++ ACCT_SANITY_CHECK(c,jeb); ++ D1(ACCT_PARANOIA_CHECK(jeb)); ++ goto retry; ++ } ++ D1(printk(KERN_DEBUG "Failed to allocate space to retry failed write: %d!\n", ret)); ++ jffs2_free_raw_node_ref(raw); ++ } + /* Release the full_dnode which is now useless, and return */ + jffs2_free_full_dirent(fd); +- if (writelen) +- *writelen = retlen; + return ERR_PTR(ret?ret:-EIO); + } + /* Mark the space used */ +- jffs2_add_physical_node_ref(c, raw, retlen, 0); +- if (writelen) +- *writelen = retlen; ++ raw->flash_offset |= REF_PRISTINE; ++ jffs2_add_physical_node_ref(c, raw); + ++ raw->next_in_ino = f->inocache->nodes; + f->inocache->nodes = raw; ++ ++ if (retried) { ++ ACCT_SANITY_CHECK(c,NULL); ++ } ++ + return fd; + } ++ ++/* The OS-specific code fills in the metadata in the jffs2_raw_inode for us, so that ++ we don't have to go digging in struct inode or its equivalent. It should set: ++ mode, uid, gid, (starting)isize, atime, ctime, mtime */ ++int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, ++ struct jffs2_raw_inode *ri, unsigned char *buf, ++ uint32_t offset, uint32_t writelen, uint32_t *retlen) ++{ ++ int ret = 0; ++ uint32_t writtenlen = 0; ++ ++ D1(printk(KERN_DEBUG "jffs2_write_inode_range(): Ino #%u, ofs 0x%x, len 0x%x\n", ++ f->inocache->ino, offset, writelen)); ++ ++ while(writelen) { ++ struct jffs2_full_dnode *fn; ++ unsigned char *comprbuf = NULL; ++ unsigned char comprtype = JFFS2_COMPR_NONE; ++ uint32_t phys_ofs, alloclen; ++ uint32_t datalen, cdatalen; ++ int retried = 0; ++ ++ retry: ++ D2(printk(KERN_DEBUG "jffs2_commit_write() loop: 0x%x to write to 0x%x\n", writelen, offset)); ++ ++ ret = jffs2_reserve_space(c, sizeof(*ri) + JFFS2_MIN_DATA_LEN, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ if (ret) { ++ D1(printk(KERN_DEBUG "jffs2_reserve_space returned %d\n", ret)); ++ break; ++ } ++ down(&f->sem); ++ datalen = min_t(uint32_t, writelen, PAGE_CACHE_SIZE - (offset & (PAGE_CACHE_SIZE-1))); ++ cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), datalen); ++ ++ comprtype = jffs2_compress(buf, &comprbuf, &datalen, &cdatalen); ++ ++ ri->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ ri->nodetype = cpu_to_je16(JFFS2_NODETYPE_INODE); ++ ri->totlen = cpu_to_je32(sizeof(*ri) + cdatalen); ++ ri->hdr_crc = cpu_to_je32(crc32(0, ri, sizeof(struct jffs2_unknown_node)-4)); ++ ++ ri->ino = cpu_to_je32(f->inocache->ino); ++ ri->version = cpu_to_je32(++f->highest_version); ++ ri->isize = cpu_to_je32(max(je32_to_cpu(ri->isize), offset + datalen)); ++ ri->offset = cpu_to_je32(offset); ++ ri->csize = cpu_to_je32(cdatalen); ++ ri->dsize = cpu_to_je32(datalen); ++ ri->compr = comprtype; ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ++ ri->data_crc = cpu_to_je32(crc32(0, comprbuf, cdatalen)); ++ ++ fn = jffs2_write_dnode(c, f, ri, comprbuf, cdatalen, phys_ofs, ALLOC_NORETRY); ++ ++ jffs2_free_comprbuf(comprbuf, buf); ++ ++ if (IS_ERR(fn)) { ++ ret = PTR_ERR(fn); ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ if (!retried) { ++ /* Write error to be retried */ ++ retried = 1; ++ D1(printk(KERN_DEBUG "Retrying node write in jffs2_write_inode_range()\n")); ++ goto retry; ++ } ++ break; ++ } ++ ret = jffs2_add_full_dnode_to_inode(c, f, fn); ++ if (f->metadata) { ++ jffs2_mark_node_obsolete(c, f->metadata->raw); ++ jffs2_free_full_dnode(f->metadata); ++ f->metadata = NULL; ++ } ++ if (ret) { ++ /* Eep */ ++ D1(printk(KERN_DEBUG "Eep. add_full_dnode_to_inode() failed in commit_write, returned %d\n", ret)); ++ jffs2_mark_node_obsolete(c, fn->raw); ++ jffs2_free_full_dnode(fn); ++ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ break; ++ } ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ if (!datalen) { ++ printk(KERN_WARNING "Eep. We didn't actually write any data in jffs2_write_inode_range()\n"); ++ ret = -EIO; ++ break; ++ } ++ D1(printk(KERN_DEBUG "increasing writtenlen by %d\n", datalen)); ++ writtenlen += datalen; ++ offset += datalen; ++ writelen -= datalen; ++ buf += datalen; ++ } ++ *retlen = writtenlen; ++ return ret; ++} ++ ++int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, struct jffs2_inode_info *f, struct jffs2_raw_inode *ri, const char *name, int namelen) ++{ ++ struct jffs2_raw_dirent *rd; ++ struct jffs2_full_dnode *fn; ++ struct jffs2_full_dirent *fd; ++ uint32_t alloclen, phys_ofs; ++ int ret; ++ ++ /* Try to reserve enough space for both node and dirent. ++ * Just the node will do for now, though ++ */ ++ ret = jffs2_reserve_space(c, sizeof(*ri), &phys_ofs, &alloclen, ALLOC_NORMAL); ++ D1(printk(KERN_DEBUG "jffs2_do_create(): reserved 0x%x bytes\n", alloclen)); ++ if (ret) { ++ up(&f->sem); ++ return ret; ++ } ++ ++ ri->data_crc = cpu_to_je32(0); ++ ri->node_crc = cpu_to_je32(crc32(0, ri, sizeof(*ri)-8)); ++ ++ fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, ALLOC_NORMAL); ++ ++ D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", ++ jemode_to_cpu(ri->mode))); ++ ++ if (IS_ERR(fn)) { ++ D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); ++ /* Eeek. Wave bye bye */ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ return PTR_ERR(fn); ++ } ++ /* No data here. Only a metadata node, which will be ++ obsoleted by the first data write ++ */ ++ f->metadata = fn; ++ ++ up(&f->sem); ++ jffs2_complete_reservation(c); ++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ ++ if (ret) { ++ /* Eep. */ ++ D1(printk(KERN_DEBUG "jffs2_reserve_space() for dirent failed\n")); ++ return ret; ++ } ++ ++ rd = jffs2_alloc_raw_dirent(); ++ if (!rd) { ++ /* Argh. Now we treat it like a normal delete */ ++ jffs2_complete_reservation(c); ++ return -ENOMEM; ++ } ++ ++ down(&dir_f->sem); ++ ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_f->inocache->ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = ri->ino; ++ rd->mctime = ri->ctime; ++ rd->nsize = namelen; ++ rd->type = DT_REG; ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); ++ ++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); ++ ++ jffs2_free_raw_dirent(rd); ++ ++ if (IS_ERR(fd)) { ++ /* dirent failed to write. Delete the inode normally ++ as if it were the final unlink() */ ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ return PTR_ERR(fd); ++ } ++ ++ /* Link the fd into the inode's list, obsoleting an old ++ one if necessary. */ ++ jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ ++ return 0; ++} ++ ++ ++int jffs2_do_unlink(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, ++ const char *name, int namelen, struct jffs2_inode_info *dead_f) ++{ ++ struct jffs2_raw_dirent *rd; ++ struct jffs2_full_dirent *fd; ++ uint32_t alloclen, phys_ofs; ++ int ret; ++ ++ rd = jffs2_alloc_raw_dirent(); ++ if (!rd) ++ return -ENOMEM; ++ ++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_DELETION); ++ if (ret) { ++ jffs2_free_raw_dirent(rd); ++ return ret; ++ } ++ ++ down(&dir_f->sem); ++ ++ /* Build a deletion node */ ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_f->inocache->ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(0); ++ rd->mctime = cpu_to_je32(get_seconds()); ++ rd->nsize = namelen; ++ rd->type = DT_UNKNOWN; ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); ++ ++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_DELETION); ++ ++ jffs2_free_raw_dirent(rd); ++ ++ if (IS_ERR(fd)) { ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ return PTR_ERR(fd); ++ } ++ ++ /* File it. This will mark the old one obsolete. */ ++ jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ ++ up(&dir_f->sem); ++ ++ /* dead_f is NULL if this was a rename not a real unlink */ ++ /* Also catch the !f->inocache case, where there was a dirent ++ pointing to an inode which didn't exist. */ ++ if (dead_f && dead_f->inocache) { ++ ++ down(&dead_f->sem); ++ ++ while (dead_f->dents) { ++ /* There can be only deleted ones */ ++ fd = dead_f->dents; ++ ++ dead_f->dents = fd->next; ++ ++ if (fd->ino) { ++ printk(KERN_WARNING "Deleting inode #%u with active dentry \"%s\"->ino #%u\n", ++ dead_f->inocache->ino, fd->name, fd->ino); ++ } else { ++ D1(printk(KERN_DEBUG "Removing deletion dirent for \"%s\" from dir ino #%u\n", fd->name, dead_f->inocache->ino)); ++ } ++ jffs2_mark_node_obsolete(c, fd->raw); ++ jffs2_free_full_dirent(fd); ++ } ++ ++ dead_f->inocache->nlink--; ++ /* NB: Caller must set inode nlink if appropriate */ ++ up(&dead_f->sem); ++ } ++ ++ jffs2_complete_reservation(c); ++ ++ return 0; ++} ++ ++ ++int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint32_t ino, uint8_t type, const char *name, int namelen) ++{ ++ struct jffs2_raw_dirent *rd; ++ struct jffs2_full_dirent *fd; ++ uint32_t alloclen, phys_ofs; ++ int ret; ++ ++ rd = jffs2_alloc_raw_dirent(); ++ if (!rd) ++ return -ENOMEM; ++ ++ ret = jffs2_reserve_space(c, sizeof(*rd)+namelen, &phys_ofs, &alloclen, ALLOC_NORMAL); ++ if (ret) { ++ jffs2_free_raw_dirent(rd); ++ return ret; ++ } ++ ++ down(&dir_f->sem); ++ ++ /* Build a deletion node */ ++ rd->magic = cpu_to_je16(JFFS2_MAGIC_BITMASK); ++ rd->nodetype = cpu_to_je16(JFFS2_NODETYPE_DIRENT); ++ rd->totlen = cpu_to_je32(sizeof(*rd) + namelen); ++ rd->hdr_crc = cpu_to_je32(crc32(0, rd, sizeof(struct jffs2_unknown_node)-4)); ++ ++ rd->pino = cpu_to_je32(dir_f->inocache->ino); ++ rd->version = cpu_to_je32(++dir_f->highest_version); ++ rd->ino = cpu_to_je32(ino); ++ rd->mctime = cpu_to_je32(get_seconds()); ++ rd->nsize = namelen; ++ ++ rd->type = type; ++ ++ rd->node_crc = cpu_to_je32(crc32(0, rd, sizeof(*rd)-8)); ++ rd->name_crc = cpu_to_je32(crc32(0, name, namelen)); ++ ++ fd = jffs2_write_dirent(c, dir_f, rd, name, namelen, phys_ofs, ALLOC_NORMAL); ++ ++ jffs2_free_raw_dirent(rd); ++ ++ if (IS_ERR(fd)) { ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ return PTR_ERR(fd); ++ } ++ ++ /* File it. This will mark the old one obsolete. */ ++ jffs2_add_fd_to_list(c, fd, &dir_f->dents); ++ ++ jffs2_complete_reservation(c); ++ up(&dir_f->sem); ++ ++ return 0; ++} +diff -Nurb linux-mips-2.4.27/fs/jffs2/writev.c linux/fs/jffs2/writev.c +--- linux-mips-2.4.27/fs/jffs2/writev.c 1970-01-01 01:00:00.000000000 +0100 ++++ linux/fs/jffs2/writev.c 2004-11-19 10:25:12.134164072 +0100 +@@ -0,0 +1,50 @@ ++/* ++ * JFFS2 -- Journalling Flash File System, Version 2. ++ * ++ * Copyright (C) 2001, 2002 Red Hat, Inc. ++ * ++ * Created by David Woodhouse <dwmw2@redhat.com> ++ * ++ * For licensing information, see the file 'LICENCE' in this directory. ++ * ++ * $Id: writev.c,v 1.4 2003/10/04 08:33:07 dwmw2 Exp $ ++ * ++ */ ++ ++#include <linux/kernel.h> ++#include <linux/mtd/mtd.h> ++#include "nodelist.h" ++ ++/* This ought to be in core MTD code. All registered MTD devices ++ without writev should have this put in place. Bug the MTD ++ maintainer */ ++static inline int mtd_fake_writev(struct mtd_info *mtd, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen) ++{ ++ unsigned long i; ++ size_t totlen = 0, thislen; ++ int ret = 0; ++ ++ for (i=0; i<count; i++) { ++ if (!vecs[i].iov_len) ++ continue; ++ ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); ++ totlen += thislen; ++ if (ret || thislen != vecs[i].iov_len) ++ break; ++ to += vecs[i].iov_len; ++ } ++ if (retlen) ++ *retlen = totlen; ++ return ret; ++} ++ ++int jffs2_flash_direct_writev(struct jffs2_sb_info *c, const struct iovec *vecs, ++ unsigned long count, loff_t to, size_t *retlen) ++{ ++ if (c->mtd->writev) ++ return c->mtd->writev(c->mtd, vecs, count, to, retlen); ++ else ++ return mtd_fake_writev(c->mtd, vecs, count, to, retlen); ++} ++ +diff -Nurb linux-mips-2.4.27/include/linux/jffs2.h linux/include/linux/jffs2.h +--- linux-mips-2.4.27/include/linux/jffs2.h 2002-06-27 00:36:46.000000000 +0200 ++++ linux/include/linux/jffs2.h 2004-11-19 10:25:12.139163312 +0100 +@@ -1,50 +1,30 @@ + /* + * JFFS2 -- Journalling Flash File System, Version 2. + * +- * Copyright (C) 2001 Red Hat, Inc. ++ * Copyright (C) 2001-2003 Red Hat, Inc. + * +- * Created by David Woodhouse <dwmw2@cambridge.redhat.com> ++ * Created by David Woodhouse <dwmw2@redhat.com> + * +- * The original JFFS, from which the design for JFFS2 was derived, +- * was designed and implemented by Axis Communications AB. ++ * For licensing information, see the file 'LICENCE' in the ++ * jffs2 directory. + * +- * The contents of this file are subject to the Red Hat eCos Public +- * License Version 1.1 (the "Licence"); you may not use this file +- * except in compliance with the Licence. You may obtain a copy of +- * the Licence at http://www.redhat.com/ +- * +- * Software distributed under the Licence is distributed on an "AS IS" +- * basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. +- * See the Licence for the specific language governing rights and +- * limitations under the Licence. +- * +- * The Original Code is JFFS2 - Journalling Flash File System, version 2 +- * +- * Alternatively, the contents of this file may be used under the +- * terms of the GNU General Public License version 2 (the "GPL"), in +- * which case the provisions of the GPL are applicable instead of the +- * above. If you wish to allow the use of your version of this file +- * only under the terms of the GPL and not to allow others to use your +- * version of this file under the RHEPL, indicate your decision by +- * deleting the provisions above and replace them with the notice and +- * other provisions required by the GPL. If you do not delete the +- * provisions above, a recipient may use your version of this file +- * under either the RHEPL or the GPL. +- * +- * $Id: jffs2.h,v 1.19 2001/10/09 13:20:23 dwmw2 Exp $ ++ * $Id: jffs2.h,v 1.31 2003/10/04 08:33:05 dwmw2 Exp $ + * + */ + + #ifndef __LINUX_JFFS2_H__ + #define __LINUX_JFFS2_H__ + +-#include <asm/types.h> ++/* You must include something which defines the C99 uintXX_t types. ++ We don't do it from here because this file is used in too many ++ different environments. */ ++ + #define JFFS2_SUPER_MAGIC 0x72b6 + + /* Values we may expect to find in the 'magic' field */ + #define JFFS2_OLD_MAGIC_BITMASK 0x1984 + #define JFFS2_MAGIC_BITMASK 0x1985 +-#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */ ++#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */ + #define JFFS2_EMPTY_BITMASK 0xffff + #define JFFS2_DIRTY_BITMASK 0x0000 + +@@ -78,16 +58,12 @@ + #define JFFS2_NODETYPE_DIRENT (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 1) + #define JFFS2_NODETYPE_INODE (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 2) + #define JFFS2_NODETYPE_CLEANMARKER (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) ++#define JFFS2_NODETYPE_PADDING (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 4) + + // Maybe later... + //#define JFFS2_NODETYPE_CHECKPOINT (JFFS2_FEATURE_RWCOMPAT_DELETE | JFFS2_NODE_ACCURATE | 3) + //#define JFFS2_NODETYPE_OPTIONS (JFFS2_FEATURE_RWCOMPAT_COPY | JFFS2_NODE_ACCURATE | 4) + +-/* Same as the non_ECC versions, but with extra space for real +- * ECC instead of just the checksum. For use on NAND flash +- */ +-//#define JFFS2_NODETYPE_DIRENT_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 5) +-//#define JFFS2_NODETYPE_INODE_ECC (JFFS2_FEATURE_INCOMPAT | JFFS2_NODE_ACCURATE | 6) + + #define JFFS2_INO_FLAG_PREREAD 1 /* Do read_inode() for this one at + mount time, don't wait for it to +@@ -96,31 +72,79 @@ + compression type */ + + ++/* These can go once we've made sure we've caught all uses without ++ byteswapping */ ++ ++typedef struct { ++ uint32_t v32; ++} __attribute__((packed)) jint32_t; ++ ++typedef struct { ++ uint32_t m; ++} __attribute__((packed)) jmode_t; ++ ++typedef struct { ++ uint16_t v16; ++} __attribute__((packed)) jint16_t; ++ ++#define JFFS2_NATIVE_ENDIAN ++ ++/* Note we handle mode bits conversion from JFFS2 (i.e. Linux) to/from ++ whatever OS we're actually running on here too. */ ++ ++#if defined(JFFS2_NATIVE_ENDIAN) ++#define cpu_to_je16(x) ((jint16_t){x}) ++#define cpu_to_je32(x) ((jint32_t){x}) ++#define cpu_to_jemode(x) ((jmode_t){os_to_jffs2_mode(x)}) ++ ++#define je16_to_cpu(x) ((x).v16) ++#define je32_to_cpu(x) ((x).v32) ++#define jemode_to_cpu(x) (jffs2_to_os_mode((x).m)) ++#elif defined(JFFS2_BIG_ENDIAN) ++#define cpu_to_je16(x) ((jint16_t){cpu_to_be16(x)}) ++#define cpu_to_je32(x) ((jint32_t){cpu_to_be32(x)}) ++#define cpu_to_jemode(x) ((jmode_t){cpu_to_be32(os_to_jffs2_mode(x))}) ++ ++#define je16_to_cpu(x) (be16_to_cpu(x.v16)) ++#define je32_to_cpu(x) (be32_to_cpu(x.v32)) ++#define jemode_to_cpu(x) (be32_to_cpu(jffs2_to_os_mode((x).m))) ++#elif defined(JFFS2_LITTLE_ENDIAN) ++#define cpu_to_je16(x) ((jint16_t){cpu_to_le16(x)}) ++#define cpu_to_je32(x) ((jint32_t){cpu_to_le32(x)}) ++#define cpu_to_jemode(x) ((jmode_t){cpu_to_le32(os_to_jffs2_mode(x))}) ++ ++#define je16_to_cpu(x) (le16_to_cpu(x.v16)) ++#define je32_to_cpu(x) (le32_to_cpu(x.v32)) ++#define jemode_to_cpu(x) (le32_to_cpu(jffs2_to_os_mode((x).m))) ++#else ++#error wibble ++#endif ++ + struct jffs2_unknown_node + { + /* All start like this */ +- __u16 magic; +- __u16 nodetype; +- __u32 totlen; /* So we can skip over nodes we don't grok */ +- __u32 hdr_crc; ++ jint16_t magic; ++ jint16_t nodetype; ++ jint32_t totlen; /* So we can skip over nodes we don't grok */ ++ jint32_t hdr_crc; + } __attribute__((packed)); + + struct jffs2_raw_dirent + { +- __u16 magic; +- __u16 nodetype; /* == JFFS_NODETYPE_DIRENT */ +- __u32 totlen; +- __u32 hdr_crc; +- __u32 pino; +- __u32 version; +- __u32 ino; /* == zero for unlink */ +- __u32 mctime; +- __u8 nsize; +- __u8 type; +- __u8 unused[2]; +- __u32 node_crc; +- __u32 name_crc; +- __u8 name[0]; ++ jint16_t magic; ++ jint16_t nodetype; /* == JFFS_NODETYPE_DIRENT */ ++ jint32_t totlen; ++ jint32_t hdr_crc; ++ jint32_t pino; ++ jint32_t version; ++ jint32_t ino; /* == zero for unlink */ ++ jint32_t mctime; ++ uint8_t nsize; ++ uint8_t type; ++ uint8_t unused[2]; ++ jint32_t node_crc; ++ jint32_t name_crc; ++ uint8_t name[0]; + } __attribute__((packed)); + + /* The JFFS2 raw inode structure: Used for storage on physical media. */ +@@ -131,28 +155,28 @@ + */ + struct jffs2_raw_inode + { +- __u16 magic; /* A constant magic number. */ +- __u16 nodetype; /* == JFFS_NODETYPE_INODE */ +- __u32 totlen; /* Total length of this node (inc data, etc.) */ +- __u32 hdr_crc; +- __u32 ino; /* Inode number. */ +- __u32 version; /* Version number. */ +- __u32 mode; /* The file's type or mode. */ +- __u16 uid; /* The file's owner. */ +- __u16 gid; /* The file's group. */ +- __u32 isize; /* Total resultant size of this inode (used for truncations) */ +- __u32 atime; /* Last access time. */ +- __u32 mtime; /* Last modification time. */ +- __u32 ctime; /* Change time. */ +- __u32 offset; /* Where to begin to write. */ +- __u32 csize; /* (Compressed) data size */ +- __u32 dsize; /* Size of the node's data. (after decompression) */ +- __u8 compr; /* Compression algorithm used */ +- __u8 usercompr; /* Compression algorithm requested by the user */ +- __u16 flags; /* See JFFS2_INO_FLAG_* */ +- __u32 data_crc; /* CRC for the (compressed) data. */ +- __u32 node_crc; /* CRC for the raw inode (excluding data) */ +-// __u8 data[dsize]; ++ jint16_t magic; /* A constant magic number. */ ++ jint16_t nodetype; /* == JFFS_NODETYPE_INODE */ ++ jint32_t totlen; /* Total length of this node (inc data, etc.) */ ++ jint32_t hdr_crc; ++ jint32_t ino; /* Inode number. */ ++ jint32_t version; /* Version number. */ ++ jmode_t mode; /* The file's type or mode. */ ++ jint16_t uid; /* The file's owner. */ ++ jint16_t gid; /* The file's group. */ ++ jint32_t isize; /* Total resultant size of this inode (used for truncations) */ ++ jint32_t atime; /* Last access time. */ ++ jint32_t mtime; /* Last modification time. */ ++ jint32_t ctime; /* Change time. */ ++ jint32_t offset; /* Where to begin to write. */ ++ jint32_t csize; /* (Compressed) data size */ ++ jint32_t dsize; /* Size of the node's data. (after decompression) */ ++ uint8_t compr; /* Compression algorithm used */ ++ uint8_t usercompr; /* Compression algorithm requested by the user */ ++ jint16_t flags; /* See JFFS2_INO_FLAG_* */ ++ jint32_t data_crc; /* CRC for the (compressed) data. */ ++ jint32_t node_crc; /* CRC for the raw inode (excluding data) */ ++ uint8_t data[0]; + } __attribute__((packed)); + + union jffs2_node_union { +diff -Nurb linux-mips-2.4.27/include/linux/jffs2_fs_i.h linux/include/linux/jffs2_fs_i.h +--- linux-mips-2.4.27/include/linux/jffs2_fs_i.h 2001-10-19 03:25:03.000000000 +0200 ++++ linux/include/linux/jffs2_fs_i.h 2004-11-19 10:25:12.141163008 +0100 +@@ -1,22 +1,12 @@ +-/* $Id: jffs2_fs_i.h,v 1.8 2001/04/18 13:05:28 dwmw2 Exp $ */ ++/* $Id: jffs2_fs_i.h,v 1.16 2003/01/09 14:03:21 dwmw2 Exp $ */ + + #ifndef _JFFS2_FS_I + #define _JFFS2_FS_I + +-/* Include the pipe_inode_info at the beginning so that we can still +- use the storage space in the inode when we have a pipe inode. +- This sucks. +-*/ +- +-#undef THISSUCKS /* Only for 2.2 */ +-#ifdef THISSUCKS +-#include <linux/pipe_fs_i.h> +-#endif ++#include <linux/version.h> ++#include <linux/rbtree.h> + + struct jffs2_inode_info { +-#ifdef THISSUCKS +- struct pipe_inode_info pipecrap; +-#endif + /* We need an internal semaphore similar to inode->i_sem. + Unfortunately, we can't used the existing one, because + either the GC would deadlock, or we'd have to release it +@@ -26,10 +16,10 @@ + struct semaphore sem; + + /* The highest (datanode) version number used for this ino */ +- __u32 highest_version; ++ uint32_t highest_version; + + /* List of data fragments which make up the file */ +- struct jffs2_node_frag *fraglist; ++ struct rb_root fragtree; + + /* There may be one datanode which isn't referenced by any of the + above fragments, if it contains a metadata update but no actual +@@ -44,19 +34,13 @@ + /* Some stuff we just have to keep in-core at all times, for each inode. */ + struct jffs2_inode_cache *inocache; + +- /* Keep a pointer to the last physical node in the list. We don't +- use the doubly-linked lists because we don't want to increase +- the memory usage that much. This is simpler */ +- // struct jffs2_raw_node_ref *lastnode; +- __u16 flags; +- __u8 usercompr; +-}; +- +-#ifdef JFFS2_OUT_OF_KERNEL +-#define JFFS2_INODE_INFO(i) ((struct jffs2_inode_info *) &(i)->u) +-#else +-#define JFFS2_INODE_INFO(i) (&i->u.jffs2_i) ++ uint16_t flags; ++ uint8_t usercompr; ++#if !defined (__ECOS) ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) ++ struct inode vfs_inode; + #endif ++#endif ++}; + + #endif /* _JFFS2_FS_I */ +- +diff -Nurb linux-mips-2.4.27/include/linux/jffs2_fs_sb.h linux/include/linux/jffs2_fs_sb.h +--- linux-mips-2.4.27/include/linux/jffs2_fs_sb.h 2002-06-27 00:36:46.000000000 +0200 ++++ linux/include/linux/jffs2_fs_sb.h 2004-11-19 10:25:12.142162856 +0100 +@@ -1,19 +1,22 @@ +-/* $Id: jffs2_fs_sb.h,v 1.16.2.1 2002/02/23 14:13:34 dwmw2 Exp $ */ ++/* $Id: jffs2_fs_sb.h,v 1.45 2003/10/08 11:46:27 dwmw2 Exp $ */ + + #ifndef _JFFS2_FS_SB + #define _JFFS2_FS_SB + + #include <linux/types.h> + #include <linux/spinlock.h> ++#include <linux/workqueue.h> + #include <linux/completion.h> + #include <asm/semaphore.h> ++#include <linux/timer.h> ++#include <linux/wait.h> + #include <linux/list.h> + +-#define INOCACHE_HASHSIZE 1 +- + #define JFFS2_SB_FLAG_RO 1 + #define JFFS2_SB_FLAG_MOUNTING 2 + ++struct jffs2_inodirty; ++ + /* A struct for the overall file system control. Pointers to + jffs2_sb_info structs are named `c' in the source code. + Nee jffs_control +@@ -21,36 +24,46 @@ + struct jffs2_sb_info { + struct mtd_info *mtd; + +- __u32 highest_ino; ++ uint32_t highest_ino; ++ uint32_t checked_ino; ++ + unsigned int flags; +- spinlock_t nodelist_lock; + +- // pid_t thread_pid; /* GC thread's PID */ + struct task_struct *gc_task; /* GC task struct */ + struct semaphore gc_thread_start; /* GC thread start mutex */ + struct completion gc_thread_exit; /* GC thread exit completion port */ +- // __u32 gc_minfree_threshold; /* GC trigger thresholds */ +- // __u32 gc_maxdirty_threshold; + + struct semaphore alloc_sem; /* Used to protect all the following + fields, and also to protect against + out-of-order writing of nodes. + And GC. + */ +- __u32 flash_size; +- __u32 used_size; +- __u32 dirty_size; +- __u32 free_size; +- __u32 erasing_size; +- __u32 bad_size; +- __u32 sector_size; +- // __u32 min_free_size; +- // __u32 max_chunk_size; ++ uint32_t cleanmarker_size; /* Size of an _inline_ CLEANMARKER ++ (i.e. zero for OOB CLEANMARKER */ ++ ++ uint32_t flash_size; ++ uint32_t used_size; ++ uint32_t dirty_size; ++ uint32_t wasted_size; ++ uint32_t free_size; ++ uint32_t erasing_size; ++ uint32_t bad_size; ++ uint32_t sector_size; ++ uint32_t unchecked_size; ++ ++ uint32_t nr_free_blocks; ++ uint32_t nr_erasing_blocks; ++ ++ /* Number of free blocks there must be before we... */ ++ uint8_t resv_blocks_write; /* ... allow a normal filesystem write */ ++ uint8_t resv_blocks_deletion; /* ... allow a normal filesystem deletion */ ++ uint8_t resv_blocks_gctrigger; /* ... wake up the GC thread */ ++ uint8_t resv_blocks_gcbad; /* ... pick a block from the bad_list to GC */ ++ uint8_t resv_blocks_gcmerge; /* ... merge pages when garbage collecting */ + +- __u32 nr_free_blocks; +- __u32 nr_erasing_blocks; ++ uint32_t nospc_dirty_size; + +- __u32 nr_blocks; ++ uint32_t nr_blocks; + struct jffs2_eraseblock *blocks; /* The whole array of blocks. Used for getting blocks + * from the offset (blocks[ofs / sector_size]) */ + struct jffs2_eraseblock *nextblock; /* The block we're currently filling */ +@@ -58,9 +71,12 @@ + struct jffs2_eraseblock *gcblock; /* The block we're currently garbage-collecting */ + + struct list_head clean_list; /* Blocks 100% full of clean data */ ++ struct list_head very_dirty_list; /* Blocks with lots of dirty space */ + struct list_head dirty_list; /* Blocks with some dirty space */ ++ struct list_head erasable_list; /* Blocks which are completely dirty, and need erasing */ ++ struct list_head erasable_pending_wbuf_list; /* Blocks which need erasing but only after the current wbuf is flushed */ + struct list_head erasing_list; /* Blocks which are currently erasing */ +- struct list_head erase_pending_list; /* Blocks which need erasing */ ++ struct list_head erase_pending_list; /* Blocks which need erasing now */ + struct list_head erase_complete_list; /* Blocks which are erased and need the clean marker written to them */ + struct list_head free_list; /* Blocks which are free and ready to be used */ + struct list_head bad_list; /* Bad blocks. */ +@@ -69,16 +85,33 @@ + spinlock_t erase_completion_lock; /* Protect free_list and erasing_list + against erase completion handler */ + wait_queue_head_t erase_wait; /* For waiting for erases to complete */ +- struct jffs2_inode_cache *inocache_list[INOCACHE_HASHSIZE]; ++ ++ wait_queue_head_t inocache_wq; ++ struct jffs2_inode_cache **inocache_list; + spinlock_t inocache_lock; +-}; + +-#ifdef JFFS2_OUT_OF_KERNEL +-#define JFFS2_SB_INFO(sb) ((struct jffs2_sb_info *) &(sb)->u) +-#else +-#define JFFS2_SB_INFO(sb) (&sb->u.jffs2_sb) ++ /* Sem to allow jffs2_garbage_collect_deletion_dirent to ++ drop the erase_completion_lock while it's holding a pointer ++ to an obsoleted node. I don't like this. Alternatives welcomed. */ ++ struct semaphore erase_free_sem; ++ ++#ifdef CONFIG_JFFS2_FS_NAND ++ /* Write-behind buffer for NAND flash */ ++ unsigned char *wbuf; ++ uint32_t wbuf_ofs; ++ uint32_t wbuf_len; ++ uint32_t wbuf_pagesize; ++ struct jffs2_inodirty *wbuf_inodes; ++ ++ /* Information about out-of-band area usage... */ ++ struct nand_oobinfo *oobinfo; ++ uint32_t badblock_pos; ++ uint32_t fsdata_pos; ++ uint32_t fsdata_len; + #endif + +-#define OFNI_BS_2SFFJ(c) ((struct super_block *) ( ((char *)c) - ((char *)(&((struct super_block *)NULL)->u)) ) ) ++ /* OS-private pointer for getting back to master superblock info */ ++ void *os_priv; ++}; + + #endif /* _JFFS2_FB_SB */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/blktrans.h linux/include/linux/mtd/blktrans.h +--- linux-mips-2.4.27/include/linux/mtd/blktrans.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/mtd/blktrans.h 2004-11-19 10:25:12.037178816 +0100 +@@ -0,0 +1,72 @@ ++/* ++ * $Id: blktrans.h,v 1.5 2003/06/23 12:00:08 dwmw2 Exp $ ++ * ++ * (C) 2003 David Woodhouse <dwmw2@infradead.org> ++ * ++ * Interface to Linux block layer for MTD 'translation layers'. ++ * ++ */ ++ ++#ifndef __MTD_TRANS_H__ ++#define __MTD_TRANS_H__ ++ ++#include <asm/semaphore.h> ++ ++struct hd_geometry; ++struct mtd_info; ++struct mtd_blktrans_ops; ++struct file; ++struct inode; ++ ++struct mtd_blktrans_dev { ++ struct mtd_blktrans_ops *tr; ++ struct list_head list; ++ struct mtd_info *mtd; ++ struct semaphore sem; ++ int devnum; ++ int blksize; ++ unsigned long size; ++ int readonly; ++ void *blkcore_priv; /* gendisk in 2.5, devfs_handle in 2.4 */ ++}; ++ ++struct blkcore_priv; /* Differs for 2.4 and 2.5 kernels; private */ ++ ++struct mtd_blktrans_ops { ++ char *name; ++ int major; ++ int part_bits; ++ ++ /* Access functions */ ++ int (*readsect)(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buffer); ++ int (*writesect)(struct mtd_blktrans_dev *dev, ++ unsigned long block, char *buffer); ++ ++ /* Block layer ioctls */ ++ int (*getgeo)(struct mtd_blktrans_dev *dev, struct hd_geometry *geo); ++ int (*flush)(struct mtd_blktrans_dev *dev); ++ ++ /* Called with mtd_table_mutex held; no race with add/remove */ ++ int (*open)(struct mtd_blktrans_dev *dev); ++ int (*release)(struct mtd_blktrans_dev *dev); ++ ++ /* Called on {de,}registration and on subsequent addition/removal ++ of devices, with mtd_table_mutex held. */ ++ void (*add_mtd)(struct mtd_blktrans_ops *tr, struct mtd_info *mtd); ++ void (*remove_dev)(struct mtd_blktrans_dev *dev); ++ ++ struct list_head devs; ++ struct list_head list; ++ struct module *owner; ++ ++ struct mtd_blkcore_priv *blkcore_priv; ++}; ++ ++extern int register_mtd_blktrans(struct mtd_blktrans_ops *tr); ++extern int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr); ++extern int add_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); ++extern int del_mtd_blktrans_dev(struct mtd_blktrans_dev *dev); ++ ++ ++#endif /* __MTD_TRANS_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/cfi.h linux/include/linux/mtd/cfi.h +--- linux-mips-2.4.27/include/linux/mtd/cfi.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/cfi.h 2004-11-19 10:25:12.038178664 +0100 +@@ -1,13 +1,14 @@ + + /* Common Flash Interface structures + * See http://support.intel.com/design/flash/technote/index.htm +- * $Id: cfi.h,v 1.32 2002/09/05 05:15:32 acurtis Exp $ ++ * $Id: cfi.h,v 1.38 2003/11/08 00:51:21 dsaxena Exp $ + */ + + #ifndef __MTD_CFI_H__ + #define __MTD_CFI_H__ + + #include <linux/config.h> ++#include <linux/version.h> + #include <linux/delay.h> + #include <linux/types.h> + #include <linux/interrupt.h> +@@ -260,7 +261,8 @@ + __u8 pri[3]; + __u8 MajorVersion; + __u8 MinorVersion; +- __u32 FeatureSupport; ++ __u32 FeatureSupport; /* if bit 31 is set then an additional __u32 feature ++ block follows - FIXME - not currently supported */ + __u8 SuspendCmdSupport; + __u16 BlkStatusRegMask; + __u8 VccOptimal; +@@ -271,6 +273,25 @@ + __u8 UserProtRegSize; + } __attribute__((packed)); + ++/* Vendor-Specific PRI for AMD/Fujitsu Extended Command Set (0x0002) */ ++ ++struct cfi_pri_amdstd { ++ __u8 pri[3]; ++ __u8 MajorVersion; ++ __u8 MinorVersion; ++ __u8 SiliconRevision; /* bits 1-0: Address Sensitive Unlock */ ++ __u8 EraseSuspend; ++ __u8 BlkProt; ++ __u8 TmpBlkUnprotect; ++ __u8 BlkProtUnprot; ++ __u8 SimultaneousOps; ++ __u8 BurstMode; ++ __u8 PageMode; ++ __u8 VppMin; ++ __u8 VppMax; ++ __u8 TopBottom; ++} __attribute__((packed)); ++ + struct cfi_pri_query { + __u8 NumFields; + __u32 ProtField[1]; /* Not host ordered */ +@@ -314,8 +335,6 @@ + struct flchip chips[0]; /* per-chip data structure for each chip */ + }; + +-#define MAX_CFI_CHIPS 8 /* Entirely arbitrary to avoid realloc() */ +- + /* + * Returns the command address according to the given geometry. + */ +@@ -387,13 +406,13 @@ + static inline cfi_word cfi_read(struct map_info *map, __u32 addr) + { + if (cfi_buswidth_is_1()) { +- return map->read8(map, addr); ++ return map_read8(map, addr); + } else if (cfi_buswidth_is_2()) { +- return map->read16(map, addr); ++ return map_read16(map, addr); + } else if (cfi_buswidth_is_4()) { +- return map->read32(map, addr); ++ return map_read32(map, addr); + } else if (cfi_buswidth_is_8()) { +- return map->read64(map, addr); ++ return map_read64(map, addr); + } else { + return 0; + } +@@ -406,13 +425,13 @@ + static inline void cfi_write(struct map_info *map, cfi_word val, __u32 addr) + { + if (cfi_buswidth_is_1()) { +- map->write8(map, val, addr); ++ map_write8(map, val, addr); + } else if (cfi_buswidth_is_2()) { +- map->write16(map, val, addr); ++ map_write16(map, val, addr); + } else if (cfi_buswidth_is_4()) { +- map->write32(map, val, addr); ++ map_write32(map, val, addr); + } else if (cfi_buswidth_is_8()) { +- map->write64(map, val, addr); ++ map_write64(map, val, addr); + } + } + +@@ -443,13 +462,13 @@ + static inline __u8 cfi_read_query(struct map_info *map, __u32 addr) + { + if (cfi_buswidth_is_1()) { +- return map->read8(map, addr); ++ return map_read8(map, addr); + } else if (cfi_buswidth_is_2()) { +- return cfi16_to_cpu(map->read16(map, addr)); ++ return cfi16_to_cpu(map_read16(map, addr)); + } else if (cfi_buswidth_is_4()) { +- return cfi32_to_cpu(map->read32(map, addr)); ++ return cfi32_to_cpu(map_read32(map, addr)); + } else if (cfi_buswidth_is_8()) { +- return cfi64_to_cpu(map->read64(map, addr)); ++ return cfi64_to_cpu(map_read64(map, addr)); + } else { + return 0; + } +@@ -479,5 +498,19 @@ + spin_unlock_bh(mutex); + } + ++struct cfi_extquery *cfi_read_pri(struct map_info *map, __u16 adr, __u16 size, ++ const char* name); ++ ++struct cfi_fixup { ++ __u16 mfr; ++ __u16 id; ++ void (*fixup)(struct map_info *map, void* param); ++ void* param; ++}; ++ ++#define CFI_MFR_ANY 0xffff ++#define CFI_ID_ANY 0xffff ++ ++void cfi_fixup(struct map_info *map, struct cfi_fixup* fixups); + + #endif /* __MTD_CFI_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/compatmac.h linux/include/linux/mtd/compatmac.h +--- linux-mips-2.4.27/include/linux/mtd/compatmac.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/compatmac.h 2004-11-19 10:25:12.041178208 +0100 +@@ -1,573 +1,152 @@ +- + /* +- * mtd/include/compatmac.h +- * +- * $Id: compatmac.h,v 1.45 2003/01/24 15:50:57 dwmw2 Exp $ ++ * $Id: compatmac.h,v 1.63 2003/11/14 19:50:04 thayne Exp $ + * + * Extensions and omissions from the normal 'linux/compatmac.h' + * files. hopefully this will end up empty as the 'real' one + * becomes fully-featured. + */ + +- +-/* First, include the parts which the kernel is good enough to provide +- * to us +- */ +- + #ifndef __LINUX_MTD_COMPATMAC_H__ + #define __LINUX_MTD_COMPATMAC_H__ + +-#include <linux/config.h> +-#include <linux/module.h> +-#ifndef LINUX_VERSION_CODE + #include <linux/version.h> +-#endif +- +-#ifndef VERSION_CODE +-# define VERSION_CODE(vers,rel,seq) ( ((vers)<<16) | ((rel)<<8) | (seq) ) +-#endif +-#ifndef KERNEL_VERSION +-# define KERNEL_VERSION(a,b,c) VERSION_CODE(a,b,c) +-#endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0) +-# error "This kernel is too old: not supported by this file" +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,0) +-#include <linux/types.h> /* used later in this header */ +- +-#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c)) +-#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c)) +- +-typedef struct wait_queue * wait_queue_head_t; +- +-#define DECLARE_WAITQUEUE(x,y) struct wait_queue x = {y,NULL} +-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL +-#define init_waitqueue_head init_waitqueue +-#define DECLARE_MUTEX(x) struct semaphore x = MUTEX +-#define DECLARE_MUTEX_LOCKED(x) struct semaphore x = MUTEX_LOCKED +- +-/* from sysdep-2.1.h */ +-# include <asm/segment.h> +-# define access_ok(t,a,sz) (verify_area((t),(a),(sz)) ? 0 : 1) +-# define verify_area_20 verify_area +-# define copy_to_user(t,f,n) (memcpy_tofs(t,f,n), 0) +-# define __copy_to_user(t,f,n) copy_to_user((t),(f),(n)) +-# define copy_to_user_ret(t,f,n,r) copy_to_user((t),(f),(n)) +-# define copy_from_user(t,f,n) (memcpy_fromfs((t),(f),(n)), 0) +-# define __copy_from_user(t,f,n) copy_from_user((t),(f),(n)) +-# define copy_from_user_ret(t,f,n,r) copy_from_user((t),(f),(n)) +-//xxx # define PUT_USER(val,add) (put_user((val),(add)), 0) +-# define Put_user(val,add) (put_user((val),(add)), 0) +-# define __PUT_USER(val,add) PUT_USER((val),(add)) +-# define PUT_USER_RET(val,add,ret) PUT_USER((val),(add)) +-# define GET_USER(dest,add) ((dest)=get_user((add)), 0) +-# define __GET_USER(dest,add) GET_USER((dest),(add)) +-# define GET_USER_RET(dest,add,ret) GET_USER((dest),(add)) +- +-#define ioremap(offset,size) vremap(offset,size) +-#define iounmap(adr) /* */ +- +-#define EXPORT_SYMBOL(s) /* */ +-#define EXPORT_SYMBOL_NOVERS(s) /* */ +- +-/* 2.1.10 and 2.1.43 introduced new functions. They are worth using */ +- +-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,10) +- +-# include <asm/byteorder.h> +-# ifdef __LITTLE_ENDIAN +-# define cpu_to_le16(x) (x) +-# define cpu_to_le32(x) (x) +-# define cpu_to_be16(x) htons((x)) +-# define cpu_to_be32(x) htonl((x)) +-# else +-# define cpu_to_be16(x) (x) +-# define cpu_to_be32(x) (x) +- extern inline __u16 cpu_to_le16(__u16 x) { return (x<<8) | (x>>8);} +- extern inline __u32 cpu_to_le32(__u32 x) { return((x>>24) | +- ((x>>8)&0xff00) | ((x<<8)&0xff0000) | (x<<24));} +-# endif +- +-# define le16_to_cpu(x) cpu_to_le16(x) +-# define le32_to_cpu(x) cpu_to_le32(x) +-# define be16_to_cpu(x) cpu_to_be16(x) +-# define be32_to_cpu(x) cpu_to_be32(x) +- +-#endif +- +-#if LINUX_VERSION_CODE < VERSION_CODE(2,1,43) +-# define cpu_to_le16p(addr) (cpu_to_le16(*(addr))) +-# define cpu_to_le32p(addr) (cpu_to_le32(*(addr))) +-# define cpu_to_be16p(addr) (cpu_to_be16(*(addr))) +-# define cpu_to_be32p(addr) (cpu_to_be32(*(addr))) +- +- extern inline void cpu_to_le16s(__u16 *a) {*a = cpu_to_le16(*a);} +- extern inline void cpu_to_le32s(__u16 *a) {*a = cpu_to_le32(*a);} +- extern inline void cpu_to_be16s(__u16 *a) {*a = cpu_to_be16(*a);} +- extern inline void cpu_to_be32s(__u16 *a) {*a = cpu_to_be32(*a);} +- +-# define le16_to_cpup(x) cpu_to_le16p(x) +-# define le32_to_cpup(x) cpu_to_le32p(x) +-# define be16_to_cpup(x) cpu_to_be16p(x) +-# define be32_to_cpup(x) cpu_to_be32p(x) +- +-# define le16_to_cpus(x) cpu_to_le16s(x) +-# define le32_to_cpus(x) cpu_to_le32s(x) +-# define be16_to_cpus(x) cpu_to_be16s(x) +-# define be32_to_cpus(x) cpu_to_be32s(x) +-#endif +- +-// from 2.2, linux/types.h +-#ifndef __BIT_TYPES_DEFINED__ +-#define __BIT_TYPES_DEFINED__ +- +-typedef __u8 u_int8_t; +-typedef __s8 int8_t; +-typedef __u16 u_int16_t; +-typedef __s16 int16_t; +-typedef __u32 u_int32_t; +-typedef __s32 int32_t; +- +-#endif /* !(__BIT_TYPES_DEFINED__) */ +- +-#if (__GNUC__ > 2) || (__GNUC__ == 2 && __GNUC_MINOR__ >= 8) +- typedef struct { } spinlock_t; +- #define SPIN_LOCK_UNLOCKED (spinlock_t) { } +-#else +- typedef struct { int gcc_is_buggy; } spinlock_t; +- #define SPIN_LOCK_UNLOCKED (spinlock_t) { 0 } +-#endif +- +-#define spin_lock_init(lock) do { } while(0) +-#define spin_lock(lock) (void)(lock) /* Not "unused variable". */ +-#define spin_trylock(lock) (1) +-#define spin_unlock_wait(lock) do { } while(0) +-#define spin_unlock(lock) do { } while(0) +-#define spin_lock_irq(lock) cli() +-#define spin_unlock_irq(lock) sti() +- +-#define spin_lock_irqsave(lock, flags) \ +- do { save_flags(flags); cli(); } while (0) +-#define spin_unlock_irqrestore(lock, flags) \ +- restore_flags(flags) +- +-// Doesn't work when tqueue.h is included. +-// #define queue_task queue_task_irq_off +-#define tty_flip_buffer_push(tty) queue_task_irq_off(&tty->flip.tqueue, &tq_timer) +-#define signal_pending(current) (current->signal & ~current->blocked) +-#define schedule_timeout(to) do {current->timeout = jiffies + (to);schedule ();} while (0) +-#define time_after(t1,t2) (((long)t1-t2) > 0) +- +-#else +- #include <linux/compatmac.h> +-#endif // LINUX_VERSION_CODE < 0x020100 +- +- +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +-#include <linux/vmalloc.h> +-#endif +- +-/* Modularization issues */ +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,18) +-# define __USE_OLD_SYMTAB__ +-# define EXPORT_NO_SYMBOLS register_symtab(NULL); +-# define REGISTER_SYMTAB(tab) register_symtab(tab) +-#else +-# define REGISTER_SYMTAB(tab) /* nothing */ +-#endif +- +-#ifdef __USE_OLD_SYMTAB__ +-# define __MODULE_STRING(s) /* nothing */ +-# define MODULE_PARM(v,t) /* nothing */ +-# define MODULE_PARM_DESC(v,t) /* nothing */ +-# define MODULE_AUTHOR(n) /* nothing */ +-# define MODULE_DESCRIPTION(d) /* nothing */ +-# define MODULE_SUPPORTED_DEVICE(n) /* nothing */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) ++#error "This kernel is too old: not supported by this file" + #endif + +-/* +- * "select" changed in 2.1.23. The implementation is twin, but this +- * header is new +- */ +-#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,22) +-# include <linux/poll.h> +-#else +-# define __USE_OLD_SELECT__ +-#endif ++ /* O(1) scheduler stuff. */ + +-/* Other change in the fops are solved using pseudo-types */ +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) +-# define lseek_t long long +-# define lseek_off_t long long +-#else +-# define lseek_t int +-# define lseek_off_t off_t +-#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) && !defined(__rh_config_h__) ++#include <linux/sched.h> ++static inline void __recalc_sigpending(void) ++{ ++ recalc_sigpending(current); ++} ++#undef recalc_sigpending ++#define recalc_sigpending() __recalc_sigpending () + +-/* changed the prototype of read/write */ ++#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0) + +-#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) || defined(__alpha__) +-# define count_t unsigned long +-# define read_write_t long +-#else +-# define count_t int +-# define read_write_t int + #endif + + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,31) +-# define release_t void +-# define release_return(x) return +-#else +-# define release_t int +-# define release_return(x) return (x) +-#endif +- +-#if LINUX_VERSION_CODE < 0x20300 +-#define __exit +-#endif +-#if LINUX_VERSION_CODE < 0x20200 +-#define __init +-#else +-#include <linux/init.h> +-#endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +-#define init_MUTEX(x) do {*(x) = MUTEX;} while (0) +-#define init_MUTEX_LOCKED(x) do {*(x) = MUTEX_LOCKED;} while (0) +-#endif ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,20) + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +-#define RQFUNC_ARG void +-#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) +-#else +-#define RQFUNC_ARG request_queue_t *q ++#ifndef yield ++#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,32) +-#define blk_cleanup_queue(nr) do {blk_dev[nr].request_fn = 0;} while(0) +-#define BLK_DEFAULT_QUEUE(nr) (blk_dev[nr].request_fn) +-#define blk_init_queue(q, rq) do {q = rq;} while(0) ++#ifndef minor ++#define major(d) (MAJOR(to_kdev_t(d))) ++#define minor(d) (MINOR(to_kdev_t(d))) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +-#ifdef CONFIG_MODULES +-#define __MOD_INC_USE_COUNT(mod) \ +- (atomic_inc(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED|MOD_USED_ONCE) +-#define __MOD_DEC_USE_COUNT(mod) \ +- (atomic_dec(&(mod)->uc.usecount), (mod)->flags |= MOD_VISITED) +-#else +-#define __MOD_INC_USE_COUNT(mod) +-#define __MOD_DEC_USE_COUNT(mod) +-#endif ++#ifndef mk_kdev ++#define mk_kdev(ma,mi) MKDEV(ma,mi) ++#define kdev_t_to_nr(x) (x) + #endif + ++#define need_resched() (current->need_resched) ++#define cond_resched() do { if need_resched() { yield(); } } while(0) + +-#ifndef HAVE_INTER_MODULE +-static inline void *inter_module_get(char *x) {return NULL;} +-static inline void *inter_module_get_request(char *x, char *y) {return NULL;} +-static inline void inter_module_put(const char *x) {} +-static inline void inter_module_register(const char *x, struct module *y, const void *z) {} +-static inline void inter_module_unregister(const char *x) {} ++#endif /* < 2.4.20 */ ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,73) ++#define iminor(i) minor((i)->i_rdev) ++#define imajor(i) major((i)->i_rdev) ++#define old_encode_dev(d) ( (major(d)<<8) | minor(d) ) ++#define old_decode_dev(rdev) (kdev_t_to_nr(mk_kdev((rdev)>>8, (rdev)&0xff))) ++#define old_valid_dev(d) (1) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,61) + +-#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL +-#define init_waitqueue_head init_waitqueue ++#include <linux/sched.h> + ++#ifdef __rh_config_h__ ++#define sigmask_lock sighand->siglock ++#define sig sighand + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +- +-static inline int try_inc_mod_count(struct module *mod) ++static inline void __daemonize_modvers(void) + { +-#ifdef CONFIG_MODULES +- if (mod) +- __MOD_INC_USE_COUNT(mod); +-#endif +- return 1; +-} +-#endif ++ daemonize(); + ++ spin_lock_irq(¤t->sigmask_lock); ++ sigfillset(¤t->blocked); ++ recalc_sigpending(); ++ spin_unlock_irq(¤t->sigmask_lock); ++} ++#undef daemonize ++#define daemonize(fmt, ...) do { \ ++ snprintf(current->comm, sizeof(current->comm), fmt ,##__VA_ARGS__); \ ++ __daemonize_modvers(); \ ++ } while(0) + +-/* Yes, I'm aware that it's a fairly ugly hack. +- Until the __constant_* macros appear in Linus' own kernels, this is +- the way it has to be done. +- DW 19/1/00 +- */ +- +-#include <asm/byteorder.h> +- +-#ifndef __constant_cpu_to_le16 +- +-#ifdef __BIG_ENDIAN +-#define __constant_cpu_to_le64(x) ___swab64((x)) +-#define __constant_le64_to_cpu(x) ___swab64((x)) +-#define __constant_cpu_to_le32(x) ___swab32((x)) +-#define __constant_le32_to_cpu(x) ___swab32((x)) +-#define __constant_cpu_to_le16(x) ___swab16((x)) +-#define __constant_le16_to_cpu(x) ___swab16((x)) +-#define __constant_cpu_to_be64(x) ((__u64)(x)) +-#define __constant_be64_to_cpu(x) ((__u64)(x)) +-#define __constant_cpu_to_be32(x) ((__u32)(x)) +-#define __constant_be32_to_cpu(x) ((__u32)(x)) +-#define __constant_cpu_to_be16(x) ((__u16)(x)) +-#define __constant_be16_to_cpu(x) ((__u16)(x)) +-#else +-#ifdef __LITTLE_ENDIAN +-#define __constant_cpu_to_le64(x) ((__u64)(x)) +-#define __constant_le64_to_cpu(x) ((__u64)(x)) +-#define __constant_cpu_to_le32(x) ((__u32)(x)) +-#define __constant_le32_to_cpu(x) ((__u32)(x)) +-#define __constant_cpu_to_le16(x) ((__u16)(x)) +-#define __constant_le16_to_cpu(x) ((__u16)(x)) +-#define __constant_cpu_to_be64(x) ___swab64((x)) +-#define __constant_be64_to_cpu(x) ___swab64((x)) +-#define __constant_cpu_to_be32(x) ___swab32((x)) +-#define __constant_be32_to_cpu(x) ___swab32((x)) +-#define __constant_cpu_to_be16(x) ___swab16((x)) +-#define __constant_be16_to_cpu(x) ___swab16((x)) +-#else +-#error No (recognised) endianness defined (unless it,s PDP) +-#endif /* __LITTLE_ENDIAN */ +-#endif /* __BIG_ENDIAN */ +- +-#endif /* ifndef __constant_cpu_to_le16 */ +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) +- #define mod_init_t int __init +- #define mod_exit_t void +-#else +- #define mod_init_t static int __init +- #define mod_exit_t static void __exit +-#endif +- +-#ifndef THIS_MODULE +-#ifdef MODULE +-#define THIS_MODULE (&__this_module) +-#else +-#define THIS_MODULE (NULL) +-#endif +-#endif +- +-#if LINUX_VERSION_CODE < 0x20300 +-#include <linux/interrupt.h> +-#define spin_lock_bh(lock) do {start_bh_atomic();spin_lock(lock);}while(0) +-#define spin_unlock_bh(lock) do {spin_unlock(lock);end_bh_atomic();}while(0) +-#else +-#include <asm/softirq.h> +-#include <linux/spinlock.h> +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) +-#define set_current_state(state_value) \ +- do { current->state = (state_value); } while (0) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) +-static inline int invalidate_device(kdev_t dev, int do_sync) { ++static inline int dequeue_signal_lock(struct task_struct *tsk, sigset_t *mask, siginfo_t *info) ++{ ++ unsigned long flags; ++ unsigned long ret; + +- if (do_sync) +- fsync_dev(dev); ++ spin_lock_irqsave(¤t->sigmask_lock, flags); ++ ret = dequeue_signal(mask, info); ++ spin_unlock_irqrestore(¤t->sigmask_lock, flags); + +- invalidate_buffers(dev); +- return 0; ++ return ret; + } +-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,5) +-static inline int invalidate_device(kdev_t dev, int do_sync) { +- struct super_block *sb = get_super(dev); +- int res = 0; + +- if (do_sync) +- fsync_dev(dev); +- +- if (sb) +- res = invalidate_inodes(sb); ++static inline int allow_signal(int sig) ++{ ++ if (sig < 1 || sig > _NSIG) ++ return -EINVAL; + +- invalidate_buffers(dev); +- return res; ++ spin_lock_irq(¤t->sigmask_lock); ++ sigdelset(¤t->blocked, sig); ++ recalc_sigpending(); ++ /* Make sure the kernel neither eats it now converts to SIGKILL */ ++ current->sig->action[sig-1].sa.sa_handler = (void *)2; ++ spin_unlock_irq(¤t->sigmask_lock); ++ return 0; + } +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) +-#undef min +-#undef max +-#undef min_t +-#undef max_t +-/* +- * min()/max() macros that also do +- * strict type-checking.. See the +- * "unnecessary" pointer comparison. +- */ +-#define min(x,y) ({ \ +- const typeof(x) _x = (x); \ +- const typeof(y) _y = (y); \ +- (void) (&_x == &_y); \ +- _x < _y ? _x : _y; }) +- +-#define max(x,y) ({ \ +- const typeof(x) _x = (x); \ +- const typeof(y) _y = (y); \ +- (void) (&_x == &_y); \ +- _x > _y ? _x : _y; }) +- +-/* +- * ..and if you can't take the strict +- * types, you can specify one yourself. +- * +- * Or not use min/max at all, of course. +- */ +-#define min_t(type,x,y) \ +- ({ type __x = (x); type __y = (y); __x < __y ? __x: __y; }) +-#define max_t(type,x,y) \ +- ({ type __x = (x); type __y = (y); __x > __y ? __x: __y; }) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,7) +-struct completion { +- struct semaphore s; +-}; +- +-#define complete(c) up(&(c)->s) +-#define wait_for_completion(c) down(&(c)->s) +-#define init_completion(c) init_MUTEX_LOCKED(&(c)->s); +- +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) +-/* This came later */ +-#define complete_and_exit(c, r) do { complete(c); do_exit(r); } while(0) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \ +- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__)) ++static inline int disallow_signal(int sig) ++{ ++ if (sig < 1 || sig > _NSIG) ++ return -EINVAL; + +-#include <linux/genhd.h> ++ spin_lock_irq(¤t->sigmask_lock); ++ sigaddset(¤t->blocked, sig); ++ recalc_sigpending(); + +-static inline void add_gendisk(struct gendisk *gp) +-{ +- gp->next = gendisk_head; +- gendisk_head = gp; ++ current->sig->action[sig-1].sa.sa_handler = SIG_DFL; ++ spin_unlock_irq(¤t->sigmask_lock); ++ return 0; + } + +-static inline void del_gendisk(struct gendisk *gp) +-{ +- struct gendisk *gd, **gdp; ++#undef sighand ++#undef sigmask_lock + +- for (gdp = &gendisk_head; *gdp; gdp = &((*gdp)->next)) +- if (*gdp == gp) { +- gd = *gdp; *gdp = gd->next; +- break; +- } +-} ++#define PF_FREEZE 0 ++#define refrigerator(x) do { ; } while(0) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,18) && defined(MODULE) ++ /* Module bits */ + +-#define module_init(func) \ +-mod_init_t init_module(void) { \ +- return func(); \ +-} + +-#define module_exit(func) \ +-mod_exit_t cleanup_module(void) { \ +- return func(); \ +-} ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,60) ++#define try_module_get(m) try_inc_mod_count(m) ++#define __module_get(m) do { if (!try_inc_mod_count(m)) BUG(); } while(0) ++#define module_put(m) do { if (m) __MOD_DEC_USE_COUNT((struct module *)(m)); } while(0) ++#define set_module_owner(x) do { x->owner = THIS_MODULE; } while(0) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,9) || \ +- (LINUX_VERSION_CODE < KERNEL_VERSION(2,4,10) && !defined(__rh_config_h__)) +-#define MODULE_LICENSE(x) /* */ +-#endif + +-/* Removed for 2.4.21 kernel. This really should have been renamed +- when it was changed -- this is a PITA */ +-#if 0 && LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) +-#include <linux/sched.h> +-static inline void __recalc_sigpending(void) +-{ +- recalc_sigpending(current); +-} +-#undef recalc_sigpending +-#define recalc_sigpending() __recalc_sigpending () +-#endif ++ /* Random filesystem stuff, only for JFFS2 really */ + + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,5) + #define parent_ino(d) ((d)->d_parent->d_inode->i_ino) + #endif + +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,3) +-#define need_resched() (current->need_resched) +-#define cond_resched() do { if need_resched() schedule(); } while(0) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,19) +-#ifndef yield +-#define yield() do { set_current_state(TASK_RUNNING); schedule(); } while(0) +-#endif +-#ifndef minor +-#define major(d) (MAJOR(to_kdev_t(d))) +-#define minor(d) (MINOR(to_kdev_t(d))) +-#endif +-#ifndef mk_kdev +-#define mk_kdev(ma,mi) MKDEV(ma,mi) +-#define kdev_t_to_nr(x) (x) +-#endif +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,0) +- /* Is this right? */ +-#define set_user_nice(tsk, n) do { (tsk)->priority = 20-(n); } while(0) +-#elif LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) && !defined(RED_HAT_LINUX_KERNEL) +-#define set_user_nice(tsk, n) do { (tsk)->nice = n; } while(0) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,21) +-#define rq_data_dir(x) ((x)->cmd) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,0) +- +-#define IS_REQ_CMD(req) (1) +- +-#define QUEUE_LOCK(q) (&io_request_lock) +- +-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req)) +- +-#else /* > 2.5.0 */ +- +-#define IS_REQ_CMD(req) ((req)->flags & REQ_CMD) +- +-#define QUEUE_LOCK(q) ((q)->queue_lock) +- +-#define BLK_INIT_QUEUE(q, req, lock) blk_init_queue((q), (req), (lock)) +- +-#endif +- +-/* Removed cos it broke stuff. Where is this required anyway? +- * #ifndef QUEUE_EMPTY +- * #define QUEUE_EMPTY (!CURRENT) +- * #endif +- */ +-#if LINUX_VERSION_CODE < 0x20300 +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].plug_tq.sync) +-#elif LINUX_VERSION_CODE < 0x20500 //FIXME (Si) +-#define QUEUE_PLUGGED (blk_dev[MAJOR_NR].request_queue.plugged) +-#else +-#define QUEUE_PLUGGED (blk_queue_plugged(QUEUE)) +-#endif +- +-#if LINUX_VERSION_CODE < KERNEL_VERSION(2,4,14) +-#define BLK_INC_USE_COUNT MOD_INC_USE_COUNT +-#define BLK_DEC_USE_COUNT MOD_DEC_USE_COUNT +-#else +-#define BLK_INC_USE_COUNT do {} while(0) +-#define BLK_DEC_USE_COUNT do {} while(0) +-#endif +- + #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,12) + #define PageUptodate(x) Page_Uptodate(x) + #endif +@@ -580,4 +159,31 @@ + #define generic_file_readonly_mmap generic_file_mmap + #endif + ++#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,70) ++ ++#include <linux/kmod.h> ++#include <linux/string.h> ++ ++static inline char *strlcpy(char *dest, const char *src, int len) ++{ ++ dest[len-1] = 0; ++ return strncpy(dest, src, len-1); ++} ++ ++static inline int do_old_request_module(const char *mod) ++{ ++ return request_module(mod); ++} ++#undef request_module ++#define request_module(fmt, ...) \ ++ ({ char modname[32]; snprintf(modname, 31, fmt ,##__VA_ARGS__); do_old_request_module(modname); }) ++ ++#endif /* 2.5.70 */ ++ ++#ifndef container_of ++#define container_of(ptr, type, member) ({ \ ++ const typeof( ((type *)0)->member ) *__mptr = (ptr); \ ++ (type *)( (char *)__mptr - offsetof(type,member) );}) ++#endif ++ + #endif /* __LINUX_MTD_COMPATMAC_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/doc2000.h linux/include/linux/mtd/doc2000.h +--- linux-mips-2.4.27/include/linux/mtd/doc2000.h 2001-11-05 21:16:30.000000000 +0100 ++++ linux/include/linux/mtd/doc2000.h 2004-11-19 10:25:12.044177752 +0100 +@@ -1,13 +1,21 @@ +- +-/* Linux driver for Disk-On-Chip 2000 */ +-/* (c) 1999 Machine Vision Holdings, Inc. */ +-/* Author: David Woodhouse <dwmw2@mvhi.com> */ +-/* $Id: doc2000.h,v 1.15 2001/09/19 00:22:15 dwmw2 Exp $ */ ++/* ++ * Linux driver for Disk-On-Chip devices ++ * ++ * Copyright (C) 1999 Machine Vision Holdings, Inc. ++ * Copyright (C) 2001-2003 David Woodhouse <dwmw2@infradead.org> ++ * Copyright (C) 2002-2003 Greg Ungerer <gerg@snapgear.com> ++ * Copyright (C) 2002-2003 SnapGear Inc ++ * ++ * $Id: doc2000.h,v 1.22 2003/11/05 10:51:36 dwmw2 Exp $ ++ * ++ * Released under GPL ++ */ + + #ifndef __MTD_DOC2000_H__ + #define __MTD_DOC2000_H__ + + #include <linux/mtd/mtd.h> ++#include <asm/semaphore.h> + + #define DoC_Sig1 0 + #define DoC_Sig2 1 +@@ -38,18 +46,47 @@ + #define DoC_Mil_CDSN_IO 0x0800 + #define DoC_2k_CDSN_IO 0x1800 + ++#define DoC_Mplus_NOP 0x1002 ++#define DoC_Mplus_AliasResolution 0x1004 ++#define DoC_Mplus_DOCControl 0x1006 ++#define DoC_Mplus_AccessStatus 0x1008 ++#define DoC_Mplus_DeviceSelect 0x1008 ++#define DoC_Mplus_Configuration 0x100a ++#define DoC_Mplus_OutputControl 0x100c ++#define DoC_Mplus_FlashControl 0x1020 ++#define DoC_Mplus_FlashSelect 0x1022 ++#define DoC_Mplus_FlashCmd 0x1024 ++#define DoC_Mplus_FlashAddress 0x1026 ++#define DoC_Mplus_FlashData0 0x1028 ++#define DoC_Mplus_FlashData1 0x1029 ++#define DoC_Mplus_ReadPipeInit 0x102a ++#define DoC_Mplus_LastDataRead 0x102c ++#define DoC_Mplus_LastDataRead1 0x102d ++#define DoC_Mplus_WritePipeTerm 0x102e ++#define DoC_Mplus_ECCSyndrome0 0x1040 ++#define DoC_Mplus_ECCSyndrome1 0x1041 ++#define DoC_Mplus_ECCSyndrome2 0x1042 ++#define DoC_Mplus_ECCSyndrome3 0x1043 ++#define DoC_Mplus_ECCSyndrome4 0x1044 ++#define DoC_Mplus_ECCSyndrome5 0x1045 ++#define DoC_Mplus_ECCConf 0x1046 ++#define DoC_Mplus_Toggle 0x1046 ++#define DoC_Mplus_DownloadStatus 0x1074 ++#define DoC_Mplus_CtrlConfirm 0x1076 ++#define DoC_Mplus_Power 0x1fff ++ + /* How to access the device? + * On ARM, it'll be mmap'd directly with 32-bit wide accesses. + * On PPC, it's mmap'd and 16-bit wide. + * Others use readb/writeb + */ + #if defined(__arm__) +-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u32 *)(((unsigned long)adr)+((reg)<<2)))) +-#define WriteDOC_(d, adr, reg) do{ *(__u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0) ++#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)))) ++#define WriteDOC_(d, adr, reg) do{ *(volatile __u32 *)(((unsigned long)adr)+((reg)<<2)) = (__u32)d; wmb();} while(0) + #define DOC_IOREMAP_LEN 0x8000 + #elif defined(__ppc__) +-#define ReadDOC_(adr, reg) ((unsigned char)(*(__u16 *)(((unsigned long)adr)+((reg)<<1)))) +-#define WriteDOC_(d, adr, reg) do{ *(__u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) ++#define ReadDOC_(adr, reg) ((unsigned char)(*(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)))) ++#define WriteDOC_(d, adr, reg) do{ *(volatile __u16 *)(((unsigned long)adr)+((reg)<<1)) = (__u16)d; wmb();} while(0) + #define DOC_IOREMAP_LEN 0x4000 + #else + #define ReadDOC_(adr, reg) readb(((unsigned long)adr) + (reg)) +@@ -71,13 +108,21 @@ + #define DOC_MODE_RESERVED1 2 + #define DOC_MODE_RESERVED2 3 + +-#define DOC_MODE_MDWREN 4 + #define DOC_MODE_CLR_ERR 0x80 ++#define DOC_MODE_RST_LAT 0x10 ++#define DOC_MODE_BDECT 0x08 ++#define DOC_MODE_MDWREN 0x04 + + #define DOC_ChipID_Doc2k 0x20 ++#define DOC_ChipID_Doc2kTSOP 0x21 /* internal number for MTD */ + #define DOC_ChipID_DocMil 0x30 ++#define DOC_ChipID_DocMilPlus32 0x40 ++#define DOC_ChipID_DocMilPlus16 0x41 + + #define CDSN_CTRL_FR_B 0x80 ++#define CDSN_CTRL_FR_B0 0x40 ++#define CDSN_CTRL_FR_B1 0x80 ++ + #define CDSN_CTRL_ECC_IO 0x20 + #define CDSN_CTRL_FLASH_IO 0x10 + #define CDSN_CTRL_WP 0x08 +@@ -93,6 +138,10 @@ + #define DOC_ECC_RESV 0x02 + #define DOC_ECC_IGNORE 0x01 + ++#define DOC_FLASH_CE 0x80 ++#define DOC_FLASH_WP 0x40 ++#define DOC_FLASH_BANK 0x02 ++ + /* We have to also set the reserved bit 1 for enable */ + #define DOC_ECC_EN (DOC_ECC__EN | DOC_ECC_RESV) + #define DOC_ECC_DIS (DOC_ECC_RESV) +@@ -107,9 +156,12 @@ + #define MAX_FLOORS 4 + #define MAX_CHIPS 4 + +-#define MAX_FLOORS_MIL 4 ++#define MAX_FLOORS_MIL 1 + #define MAX_CHIPS_MIL 1 + ++#define MAX_FLOORS_MPLUS 2 ++#define MAX_CHIPS_MPLUS 1 ++ + #define ADDR_COLUMN 1 + #define ADDR_PAGE 2 + #define ADDR_COLUMN_PAGE 3 +@@ -118,7 +170,7 @@ + unsigned long physadr; + unsigned long virtadr; + unsigned long totlen; +- char ChipID; /* Type of DiskOnChip */ ++ unsigned char ChipID; /* Type of DiskOnChip */ + int ioreg; + + unsigned long mfr; /* Flash IDs - only one type of flash per device */ +@@ -126,6 +178,7 @@ + int chipshift; + char page256; + char pageadrlen; ++ char interleave; /* Internal interleaving - Millennium Plus style */ + unsigned long erasesize; + + int curfloor; +diff -Nurb linux-mips-2.4.27/include/linux/mtd/flashchip.h linux/include/linux/mtd/flashchip.h +--- linux-mips-2.4.27/include/linux/mtd/flashchip.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/flashchip.h 2004-11-19 10:25:12.045177600 +0100 +@@ -6,7 +6,7 @@ + * + * (C) 2000 Red Hat. GPLd. + * +- * $Id: flashchip.h,v 1.8 2002/10/21 13:20:52 jocke Exp $ ++ * $Id: flashchip.h,v 1.10 2004/01/27 10:16:20 dvrabel Exp $ + * + */ + +@@ -58,6 +58,11 @@ + int ref_point_counter; + flstate_t state; + flstate_t oldstate; ++ ++ int write_suspended:1; ++ int erase_suspended:1; ++ unsigned long in_progress_block_addr; ++ + spinlock_t *mutex; + spinlock_t _spinlock; /* We do it like this because sometimes they'll be shared. */ + wait_queue_head_t wq; /* Wait on here when we're waiting for the chip +diff -Nurb linux-mips-2.4.27/include/linux/mtd/gen_probe.h linux/include/linux/mtd/gen_probe.h +--- linux-mips-2.4.27/include/linux/mtd/gen_probe.h 2001-11-05 21:16:30.000000000 +0100 ++++ linux/include/linux/mtd/gen_probe.h 2004-11-19 10:25:12.048177144 +0100 +@@ -1,7 +1,7 @@ + /* + * (C) 2001, 2001 Red Hat, Inc. + * GPL'd +- * $Id: gen_probe.h,v 1.1 2001/09/02 18:50:13 dwmw2 Exp $ ++ * $Id: gen_probe.h,v 1.2 2003/11/08 00:51:21 dsaxena Exp $ + */ + + #ifndef __LINUX_MTD_GEN_PROBE_H__ +@@ -10,12 +10,12 @@ + #include <linux/mtd/flashchip.h> + #include <linux/mtd/map.h> + #include <linux/mtd/cfi.h> ++#include <asm/bitops.h> + + struct chip_probe { + char *name; + int (*probe_chip)(struct map_info *map, __u32 base, +- struct flchip *chips, struct cfi_private *cfi); +- ++ unsigned long *chip_map, struct cfi_private *cfi); + }; + + struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp); +diff -Nurb linux-mips-2.4.27/include/linux/mtd/inftl.h linux/include/linux/mtd/inftl.h +--- linux-mips-2.4.27/include/linux/mtd/inftl.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/mtd/inftl.h 2004-11-19 10:25:12.051176688 +0100 +@@ -0,0 +1,129 @@ ++/* ++ * inftl.h -- defines to support the Inverse NAND Flash Translation Layer ++ * ++ * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) ++ * ++ * $Id: inftl.h,v 1.4 2003/06/29 16:15:46 dwmw2 Exp $ ++ */ ++ ++#ifndef __MTD_INFTL_H__ ++#define __MTD_INFTL_H__ ++ ++#include <linux/mtd/blktrans.h> ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/nftl.h> ++ ++#define OSAK_VERSION 0x5120 ++#define PERCENTUSED 98 ++ ++#define SECTORSIZE 512 ++ ++#ifndef INFTL_MAJOR ++#define INFTL_MAJOR 94 ++#endif ++#define INFTL_PARTN_BITS 4 ++ ++/* Block Control Information */ ++ ++struct inftl_bci { ++ __u8 ECCsig[6]; ++ __u8 Status; ++ __u8 Status1; ++} __attribute__((packed)); ++ ++struct inftl_unithead1 { ++ __u16 virtualUnitNo; ++ __u16 prevUnitNo; ++ __u8 ANAC; ++ __u8 NACs; ++ __u8 parityPerField; ++ __u8 discarded; ++} __attribute__((packed)); ++ ++struct inftl_unithead2 { ++ __u8 parityPerField; ++ __u8 ANAC; ++ __u16 prevUnitNo; ++ __u16 virtualUnitNo; ++ __u8 NACs; ++ __u8 discarded; ++} __attribute__((packed)); ++ ++struct inftl_unittail { ++ __u8 Reserved[4]; ++ __u16 EraseMark; ++ __u16 EraseMark1; ++} __attribute__((packed)); ++ ++union inftl_uci { ++ struct inftl_unithead1 a; ++ struct inftl_unithead2 b; ++ struct inftl_unittail c; ++}; ++ ++struct inftl_oob { ++ struct inftl_bci b; ++ union inftl_uci u; ++}; ++ ++ ++/* INFTL Media Header */ ++ ++struct INFTLPartition { ++ __u32 virtualUnits; ++ __u32 firstUnit; ++ __u32 lastUnit; ++ __u32 flags; ++ __u32 spareUnits; ++ __u32 Reserved0; ++ __u32 Reserved1; ++} __attribute__((packed)); ++ ++struct INFTLMediaHeader { ++ char bootRecordID[8]; ++ __u32 NoOfBootImageBlocks; ++ __u32 NoOfBinaryPartitions; ++ __u32 NoOfBDTLPartitions; ++ __u32 BlockMultiplierBits; ++ __u32 FormatFlags; ++ __u32 OsakVersion; ++ __u32 PercentUsed; ++ struct INFTLPartition Partitions[4]; ++} __attribute__((packed)); ++ ++/* Partition flag types */ ++#define INFTL_BINARY 0x20000000 ++#define INFTL_BDTL 0x40000000 ++#define INFTL_LAST 0x80000000 ++ ++ ++#ifdef __KERNEL__ ++ ++struct INFTLrecord { ++ struct mtd_blktrans_dev mbd; ++ __u16 MediaUnit, SpareMediaUnit; ++ __u32 EraseSize; ++ struct INFTLMediaHeader MediaHdr; ++ int usecount; ++ unsigned char heads; ++ unsigned char sectors; ++ unsigned short cylinders; ++ __u16 numvunits; ++ __u16 firstEUN; ++ __u16 lastEUN; ++ __u16 numfreeEUNs; ++ __u16 LastFreeEUN; /* To speed up finding a free EUN */ ++ int head,sect,cyl; ++ __u16 *PUtable; /* Physical Unit Table */ ++ __u16 *VUtable; /* Virtual Unit Table */ ++ unsigned int nb_blocks; /* number of physical blocks */ ++ unsigned int nb_boot_blocks; /* number of blocks used by the bios */ ++ struct erase_info instr; ++}; ++ ++int INFTL_mount(struct INFTLrecord *s); ++int INFTL_formatblock(struct INFTLrecord *s, int block); ++ ++#endif /* __KERNEL__ */ ++ ++#endif /* __MTD_INFTL_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/jedec.h linux/include/linux/mtd/jedec.h +--- linux-mips-2.4.27/include/linux/mtd/jedec.h 2001-12-02 12:35:00.000000000 +0100 ++++ linux/include/linux/mtd/jedec.h 2004-11-19 10:25:12.052176536 +0100 +@@ -7,14 +7,13 @@ + * + * See the AMD flash databook for information on how to operate the interface. + * +- * $Id: jedec.h,v 1.2 2001/11/06 14:37:36 dwmw2 Exp $ ++ * $Id: jedec.h,v 1.3 2003/05/21 11:51:01 dwmw2 Exp $ + */ + + #ifndef __LINUX_MTD_JEDEC_H__ + #define __LINUX_MTD_JEDEC_H__ + + #include <linux/types.h> +-#include <linux/mtd/map.h> + + #define MAX_JEDEC_CHIPS 16 + +diff -Nurb linux-mips-2.4.27/include/linux/mtd/map.h linux/include/linux/mtd/map.h +--- linux-mips-2.4.27/include/linux/mtd/map.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/map.h 2004-11-19 10:25:12.054176232 +0100 +@@ -1,14 +1,15 @@ + + /* Overhauled routines for dealing with different mmap regions of flash */ +-/* $Id: map.h,v 1.29 2002/10/21 13:20:52 jocke Exp $ */ ++/* $Id: map.h,v 1.34 2003/05/28 12:42:22 dwmw2 Exp $ */ + + #ifndef __LINUX_MTD_MAP_H__ + #define __LINUX_MTD_MAP_H__ + + #include <linux/config.h> + #include <linux/types.h> +-#include <linux/mtd/mtd.h> +-#include <linux/slab.h> ++#include <linux/list.h> ++#include <asm/system.h> ++#include <asm/io.h> + + /* The map stuff is very simple. You fill in your struct map_info with + a handful of routines for accessing the device, making sure they handle +@@ -29,39 +30,44 @@ + struct map_info { + char *name; + unsigned long size; ++ unsigned long phys; ++#define NO_XIP (-1UL) ++ ++ unsigned long virt; ++ void *cached; ++ + int buswidth; /* in octets */ +- __u8 (*read8)(struct map_info *, unsigned long); +- __u16 (*read16)(struct map_info *, unsigned long); +- __u32 (*read32)(struct map_info *, unsigned long); +- __u64 (*read64)(struct map_info *, unsigned long); ++ ++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS ++ u8 (*read8)(struct map_info *, unsigned long); ++ u16 (*read16)(struct map_info *, unsigned long); ++ u32 (*read32)(struct map_info *, unsigned long); ++ u64 (*read64)(struct map_info *, unsigned long); + /* If it returned a 'long' I'd call it readl. + * It doesn't. + * I won't. + * dwmw2 */ + + void (*copy_from)(struct map_info *, void *, unsigned long, ssize_t); +- void (*write8)(struct map_info *, __u8, unsigned long); +- void (*write16)(struct map_info *, __u16, unsigned long); +- void (*write32)(struct map_info *, __u32, unsigned long); +- void (*write64)(struct map_info *, __u64, unsigned long); ++ void (*write8)(struct map_info *, u8, unsigned long); ++ void (*write16)(struct map_info *, u16, unsigned long); ++ void (*write32)(struct map_info *, u32, unsigned long); ++ void (*write64)(struct map_info *, u64, unsigned long); + void (*copy_to)(struct map_info *, unsigned long, const void *, ssize_t); + +- u_char * (*point) (struct map_info *, loff_t, size_t); +- void (*unpoint) (struct map_info *, u_char *, loff_t, size_t); +- ++ /* We can perhaps put in 'point' and 'unpoint' methods, if we really ++ want to enable XIP for non-linear mappings. Not yet though. */ ++#endif ++ /* set_vpp() must handle being reentered -- enable, enable, disable ++ must leave it enabled. */ + void (*set_vpp)(struct map_info *, int); +- /* We put these two here rather than a single void *map_priv, +- because we want mappers to be able to have quickly-accessible +- cache for the 'currently-mapped page' without the _extra_ +- redirection that would be necessary. If you need more than +- two longs, turn the second into a pointer. dwmw2 */ ++ + unsigned long map_priv_1; + unsigned long map_priv_2; + void *fldrv_priv; + struct mtd_chip_driver *fldrv; + }; + +- + struct mtd_chip_driver { + struct mtd_info *(*probe)(struct map_info *map); + void (*destroy)(struct mtd_info *); +@@ -74,26 +80,93 @@ + void unregister_mtd_chip_driver(struct mtd_chip_driver *); + + struct mtd_info *do_map_probe(const char *name, struct map_info *map); ++void map_destroy(struct mtd_info *mtd); ++ ++#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0) ++#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0) ++ ++#ifdef CONFIG_MTD_COMPLEX_MAPPINGS ++#define map_read8(map, ofs) (map)->read8(map, ofs) ++#define map_read16(map, ofs) (map)->read16(map, ofs) ++#define map_read32(map, ofs) (map)->read32(map, ofs) ++#define map_read64(map, ofs) (map)->read64(map, ofs) ++#define map_copy_from(map, to, from, len) (map)->copy_from(map, to, from, len) ++#define map_write8(map, datum, ofs) (map)->write8(map, datum, ofs) ++#define map_write16(map, datum, ofs) (map)->write16(map, datum, ofs) ++#define map_write32(map, datum, ofs) (map)->write32(map, datum, ofs) ++#define map_write64(map, datum, ofs) (map)->write64(map, datum, ofs) ++#define map_copy_to(map, to, from, len) (map)->copy_to(map, to, from, len) + ++extern void simple_map_init(struct map_info *); ++#define map_is_linear(map) (map->phys != NO_XIP) + +-/* +- * Destroy an MTD device which was created for a map device. +- * Make sure the MTD device is already unregistered before calling this +- */ +-static inline void map_destroy(struct mtd_info *mtd) +-{ +- struct map_info *map = mtd->priv; +- +- if (map->fldrv->destroy) +- map->fldrv->destroy(mtd); +-#ifdef CONFIG_MODULES +- if (map->fldrv->module) +- __MOD_DEC_USE_COUNT(map->fldrv->module); ++#else ++static inline u8 map_read8(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readb(map->virt + ofs); ++} ++ ++static inline u16 map_read16(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readw(map->virt + ofs); ++} ++ ++static inline u32 map_read32(struct map_info *map, unsigned long ofs) ++{ ++ return __raw_readl(map->virt + ofs); ++} ++ ++static inline u64 map_read64(struct map_info *map, unsigned long ofs) ++{ ++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ ++ BUG(); ++ return 0; ++#else ++ return __raw_readll(map->virt + ofs); + #endif +- kfree(mtd); + } + +-#define ENABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 1); } while(0) +-#define DISABLE_VPP(map) do { if(map->set_vpp) map->set_vpp(map, 0); } while(0) ++static inline void map_write8(struct map_info *map, u8 datum, unsigned long ofs) ++{ ++ __raw_writeb(datum, map->virt + ofs); ++ mb(); ++} ++ ++static inline void map_write16(struct map_info *map, u16 datum, unsigned long ofs) ++{ ++ __raw_writew(datum, map->virt + ofs); ++ mb(); ++} ++ ++static inline void map_write32(struct map_info *map, u32 datum, unsigned long ofs) ++{ ++ __raw_writel(datum, map->virt + ofs); ++ mb(); ++} ++ ++static inline void map_write64(struct map_info *map, u64 datum, unsigned long ofs) ++{ ++#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ ++ BUG(); ++#else ++ __raw_writell(datum, map->virt + ofs); ++ mb(); ++#endif /* CFI_B8 */ ++} ++ ++static inline void map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) ++{ ++ memcpy_fromio(to, map->virt + from, len); ++} ++ ++static inline void map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) ++{ ++ memcpy_toio(map->virt + to, from, len); ++} ++ ++#define simple_map_init(map) do { } while (0) ++#define map_is_linear(map) (1) ++ ++#endif /* !CONFIG_MTD_COMPLEX_MAPPINGS */ + + #endif /* __LINUX_MTD_MAP_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/mtd.h linux/include/linux/mtd/mtd.h +--- linux-mips-2.4.27/include/linux/mtd/mtd.h 2003-08-13 19:19:29.000000000 +0200 ++++ linux/include/linux/mtd/mtd.h 2004-11-19 10:25:12.055176080 +0100 +@@ -1,5 +1,10 @@ +- +-/* $Id: mtd.h,v 1.38 2003/01/12 16:30:19 spse Exp $ */ ++/* ++ * $Id: mtd.h,v 1.46 2003/07/11 07:36:21 dwmw2 Exp $ ++ * ++ * Copyright (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> et al. ++ * ++ * Released under GPL ++ */ + + #ifndef __MTD_MTD_H__ + #define __MTD_MTD_H__ +@@ -9,7 +14,6 @@ + #include <linux/config.h> + #include <linux/version.h> + #include <linux/types.h> +-#include <linux/mtd/compatmac.h> + #include <linux/module.h> + #include <linux/uio.h> + +@@ -26,7 +30,6 @@ + unsigned char *ptr; + }; + +- + #define MTD_CHAR_MAJOR 90 + #define MTD_BLOCK_MAJOR 31 + #define MAX_MTD_DEVICES 16 +@@ -93,18 +96,23 @@ + #define MEMUNLOCK _IOW('M', 6, struct erase_info_user) + #define MEMGETREGIONCOUNT _IOR('M', 7, int) + #define MEMGETREGIONINFO _IOWR('M', 8, struct region_info_user) +-#define MEMREADDATA _IOWR('M', 9, struct mtd_oob_buf) +-#define MEMWRITEDATA _IOWR('M', 10, struct mtd_oob_buf) ++#define MEMSETOOBSEL _IOW('M', 9, struct nand_oobinfo) ++ ++struct nand_oobinfo { ++ int useecc; ++ int eccpos[6]; ++}; ++ + + #ifndef __KERNEL__ + + typedef struct mtd_info_user mtd_info_t; + typedef struct erase_info_user erase_info_t; + typedef struct region_info_user region_info_t; ++typedef struct nand_oobinfo nand_oobinfo_t; + + /* User-space ioctl definitions */ + +- + #else /* __KERNEL__ */ + + +@@ -150,10 +158,14 @@ + u_int32_t ecctype; + u_int32_t eccsize; + ++ + // Kernel-only stuff starts here. + char *name; + int index; + ++ // oobinfo is a nand_oobinfo structure, which can be set by iotcl (MEMSETOOBINFO) ++ struct nand_oobinfo oobinfo; ++ + /* Data for variable erase regions. If numeraseregions is zero, + * it means that the whole device has erasesize as given above. + */ +@@ -163,7 +175,6 @@ + /* This really shouldn't be here. It can go away in 2.5 */ + u_int32_t bank_size; + +- struct module *module; + int (*erase) (struct mtd_info *mtd, struct erase_info *instr); + + /* This stuff for eXecute-In-Place */ +@@ -176,8 +187,8 @@ + 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); + +- int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel); +- int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel); ++ int (*read_ecc) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); ++ int (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf, struct nand_oobinfo *oobsel); + + int (*read_oob) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + int (*write_oob) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf); +@@ -201,10 +212,10 @@ + */ + int (*readv) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, size_t *retlen); + int (*readv_ecc) (struct mtd_info *mtd, struct iovec *vecs, unsigned long count, loff_t from, +- size_t *retlen, u_char *eccbuf, int oobsel); ++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); + int (*writev) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, size_t *retlen); + int (*writev_ecc) (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, loff_t to, +- size_t *retlen, u_char *eccbuf, int oobsel); ++ size_t *retlen, u_char *eccbuf, struct nand_oobinfo *oobsel); + + /* Sync */ + void (*sync) (struct mtd_info *mtd); +@@ -218,6 +229,9 @@ + void (*resume) (struct mtd_info *mtd); + + void *priv; ++ ++ struct module *owner; ++ int usecount; + }; + + +@@ -226,31 +240,15 @@ + extern int add_mtd_device(struct mtd_info *mtd); + extern int del_mtd_device (struct mtd_info *mtd); + +-extern struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num); +- +-static inline struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num) +-{ +- struct mtd_info *ret; ++extern struct mtd_info *get_mtd_device(struct mtd_info *mtd, int num); + +- ret = __get_mtd_device(mtd, num); +- +- if (ret && ret->module && !try_inc_mod_count(ret->module)) +- return NULL; +- +- return ret; +-} +- +-static inline void put_mtd_device(struct mtd_info *mtd) +-{ +- if (mtd->module) +- __MOD_DEC_USE_COUNT(mtd->module); +-} ++extern void put_mtd_device(struct mtd_info *mtd); + + + struct mtd_notifier { + void (*add)(struct mtd_info *mtd); + void (*remove)(struct mtd_info *mtd); +- struct mtd_notifier *next; ++ struct list_head list; + }; + + +@@ -263,7 +261,6 @@ + int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs, + unsigned long count, loff_t from, size_t *retlen); + +-#ifndef MTDC + #define MTD_ERASE(mtd, args...) (*(mtd->erase))(mtd, args) + #define MTD_POINT(mtd, a,b,c,d) (*(mtd->point))(mtd, a,b,c, (u_char **)(d)) + #define MTD_UNPOINT(mtd, arg) (*(mtd->unpoint))(mtd, (u_char *)arg) +@@ -276,7 +273,6 @@ + #define MTD_READOOB(mtd, args...) (*(mtd->read_oob))(mtd, args) + #define MTD_WRITEOOB(mtd, args...) (*(mtd->write_oob))(mtd, args) + #define MTD_SYNC(mtd) do { if (mtd->sync) (*(mtd->sync))(mtd); } while (0) +-#endif /* MTDC */ + + /* + * Debugging macro and defines +@@ -293,7 +289,8 @@ + printk(KERN_INFO args); \ + } while(0) + #else /* CONFIG_MTD_DEBUG */ +-#define DEBUG(n, args...) ++#define DEBUG(n, args...) do { } while(0) ++ + #endif /* CONFIG_MTD_DEBUG */ + + #endif /* __KERNEL__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/nand.h linux/include/linux/mtd/nand.h +--- linux-mips-2.4.27/include/linux/mtd/nand.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/nand.h 2004-11-19 10:25:12.057175776 +0100 +@@ -2,10 +2,10 @@ + * linux/include/linux/mtd/nand.h + * + * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com> +- * Steven J. Hill <sjhill@cotw.com> ++ * Steven J. Hill <sjhill@realitydiluted.com> + * Thomas Gleixner <tglx@linutronix.de> + * +- * $Id: nand.h,v 1.19 2002/12/02 21:48:17 gleixner Exp $ ++ * $Id: nand.h,v 1.31 2003/07/11 15:07:02 dwmw2 Exp $ + * + * 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 +@@ -49,12 +49,14 @@ + #define __LINUX_MTD_NAND_H + + #include <linux/config.h> +-#include <linux/sched.h> ++#include <linux/wait.h> ++#include <linux/spinlock.h> + ++struct mtd_info; + /* + * Searches for a NAND device + */ +-extern int nand_scan (struct mtd_info *mtd); ++extern int nand_scan (struct mtd_info *mtd, int max_chips); + + /* + * Constants for hardware specific CLE/ALE/NCE function +@@ -65,6 +67,8 @@ + #define NAND_CTL_CLRCLE 4 + #define NAND_CTL_SETALE 5 + #define NAND_CTL_CLRALE 6 ++#define NAND_CTL_SETWP 7 ++#define NAND_CTL_CLRWP 8 + + /* + * Standard NAND flash commands +@@ -160,24 +164,33 @@ + struct nand_chip { + unsigned long IO_ADDR_R; + unsigned long IO_ADDR_W; +- void (*hwcontrol)(int cmd); +- int (*dev_ready)(void); ++ ++ u_char (*read_byte)(struct mtd_info *mtd); ++ void (*write_byte)(struct mtd_info *mtd, u_char byte); ++ ++ void (*write_buf)(struct mtd_info *mtd, const u_char *buf, int len); ++ void (*read_buf)(struct mtd_info *mtd, u_char *buf, int len); ++ int (*verify_buf)(struct mtd_info *mtd, const u_char *buf, int len); ++ void (*select_chip)(struct mtd_info *mtd, int chip); ++ int (*block_bad)(struct mtd_info *mtd, unsigned long pos); ++ void (*hwcontrol)(struct mtd_info *mtd, int cmd); ++ int (*dev_ready)(struct mtd_info *mtd); + void (*cmdfunc)(struct mtd_info *mtd, unsigned command, int column, int page_addr); + int (*waitfunc)(struct mtd_info *mtd, struct nand_chip *this, int state); +- void (*calculate_ecc)(const u_char *dat, u_char *ecc_code); +- int (*correct_data)(u_char *dat, u_char *read_ecc, u_char *calc_ecc); +- void (*enable_hwecc)(int mode); ++ void (*calculate_ecc)(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); ++ int (*correct_data)(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); ++ void (*enable_hwecc)(struct mtd_info *mtd, int mode); + int eccmode; + int eccsize; + int chip_delay; ++ int chipshift; + spinlock_t chip_lock; + wait_queue_head_t wq; + nand_state_t state; + int page_shift; + u_char *data_buf; + u_char *data_poi; +- u_char *data_cache; +- int cache_page; ++ void *priv; + }; + + /* +@@ -241,34 +254,4 @@ + */ + #define NAND_BADBLOCK_POS 5 + +-#define NAND_NONE_OOB 0 +-#define NAND_JFFS2_OOB 1 +-#define NAND_YAFFS_OOB 2 +- +-#define NAND_NOOB_ECCPOS0 0 +-#define NAND_NOOB_ECCPOS1 1 +-#define NAND_NOOB_ECCPOS2 2 +-#define NAND_NOOB_ECCPOS3 3 +-#define NAND_NOOB_ECCPOS4 6 +-#define NAND_NOOB_ECCPOS5 7 +- +-#define NAND_JFFS2_OOB_ECCPOS0 0 +-#define NAND_JFFS2_OOB_ECCPOS1 1 +-#define NAND_JFFS2_OOB_ECCPOS2 2 +-#define NAND_JFFS2_OOB_ECCPOS3 3 +-#define NAND_JFFS2_OOB_ECCPOS4 6 +-#define NAND_JFFS2_OOB_ECCPOS5 7 +- +-#define NAND_YAFFS_OOB_ECCPOS0 8 +-#define NAND_YAFFS_OOB_ECCPOS1 9 +-#define NAND_YAFFS_OOB_ECCPOS2 10 +-#define NAND_YAFFS_OOB_ECCPOS3 13 +-#define NAND_YAFFS_OOB_ECCPOS4 14 +-#define NAND_YAFFS_OOB_ECCPOS5 15 +- +-#define NAND_JFFS2_OOB8_FSDAPOS 6 +-#define NAND_JFFS2_OOB16_FSDAPOS 8 +-#define NAND_JFFS2_OOB8_FSDALEN 2 +-#define NAND_JFFS2_OOB16_FSDALEN 8 +- + #endif /* __LINUX_MTD_NAND_H */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/nand_ecc.h linux/include/linux/mtd/nand_ecc.h +--- linux-mips-2.4.27/include/linux/mtd/nand_ecc.h 2001-08-22 05:25:12.000000000 +0200 ++++ linux/include/linux/mtd/nand_ecc.h 2004-11-19 10:25:12.058175624 +0100 +@@ -1,9 +1,9 @@ + /* + * drivers/mtd/nand_ecc.h + * +- * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) ++ * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * +- * $Id: nand_ecc.h,v 1.1 2000/10/12 00:57:15 sjhill Exp $ ++ * $Id: nand_ecc.h,v 1.3 2003/07/01 23:31:15 dwmw2 Exp $ + * + * 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 +@@ -12,17 +12,19 @@ + * This file is the header for the ECC algorithm. + */ + +-/* +- * Creates non-inverted ECC code from line parity +- */ +-void nand_trans_result(u_char reg2, u_char reg3, u_char *ecc_code); ++#ifndef __MTD_NAND_ECC_H__ ++#define __MTD_NAND_ECC_H__ ++ ++struct mtd_info; + + /* + * Calculate 3 byte ECC code for 256 byte block + */ +-void nand_calculate_ecc (const u_char *dat, u_char *ecc_code); ++void nand_calculate_ecc(struct mtd_info *mtd, const u_char *dat, u_char *ecc_code); + + /* + * Detect and correct a 1 bit error for 256 byte block + */ +-int nand_correct_data (u_char *dat, u_char *read_ecc, u_char *calc_ecc); ++int nand_correct_data(struct mtd_info *mtd, u_char *dat, u_char *read_ecc, u_char *calc_ecc); ++ ++#endif /* __MTD_NAND_ECC_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/nftl.h linux/include/linux/mtd/nftl.h +--- linux-mips-2.4.27/include/linux/mtd/nftl.h 2003-02-26 01:53:51.000000000 +0100 ++++ linux/include/linux/mtd/nftl.h 2004-11-19 10:25:12.060175320 +0100 +@@ -1,15 +1,14 @@ +- +-/* Defines for NAND Flash Translation Layer */ +-/* (c) 1999 Machine Vision Holdings, Inc. */ +-/* Author: David Woodhouse <dwmw2@mvhi.com> */ +-/* $Id: nftl.h,v 1.11 2002/06/18 13:54:24 dwmw2 Exp $ */ ++/* ++ * $Id: nftl.h,v 1.13 2003/05/23 11:25:02 dwmw2 Exp $ ++ * ++ * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> ++ */ + + #ifndef __MTD_NFTL_H__ + #define __MTD_NFTL_H__ + +-#ifndef __BOOT__ + #include <linux/mtd/mtd.h> +-#endif ++#include <linux/mtd/blktrans.h> + + /* Block Control Information */ + +@@ -84,8 +83,7 @@ + #define BLOCK_RESERVED 0xfffc /* bios block or bad block */ + + struct NFTLrecord { +- struct mtd_info *mtd; +- struct semaphore mutex; ++ struct mtd_blktrans_dev mbd; + __u16 MediaUnit, SpareMediaUnit; + __u32 EraseSize; + struct NFTLMediaHeader MediaHdr; +@@ -97,7 +95,6 @@ + __u16 lastEUN; /* should be suppressed */ + __u16 numfreeEUNs; + __u16 LastFreeEUN; /* To speed up finding a free EUN */ +- __u32 nr_sects; + int head,sect,cyl; + __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */ + __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */ +@@ -114,7 +111,7 @@ + #endif + + #define MAX_NFTLS 16 +-#define MAX_SECTORS_PER_UNIT 32 ++#define MAX_SECTORS_PER_UNIT 64 + #define NFTL_PARTN_BITS 4 + + #endif /* __KERNEL__ */ +diff -Nurb linux-mips-2.4.27/include/linux/mtd/partitions.h linux/include/linux/mtd/partitions.h +--- linux-mips-2.4.27/include/linux/mtd/partitions.h 2002-06-27 00:36:47.000000000 +0200 ++++ linux/include/linux/mtd/partitions.h 2004-11-19 10:25:12.061175168 +0100 +@@ -5,7 +5,7 @@ + * + * This code is GPL + * +- * $Id: partitions.h,v 1.8 2002/03/08 16:34:36 rkaiser Exp $ ++ * $Id: partitions.h,v 1.15 2003/07/09 11:15:43 dwmw2 Exp $ + */ + + #ifndef MTD_PARTITIONS_H +@@ -41,6 +41,7 @@ + u_int32_t size; /* partition size */ + u_int32_t offset; /* offset within the master MTD space */ + u_int32_t mask_flags; /* master MTD flags to mask out for this partition */ ++ struct nand_oobinfo *oobsel; /* out of band layout for this partition (NAND only)*/ + struct mtd_info **mtdp; /* pointer to store the MTD object */ + }; + +@@ -49,8 +50,27 @@ + #define MTDPART_SIZ_FULL (0) + + +-int add_mtd_partitions(struct mtd_info *, struct mtd_partition *, int); ++int add_mtd_partitions(struct mtd_info *, const struct mtd_partition *, int); + int del_mtd_partitions(struct mtd_info *); + ++/* ++ * Functions dealing with the various ways of partitioning the space ++ */ ++ ++struct mtd_part_parser { ++ struct list_head list; ++ struct module *owner; ++ const char *name; ++ int (*parse_fn)(struct mtd_info *, struct mtd_partition **, unsigned long); ++}; ++ ++extern struct mtd_part_parser *get_partition_parser(const char *name); ++extern int register_mtd_parser(struct mtd_part_parser *parser); ++extern int deregister_mtd_parser(struct mtd_part_parser *parser); ++extern int parse_mtd_partitions(struct mtd_info *master, const char **types, ++ struct mtd_partition **pparts, unsigned long origin); ++ ++#define put_partition_parser(p) do { module_put((p)->owner); } while(0) ++ + #endif + +diff -Nurb linux-mips-2.4.27/include/linux/mtd/physmap.h linux/include/linux/mtd/physmap.h +--- linux-mips-2.4.27/include/linux/mtd/physmap.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/mtd/physmap.h 2004-11-19 10:25:12.062175016 +0100 +@@ -0,0 +1,59 @@ ++/* ++ * For boards with physically mapped flash and using ++ * drivers/mtd/maps/physmap.c mapping driver. ++ * ++ * Copyright (C) 2003 MontaVista Software Inc. ++ * Author: Jun Sun, jsun@mvista.com or jsun@junsun.net ++ * ++ * 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. ++ * ++ */ ++ ++#ifndef __LINUX_MTD_PHYSMAP__ ++ ++#include <linux/config.h> ++ ++#if defined(CONFIG_MTD_PHYSMAP) ++ ++#include <linux/mtd/mtd.h> ++#include <linux/mtd/map.h> ++#include <linux/mtd/partitions.h> ++ ++/* ++ * The map_info for physmap. Board can override size, buswidth, phys, ++ * (*set_vpp)(), etc in their initial setup routine. ++ */ ++extern struct map_info physmap_map; ++ ++/* ++ * Board needs to specify the exact mapping during their setup time. ++ */ ++static inline void physmap_configure(unsigned long addr, unsigned long size, int buswidth, void (*set_vpp)(struct map_info *, int) ) ++{ ++ physmap_map.phys = addr; ++ physmap_map.size = size; ++ physmap_map.buswidth = buswidth; ++ physmap_map.set_vpp = set_vpp; ++} ++ ++#if defined(CONFIG_MTD_PARTITIONS) ++ ++/* ++ * Machines that wish to do flash partition may want to call this function in ++ * their setup routine. ++ * ++ * physmap_set_partitions(mypartitions, num_parts); ++ * ++ * Note that one can always override this hard-coded partition with ++ * command line partition (you need to enable CONFIG_MTD_CMDLINE_PARTS). ++ */ ++void physmap_set_partitions(struct mtd_partition *parts, int num_parts); ++ ++#endif /* defined(CONFIG_MTD_PARTITIONS) */ ++#endif /* defined(CONFIG_MTD) */ ++ ++#endif /* __LINUX_MTD_PHYSMAP__ */ ++ +diff -Nurb linux-mips-2.4.27/include/linux/rbtree-24.h linux/include/linux/rbtree-24.h +--- linux-mips-2.4.27/include/linux/rbtree-24.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/rbtree-24.h 2004-11-19 10:25:12.143162704 +0100 +@@ -0,0 +1,133 @@ ++/* ++ Red Black Trees ++ (C) 1999 Andrea Arcangeli <andrea@suse.de> ++ ++ This program is free software; you can redistribute it and/or modify ++ it under the terms of the GNU General Public License as published by ++ the Free Software Foundation; either version 2 of the License, or ++ (at your option) any later version. ++ ++ This program is distributed in the hope that it will be useful, ++ but WITHOUT ANY WARRANTY; without even the implied warranty of ++ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the ++ GNU General Public License for more details. ++ ++ You should have received a copy of the GNU General Public License ++ along with this program; if not, write to the Free Software ++ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA ++ ++ linux/include/linux/rbtree.h ++ ++ To use rbtrees you'll have to implement your own insert and search cores. ++ This will avoid us to use callbacks and to drop drammatically performances. ++ I know it's not the cleaner way, but in C (not in C++) to get ++ performances and genericity... ++ ++ Some example of insert and search follows here. The search is a plain ++ normal search over an ordered tree. The insert instead must be implemented ++ int two steps: as first thing the code must insert the element in ++ order as a red leaf in the tree, then the support library function ++ rb_insert_color() must be called. Such function will do the ++ not trivial work to rebalance the rbtree if necessary. ++ ++----------------------------------------------------------------------- ++static inline struct page * rb_search_page_cache(struct inode * inode, ++ unsigned long offset) ++{ ++ rb_node_t * n = inode->i_rb_page_cache.rb_node; ++ struct page * page; ++ ++ while (n) ++ { ++ page = rb_entry(n, struct page, rb_page_cache); ++ ++ if (offset < page->offset) ++ n = n->rb_left; ++ else if (offset > page->offset) ++ n = n->rb_right; ++ else ++ return page; ++ } ++ return NULL; ++} ++ ++static inline struct page * __rb_insert_page_cache(struct inode * inode, ++ unsigned long offset, ++ rb_node_t * node) ++{ ++ rb_node_t ** p = &inode->i_rb_page_cache.rb_node; ++ rb_node_t * parent = NULL; ++ struct page * page; ++ ++ while (*p) ++ { ++ parent = *p; ++ page = rb_entry(parent, struct page, rb_page_cache); ++ ++ if (offset < page->offset) ++ p = &(*p)->rb_left; ++ else if (offset > page->offset) ++ p = &(*p)->rb_right; ++ else ++ return page; ++ } ++ ++ rb_link_node(node, parent, p); ++ ++ return NULL; ++} ++ ++static inline struct page * rb_insert_page_cache(struct inode * inode, ++ unsigned long offset, ++ rb_node_t * node) ++{ ++ struct page * ret; ++ if ((ret = __rb_insert_page_cache(inode, offset, node))) ++ goto out; ++ rb_insert_color(node, &inode->i_rb_page_cache); ++ out: ++ return ret; ++} ++----------------------------------------------------------------------- ++*/ ++ ++#ifndef _LINUX_RBTREE_H ++#define _LINUX_RBTREE_H ++ ++#include <linux/kernel.h> ++#include <linux/stddef.h> ++ ++typedef struct rb_node_s ++{ ++ struct rb_node_s * rb_parent; ++ int rb_color; ++#define RB_RED 0 ++#define RB_BLACK 1 ++ struct rb_node_s * rb_right; ++ struct rb_node_s * rb_left; ++} ++rb_node_t; ++ ++typedef struct rb_root_s ++{ ++ struct rb_node_s * rb_node; ++} ++rb_root_t; ++ ++#define RB_ROOT (rb_root_t) { NULL, } ++#define rb_entry(ptr, type, member) \ ++ ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) ++ ++extern void rb_insert_color(rb_node_t *, rb_root_t *); ++extern void rb_erase(rb_node_t *, rb_root_t *); ++ ++static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link) ++{ ++ node->rb_parent = parent; ++ node->rb_color = RB_RED; ++ node->rb_left = node->rb_right = NULL; ++ ++ *rb_link = node; ++} ++ ++#endif /* _LINUX_RBTREE_H */ +diff -Nurb linux-mips-2.4.27/include/linux/rbtree.h linux/include/linux/rbtree.h +--- linux-mips-2.4.27/include/linux/rbtree.h 2001-10-19 03:25:03.000000000 +0200 ++++ linux/include/linux/rbtree.h 2004-11-19 10:25:12.148161944 +0100 +@@ -1,133 +1,25 @@ + /* +- Red Black Trees +- (C) 1999 Andrea Arcangeli <andrea@suse.de> ++ * 2.5 compatibility ++ * $Id: rbtree.h,v 1.3 2003/01/14 13:56:05 dwmw2 Exp $ ++ */ ++ ++#ifndef __MTD_COMPAT_RBTREE_H__ ++#define __MTD_COMPAT_RBTREE_H__ ++ ++#include <linux/version.h> ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40) ++#include_next <linux/rbtree.h> ++#else ++#define rb_node_s rb_node ++#define rb_root_s rb_root ++ ++#include <linux/rbtree-24.h> ++ ++/* Find logical next and previous nodes in a tree */ ++extern struct rb_node *rb_next(struct rb_node *); ++extern struct rb_node *rb_prev(struct rb_node *); ++extern struct rb_node *rb_first(struct rb_root *); ++#endif + +- This program is free software; you can redistribute it and/or modify +- it under the terms of the GNU General Public License as published by +- the Free Software Foundation; either version 2 of the License, or +- (at your option) any later version. +- +- This program is distributed in the hope that it will be useful, +- but WITHOUT ANY WARRANTY; without even the implied warranty of +- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +- GNU General Public License for more details. +- +- You should have received a copy of the GNU General Public License +- along with this program; if not, write to the Free Software +- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA +- +- linux/include/linux/rbtree.h +- +- To use rbtrees you'll have to implement your own insert and search cores. +- This will avoid us to use callbacks and to drop drammatically performances. +- I know it's not the cleaner way, but in C (not in C++) to get +- performances and genericity... +- +- Some example of insert and search follows here. The search is a plain +- normal search over an ordered tree. The insert instead must be implemented +- int two steps: as first thing the code must insert the element in +- order as a red leaf in the tree, then the support library function +- rb_insert_color() must be called. Such function will do the +- not trivial work to rebalance the rbtree if necessary. +- +------------------------------------------------------------------------ +-static inline struct page * rb_search_page_cache(struct inode * inode, +- unsigned long offset) +-{ +- rb_node_t * n = inode->i_rb_page_cache.rb_node; +- struct page * page; +- +- while (n) +- { +- page = rb_entry(n, struct page, rb_page_cache); +- +- if (offset < page->offset) +- n = n->rb_left; +- else if (offset > page->offset) +- n = n->rb_right; +- else +- return page; +- } +- return NULL; +-} +- +-static inline struct page * __rb_insert_page_cache(struct inode * inode, +- unsigned long offset, +- rb_node_t * node) +-{ +- rb_node_t ** p = &inode->i_rb_page_cache.rb_node; +- rb_node_t * parent = NULL; +- struct page * page; +- +- while (*p) +- { +- parent = *p; +- page = rb_entry(parent, struct page, rb_page_cache); +- +- if (offset < page->offset) +- p = &(*p)->rb_left; +- else if (offset > page->offset) +- p = &(*p)->rb_right; +- else +- return page; +- } +- +- rb_link_node(node, parent, p); +- +- return NULL; +-} +- +-static inline struct page * rb_insert_page_cache(struct inode * inode, +- unsigned long offset, +- rb_node_t * node) +-{ +- struct page * ret; +- if ((ret = __rb_insert_page_cache(inode, offset, node))) +- goto out; +- rb_insert_color(node, &inode->i_rb_page_cache); +- out: +- return ret; +-} +------------------------------------------------------------------------ +-*/ +- +-#ifndef _LINUX_RBTREE_H +-#define _LINUX_RBTREE_H +- +-#include <linux/kernel.h> +-#include <linux/stddef.h> +- +-typedef struct rb_node_s +-{ +- struct rb_node_s * rb_parent; +- int rb_color; +-#define RB_RED 0 +-#define RB_BLACK 1 +- struct rb_node_s * rb_right; +- struct rb_node_s * rb_left; +-} +-rb_node_t; +- +-typedef struct rb_root_s +-{ +- struct rb_node_s * rb_node; +-} +-rb_root_t; +- +-#define RB_ROOT (rb_root_t) { NULL, } +-#define rb_entry(ptr, type, member) \ +- ((type *)((char *)(ptr)-(unsigned long)(&((type *)0)->member))) +- +-extern void rb_insert_color(rb_node_t *, rb_root_t *); +-extern void rb_erase(rb_node_t *, rb_root_t *); +- +-static inline void rb_link_node(rb_node_t * node, rb_node_t * parent, rb_node_t ** rb_link) +-{ +- node->rb_parent = parent; +- node->rb_color = RB_RED; +- node->rb_left = node->rb_right = NULL; +- +- *rb_link = node; +-} +- +-#endif /* _LINUX_RBTREE_H */ ++#endif /* __MTD_COMPAT_RBTREE_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/suspend.h linux/include/linux/suspend.h +--- linux-mips-2.4.27/include/linux/suspend.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/suspend.h 2004-11-19 10:25:12.150161640 +0100 +@@ -0,0 +1,10 @@ ++/* $Id: suspend.h,v 1.1 2003/10/13 20:56:47 dwmw2 Exp $ */ ++ ++#ifndef __MTD_COMPAT_VERSION_H__ ++#include <linux/version.h> ++ ++#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) ++#include_next <linux/suspend.h> ++#endif ++ ++#endif /* __MTD_COMPAT_VERSION_H__ */ +diff -Nurb linux-mips-2.4.27/include/linux/workqueue.h linux/include/linux/workqueue.h +--- linux-mips-2.4.27/include/linux/workqueue.h 1970-01-01 01:00:00.000000000 +0100 ++++ linux/include/linux/workqueue.h 2004-11-19 10:25:12.152161336 +0100 +@@ -0,0 +1,21 @@ ++/* ++ * 2.5 compatibility ++ * $Id: workqueue.h,v 1.1 2002/11/11 16:39:10 dwmw2 Exp $ ++ */ ++ ++#ifndef __MTD_COMPAT_WORKQUEUE_H__ ++#define __MTD_COMPAT_WORKQUEUE_H__ ++ ++#include <linux/version.h> ++ ++#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40) ++#include_next <linux/workqueue.h> ++#else ++#include <linux/tqueue.h> ++#define work_struct tq_struct ++#define schedule_work(x) schedule_task(x) ++#define flush_scheduled_work flush_scheduled_tasks ++#define INIT_WORK(x,y,z) INIT_TQUEUE(x,y,z) ++#endif ++ ++#endif /* __MTD_COMPAT_WORKQUEUE_H__ */ |