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 * - * $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 #include #include +#include #include #include #include @@ -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 @@ -21,6 +21,7 @@ #include #include #include +#include #include #include @@ -29,10 +30,14 @@ #include #include #include -#include +#include #include +#include + +/* #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; iMajorVersion != '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)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 && inumchips; 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 #include #include #include #include +#include #include #include @@ -23,16 +29,50 @@ #include #include #include +#include #include +#include #include #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<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 (inumeraseregions && (instr->addr + instr->len) >= regions[i].offset) + while (inumeraseregions && (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. 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 #include #include +#include #include #include #include +#include #include #include #include +#include #include #include -#include +#include 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; iMajorVersion != '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 #include #include #include +#include #include #include #include @@ -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; inumchips; 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 +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +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; iMajorVersion != '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 #include +#include #include #include -#include +#include #include +#include +#include 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 "); 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 +#include +#include #include #include #include +#include #include 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<size; - base += (1<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 +#include +#include #include +#include +#include +#include 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)<addrshift)) - #define flwrite(v,x) map->write8(map,v,chip->base+((x)<addrshift)) + #define flread(x) map_read8(map,chip->base+((x)<addrshift)) + #define flwrite(v,x) map_write8(map,v,chip->base+((x)<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<addrshift)-1))+((x)<addrshift)) - #define flwrite(v,x) map->write8(map,v,base+(off&((1<addrshift)-1))+((x)<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<addrshift)-1))+((x)<addrshift)) + #define flwrite(v,x) map_write8(map,v,base+(off&((1<addrshift)-1))+((x)<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 @@ -15,7 +17,9 @@ #include #include #include +#include +#include #include #include #include @@ -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; imfr == 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; inumchips; 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 - * $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 #include #include - +#include +#include #include - +#include 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 @@ -11,8 +11,10 @@ #include #include #include - +#include +#include #include +#include 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; ilen; 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 @@ -12,8 +12,10 @@ #include #include #include - +#include +#include #include +#include 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 * 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 #include #include +#include #include #include @@ -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 #include -#include +#include #include /* 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 "); 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 "); +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 * - * $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 @@ -25,6 +25,7 @@ #include #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 * - * $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 @@ -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 + * (c) 2002-2003 SnapGear Inc + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse + * + * $Id: doc2001plus.c,v 1.7 2003/07/11 07:36:22 dwmw2 Exp $ + * + * Released under GPL + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +/* #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 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 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 "); 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 */ -/* $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 + . */ #define DOC_SINGLE_DRIVER @@ -47,18 +45,15 @@ #include #include #include -#include -#include -#include #include #include -#include #include #include #include #include #include +#include /* 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 * @@ -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 @@ -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 "); +MODULE_AUTHOR("Maciej W. Rozycki "); 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 * * Copyright (c) 1999 Alexander Larsson @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -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 + * 07/2003 rewritten by Joern Engel + * + * 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=,, + * may be up to 63 characters. + * and can be octal, decimal or hexadecimal. If followed + * by "k", "M" or "G", the numbers will be interpreted as kilo, mega or + * gigabytes. + * + */ + +#include +#include +#include +#include +#include +#include +#include + +#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=,\""); + +/* + * 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=,\""); + + +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 "); +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 * - 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 #include -#include #include /*#define PSYCHO_DEBUG */ @@ -68,43 +68,13 @@ #include #include #include -#include +#include #include - -#if (LINUX_VERSION_CODE >= 0x20100) #include -#endif -#if (LINUX_VERSION_CODE >= 0x20303) #include -#endif +#include #include -/*====================================================================*/ -/* 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 /*====================================================================*/ @@ -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 = (0x100000mtd->size)?0x100000:part->mtd->size; + max_offset = (0x100000mbd.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 + * + * $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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 , David Woodhouse , Fabrice Bellard 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 #include #include +#include #include #include #include @@ -16,77 +17,59 @@ #include +#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 + * + */ + +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +/* + * 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 "); +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 #include #include +#include #include #include #include @@ -32,80 +33,27 @@ #include #include -__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 +#include +#include +#include + +#include +#include +#include + +#include +#include + +#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 "); +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 #include #include #include +#include #include #include #include @@ -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 * - * $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 @@ -19,6 +19,7 @@ #include #include #include +#include #include #include @@ -31,62 +32,10 @@ #include /* - * 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 * - * $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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include #include #include - -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) #include -#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;imodule = 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 #include #include +#include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 "); -MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board"); +MODULE_AUTHOR("Kári Davíðsson , Bastian Blank , Alexander Wild "); +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 #include #include #include +#include #include #include @@ -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 #include #include +#include #include #include #include @@ -36,7 +37,7 @@ #include /* -** 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 "); +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 #include #include +#include #include #include #include @@ -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 #include +#include #include #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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 + * 2003 Thomas Gleixner +*/ + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include + +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 "); +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#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 "); +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 #include #include +#include #include #include #include @@ -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; imodule = 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +// 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 #include #include +#include #include #include #include #include +#include #include #include #include -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 #include #include +#include +#include #include #include #include @@ -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 + * + * Copyright (C) 2002 Intel Corporation + * Copyright (C) 2003 MontaVista Software, Inc. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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 #include #include +#include #include #include #include @@ -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 + * + * 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 #include #include +#include #include #include #include @@ -13,123 +22,80 @@ #include #include -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 "); 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 +#include +#include +#include +#include +#include +#include +#include +#include + + +#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 "); +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 +#include +#include +#include +#include +#include + +#include + +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 #include #include +#include #include #include #include @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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 "); +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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -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 #include +#include #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 +#include +#include +#include + +#include +#include + +#include +#include +#include + +#include +#include + + +#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 * - * $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 #include #include +#include #include #include @@ -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 #include #include +#include #include #include @@ -24,6 +25,7 @@ #include #include +#include #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 #include #include +#include +#include #include #include #include #include - -#ifdef CONFIG_MTD_PARTITIONS #include -#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 #include #include +#include #include #include @@ -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. * + * 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 #include #include #include +#include #include #include @@ -40,96 +25,102 @@ #include +#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 "); +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 #include #include +#include #include #include #include @@ -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 * - * $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 @@ -11,278 +11,212 @@ #include #include #include +#include +#include +#include #include #include #include +#include #include +#include #include +#include +#include #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) + * + * + * + * 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 +#include +#include +#include +#include + +#include +#include +#include + +#ifdef CONFIG_MTD_PARTITIONS +#include +#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 "); +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 #include #include +#include #include #include #include @@ -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 * @@ -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 #include #include +#include #include #include #include @@ -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 - $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 #include #include +#include #include #include #include @@ -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 #include #include +#include #include #include #include @@ -18,60 +19,39 @@ #include -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 #include #include +#include #include #include @@ -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 #include +#include #include +#include #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 #include +#include #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 + * + * Interface to Linux 2.4 block layer for MTD 'translation layers'. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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<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<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; + } + + /* + dwmw2: BLOCK_SIZE_BITS has nothing to do with block devices + 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<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; itype != 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 "); +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 + * + * Interface to Linux 2.5 block layer for MTD 'translation layers'. + * + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; itype != 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 "); +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 + * (C) 1999-2003 David Woodhouse */ #include #include #include #include +#include +#include #include +#include #include -#include - -#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 -/* 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 -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 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 + * + * Simple read-only (writable only for RAM) mtdblock driver */ -#ifdef MTDBLOCK_DEBUG -#define DEBUGLVL debug -#endif - - -#include -#include - +#include +#include #include -#include - -#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 - -#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 - -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 et al."); +MODULE_AUTHOR("David Woodhouse "); 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 #include #include +#include #include +#include +#include +#include #ifdef CONFIG_DEVFS_FS #include @@ -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 * + * NAND support by Christian Gan + * * 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 @@ -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 "); 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 #include #include +#include #include #ifdef CONFIG_PROC_FS #include @@ -24,9 +25,15 @@ #include -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 * added support for read_oob, write_oob @@ -16,10 +16,11 @@ #include #include #include - +#include +#include #include #include - +#include /* 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 "); 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 * * 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 +#include #include #include #include @@ -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 + * + * 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 +#include +#include +#include +#include + +#include +#include +#include +#include + +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 "); +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 #include +#include #include #include #include @@ -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 #include #include +#include #include #include /* - * 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; iIO_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; iIO_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; iIO_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 , Thomas Gleixner "); +MODULE_AUTHOR ("Steven J. Hill , Thomas Gleixner "); 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 #include #include +#include /* * 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 "); +MODULE_AUTHOR("Steven J. Hill "); 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 +#include #include #include #include @@ -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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * 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 "); +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 +#include +#include +#include +#include +#include +#include +#include +#include + +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; iIO_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; iIO_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; iIO_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 "); +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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; iIO_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; iIO_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; iIO_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 "); +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 */ -/* $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 #include #include -#include +#include -#ifdef CONFIG_KMOD #include -#endif #include #include #include -#include +#include /* 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 -#include - -/* 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<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<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< 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<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<rq_dev)); - res = 0; /* fail */ - goto repeat; - } - - nftl = NFTLs[dev / (1<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 -#include #include -#include -#include -#include -#include #include #include -#include -#include #include #include #include -#include #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 #include +#include #include #include @@ -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 "); 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 + * Created by David Woodhouse * - * 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 -#include -#include #include #include -#include #include +#include +#include +#include #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 + * Created by David Woodhouse * - * 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 -#include +#include #include #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; inr_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 * - * 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 #include -#include #include +#include +#include #include +#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 * - * 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 /* _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 * - * 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 + * Created by David Woodhouse * - * 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 #include -#include /* for min() */ #include -#include #include +#include +#include #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 +#include 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 #include @@ -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 + +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 + * Created by David Woodhouse * - * 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 #include +#include #include -#include /* For completion */ +#include #include #include #include +#include #include "nodelist.h" -#include + +/* Urgh. Please tell me there's a nicer way of doing these. */ +#include +#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 + * Created by David Woodhouse * - * 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 #include #include -#include -#include -#include "nodelist.h" +#include #include +#include +#include +#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; ierase_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 + * Created by David Woodhouse * - * 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 #include -#include /* for min() */ #include #include +#include #include +#include +#include #include #include "nodelist.h" -#include 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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + * Created by David Woodhouse * - * 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 #include #include -#include -#include -#include #include -#include "nodelist.h" #include +#include +#include +#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 + * Created by David Woodhouse * - * 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 + * Created by David Woodhouse * - * 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 + * Created by David Woodhouse * - * 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 -#include +#include #include #include +#include +#include +#include +#include #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 + * Created by David Woodhouse * - * 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 #include - +#include +#include #include #include +#ifdef __ECOS +#include "os-ecos.h" +#else +#include /* 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 + * Created by David Woodhouse * - * 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 #include -#include #include -#include +#include +#include /* 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 :)) */ 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 + * + * 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 + +/* 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 + * Created by David Woodhouse * - * 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 + 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 + (C) 2002 David Woodhouse + + 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 +#include + +/* 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 + * Created by David Woodhouse * - * 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 #include -#include +#include +#include #include +#include #include "nodelist.h" -#include 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 + * Created by David Woodhouse * - * 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 #include #include +#include +#include #include -#include +#include #include "nodelist.h" -#include +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 + * Created by David Woodhouse * - * 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 +#include #include -#include #include #include -#include "nodelist.h" #include +#include +#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; inr_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 + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#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 + * Created by David Woodhouse * - * 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 #include #include -#include #include #include #include #include +#include #include #include #include -#include +#include +#include #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; inr_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 + * Created by David Woodhouse * - * 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 #include #include -#include #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 + * + * 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 +#include +#include +#include +#include +#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 + * Created by David Woodhouse * - * 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 #include -#include +#include +#include +#include #include #include "nodelist.h" -#include -/* 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; iwrite(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 + * + * 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 +#include +#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; iwrite(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 + * Created by David Woodhouse * - * 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 +/* 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 -#endif +#include +#include 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 #include +#include #include #include +#include +#include #include -#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 + * + * Interface to Linux block layer for MTD 'translation layers'. + * + */ + +#ifndef __MTD_TRANS_H__ +#define __MTD_TRANS_H__ + +#include + +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 +#include #include #include #include @@ -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 -#include -#ifndef LINUX_VERSION_CODE #include -#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 /* 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 -# 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 -# 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 -#endif // LINUX_VERSION_CODE < 0x020100 - - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) -#include -#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 -#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 +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 -#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 +#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 - -#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 -#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 -#include -#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 + 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 -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 +#include + +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 */ -/* $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 + * Copyright (C) 2002-2003 Greg Ungerer + * 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 +#include #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 #include #include +#include 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 +#include +#include + +#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 -#include #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 #include -#include -#include +#include +#include +#include /* 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 et al. + * + * Released under GPL + */ #ifndef __MTD_MTD_H__ #define __MTD_MTD_H__ @@ -9,7 +14,6 @@ #include #include #include -#include #include #include @@ -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 - * Steven J. Hill + * Steven J. Hill * Thomas Gleixner * - * $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 -#include +#include +#include +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 */ -/* $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 + */ #ifndef __MTD_NFTL_H__ #define __MTD_NFTL_H__ -#ifndef __BOOT__ #include -#endif +#include /* 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 + +#if defined(CONFIG_MTD_PHYSMAP) + +#include +#include +#include + +/* + * 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 + + 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 +#include + +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 + * 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 + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,5,40) +#include_next +#else +#define rb_node_s rb_node +#define rb_root_s rb_root + +#include + +/* 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 -#include - -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 + +#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,6,0) +#include_next +#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 + +#if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,40) +#include_next +#else +#include +#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__ */