diff options
| author | David Woodhouse <dwmw2@infradead.org> | 2003-05-28 18:03:33 +0100 |
|---|---|---|
| committer | David Woodhouse <dwmw2@infradead.org> | 2003-05-28 18:03:33 +0100 |
| commit | 5af017c0de780cb463b784ef909406ce397332ee (patch) | |
| tree | ecfae73f9c51082ebe4cbe6d228adcba21ca524d | |
| parent | 015498d534572f8a9c3bf5f1dfc02bd02bfb2c9d (diff) | |
MTD and JFFS2 update.
- JFFS2 bugfixes and performance improvements
- Support for 64-bit flash arrangements
- Optimise for linear mappings of flash, without out-of-line access functions
- New map drivers
- Updated NAND flash support, new board drivers
- Support for DiskOnChip Millennium Plus and INFTL translation layer
- Clean up all translation layers with a single blkdev helper library.
- Fix races in MTD device registration/deregistration
- Add support for new flash chips
- Clean up partition parsing code
More detailed comments in per-file changelogs.
145 files changed, 18441 insertions, 7896 deletions
diff --git a/drivers/mtd/Kconfig b/drivers/mtd/Kconfig index 692d7b991124..5fb011c37398 100644 --- a/drivers/mtd/Kconfig +++ b/drivers/mtd/Kconfig @@ -1,4 +1,4 @@ -# $Id: Config.in,v 1.74 2002/04/23 13:52:14 mag Exp $ +# $Id: Kconfig,v 1.3 2003/05/28 11:02:23 dwmw2 Exp $ menu "Memory Technology Devices (MTD)" @@ -199,13 +199,28 @@ config NFTL not use it. config NFTL_RW - bool "Write support for NFTL (BETA)" + bool "Write support for NFTL" depends on NFTL help - If you're lucky, this will actually work. Don't whinge if it - doesn't. Send mail to the MTD mailing list - <linux-mtd@lists.infradead.org> if you want to help to make it more - reliable. + Support for writing to the NAND Flash Translation Layer, as used + on the DiskOnChip. + +config INFTL + tristate "INFTL (Inverse NAND Flash Translation Layer) support" + depends on MTD + ---help--- + This provides support for the Inverse NAND Flash Translation + Layer which is used on M-Systems' newer DiskOnChip devices. It + uses a kind of pseudo-file system on a flash device to emulate + a block device with 512-byte sectors, on top of which you put + a 'normal' file system. + + You may find that the algorithms used in this code are patented + unless you live in the Free World where software patents aren't + legal - in the USA you are only permitted to use this on DiskOnChip + hardware, although under the terms of the GPL you're obviously + permitted to copy, modify and distribute the code as you wish. Just + not use it. source "drivers/mtd/chips/Kconfig" diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 99d84bed9b3c..2915a535dceb 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -1,10 +1,7 @@ # # Makefile for the memory technology device drivers. # -# Based on: -# $Id: Makefile,v 1.66 2002/04/23 13:52:14 mag Exp $ - -obj-y += chips/ maps/ devices/ nand/ +# $Id: Makefile.common,v 1.2 2003/05/23 11:38:29 dwmw2 Exp $ # *** BIG UGLY NOTE *** # @@ -27,14 +24,18 @@ obj-$(CONFIG_MTD) += mtdcore.o obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.o -obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdline.o +obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdlinepart.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o # '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 -nftl-objs := nftlcore.o nftlmount.o +obj-y += chips/ maps/ devices/ nand/ diff --git a/drivers/mtd/afs.c b/drivers/mtd/afs.c index 23d980122df3..6640ebc8e994 100644 --- a/drivers/mtd/afs.c +++ b/drivers/mtd/afs.c @@ -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.11 2003/05/16 17:08:24 dwmw2 Exp $ ======================================================================*/ @@ -125,7 +125,9 @@ afs_read_iis(struct mtd_info *mtd, struct image_info_struct *iis, u_int ptr) 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; @@ -227,7 +229,25 @@ int parse_afs_partitions(struct mtd_info *mtd, struct mtd_partition **pparts) 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 --git a/drivers/mtd/chips/Kconfig b/drivers/mtd/chips/Kconfig index adf674fccbb0..200db7ca7692 100644 --- a/drivers/mtd/chips/Kconfig +++ b/drivers/mtd/chips/Kconfig @@ -1,5 +1,5 @@ -# drivers/mtd/chips/Config.in -# $Id: Config.in,v 1.12 2001/09/23 15:35:21 dwmw2 Exp $ +# drivers/mtd/chips/Kconfig +# $Id: Kconfig,v 1.2 2003/05/28 10:59:06 dwmw2 Exp $ menu "RAM/ROM/Flash chip drivers" depends on MTD!=n @@ -15,7 +15,6 @@ config MTD_CFI option. Visit <http://www.amd.com/products/nvd/overview/cfi.html> for more information on CFI. -#dep_tristate ' Detect non-CFI Intel-compatible flash chips' CONFIG_MTD_INTELPROBE $CONFIG_MTD config MTD_JEDECPROBE tristate "Detect non-CFI AMD/JEDEC-compatible flash chips" depends on MTD @@ -145,7 +144,15 @@ config MTD_CFI_AMDSTD The Common Flash Interface defines a number of different command sets which a CFI-compliant chip may claim to implement. This code provides support for one of those command sets, used on chips - chips including the AMD Am29LV320. + including the AMD Am29LV320. + +config MTD_CFI_STAA + tristate "Support for ST (Advanced Architecture) flash chips" + depends on MTD_GEN_PROBE + help + The Common Flash Interface defines a number of different command + sets which a CFI-compliant chip may claim to implement. This code + provides support for one of those command sets. config MTD_RAM tristate "Support for RAM chips in bus mapping" @@ -177,10 +184,10 @@ config MTD_OBSOLETE_CHIPS help This option does not enable any code directly, but will allow you to select some other chip drivers which are now considered obsolete, - because the generic CONFIG_JEDEC_PROBE code above should now detect + because the generic CONFIG_JEDECPROBE code above should now detect the chips which are supported by these drivers, and allow the generic CFI-compatible drivers to drive the chips. Say 'N' here unless you have - already tried the CONFIG_JEDEC_PROBE method and reported its failure + already tried the CONFIG_JEDECPROBE method and reported its failure to the MTD mailing list at <linux-mtd@lists.infradead.org> config MTD_AMDSTD @@ -209,8 +216,7 @@ config MTD_JEDEC programming flash. It is commonly used in older AMD chips. It is only called JEDEC because the JEDEC association <http://www.jedec.org/> distributes the identification codes for the - chips. WARNING!!!! This code does not compile and is incomplete as - are the specific JEDEC devices drivers. + chips. endmenu diff --git a/drivers/mtd/chips/Makefile b/drivers/mtd/chips/Makefile index 0ad996f50e59..7293717aff78 100644 --- a/drivers/mtd/chips/Makefile +++ b/drivers/mtd/chips/Makefile @@ -1,7 +1,7 @@ # # linux/drivers/chips/Makefile # -# $Id: Makefile,v 1.7 2001/10/05 06:53:51 dwmw2 Exp $ +# $Id: Makefile.common,v 1.1 2003/05/21 15:00:01 dwmw2 Exp $ # *** BIG UGLY NOTE *** # @@ -13,10 +13,10 @@ obj-$(CONFIG_MTD) += chipreg.o obj-$(CONFIG_MTD_AMDSTD) += amd_flash.o obj-$(CONFIG_MTD_CFI) += cfi_probe.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 obj-$(CONFIG_MTD_GEN_PROBE) += gen_probe.o -obj-$(CONFIG_MTD_INTELPROBE) += intel_probe.o obj-$(CONFIG_MTD_JEDEC) += jedec.o obj-$(CONFIG_MTD_JEDECPROBE) += jedec_probe.o obj-$(CONFIG_MTD_RAM) += map_ram.o diff --git a/drivers/mtd/chips/amd_flash.c b/drivers/mtd/chips/amd_flash.c index 83deb5ed90b8..78cdbfa561b1 100644 --- a/drivers/mtd/chips/amd_flash.c +++ b/drivers/mtd/chips/amd_flash.c @@ -3,7 +3,7 @@ * * Author: Jonas Holmberg <jonas.holmberg@axis.com> * - * $Id: amd_flash.c,v 1.15 2001/10/02 15:05:11 dwmw2 Exp $ + * $Id: amd_flash.c,v 1.22 2003/05/28 13:47:19 dwmw2 Exp $ * * Copyright (c) 2001 Axis Communications AB * @@ -52,6 +52,7 @@ /* Manufacturers */ #define MANUFACTURER_AMD 0x0001 +#define MANUFACTURER_ATMEL 0x001F #define MANUFACTURER_FUJITSU 0x0004 #define MANUFACTURER_ST 0x0020 #define MANUFACTURER_SST 0x00BF @@ -67,10 +68,14 @@ #define AM29BDS323D 0x22D1 #define AM29BDS643D 0x227E +/* Atmel */ +#define AT49xV16x 0x00C0 +#define AT49xV16xT 0x00C2 /* Fujitsu */ #define MBM29LV160TE 0x22C4 #define MBM29LV160BE 0x2249 +#define MBM29LV800BB 0x225B /* ST - www.st.com */ #define M29W800T 0x00D7 @@ -120,10 +125,10 @@ static struct mtd_info *amd_flash_probe(struct map_info *map); 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 }; @@ -135,11 +140,11 @@ static const char im_name[] = "amd_flash"; 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; @@ -148,11 +153,11 @@ static inline __u32 wide_read(struct map_info *map, __u32 addr) 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); } } @@ -419,10 +424,7 @@ static int probe_new_chip(struct mtd_info *mtd, __u32 base, 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, @@ -431,9 +433,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .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 } + { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_AMD, @@ -442,9 +444,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .size = 0x00200000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } } }, { @@ -455,9 +457,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .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 } + { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_FUJITSU, @@ -467,9 +469,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .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 } + { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_TOSHIBA, @@ -478,9 +480,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .size = 0x00200000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } } }, { @@ -490,9 +492,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .size = 0x00200000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } } }, { @@ -502,9 +504,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .size = 0x00100000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } } }, { @@ -514,9 +516,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .size = 0x00100000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 15 } } }, { @@ -527,9 +529,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .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 } + { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_AMD, @@ -539,9 +541,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .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 } + { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_AMD, @@ -551,9 +553,21 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .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 } + { .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_ST, @@ -563,9 +577,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .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 } + { .offset = 0x0F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x0F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x0FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_ST, @@ -575,9 +589,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .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 } + { .offset = 0x1F0000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x1F8000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x1FC000, .erasesize = 0x04000, .numblocks = 1 } } }, { .mfr_id = MANUFACTURER_ST, @@ -586,9 +600,9 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .size = 0x00200000, .numeraseregions = 4, .regions = { - { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, - { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, - { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, + { .offset = 0x000000, .erasesize = 0x04000, .numblocks = 1 }, + { .offset = 0x004000, .erasesize = 0x02000, .numblocks = 2 }, + { .offset = 0x008000, .erasesize = 0x08000, .numblocks = 1 }, { .offset = 0x010000, .erasesize = 0x10000, .numblocks = 31 } } }, { @@ -600,7 +614,7 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 48 }, { .offset = 0x300000, .erasesize = 0x10000, .numblocks = 15 }, - { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, + { .offset = 0x3f0000, .erasesize = 0x02000, .numblocks = 8 }, } }, { .mfr_id = MANUFACTURER_AMD, @@ -611,7 +625,27 @@ static struct mtd_info *amd_flash_probe(struct map_info *map) .regions = { { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 96 }, { .offset = 0x600000, .erasesize = 0x10000, .numblocks = 31 }, - { .offset = 0x7f0000, .erasesize = 0x02000, .numblocks = 8 }, + { .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 = AT49xV16xT, + .name = "Atmel AT49xV16xT", + .size = 0x00200000, + .numeraseregions = 2, + .regions = { + { .offset = 0x000000, .erasesize = 0x10000, .numblocks = 31 }, + { .offset = 0x1F0000, .erasesize = 0x02000, .numblocks = 8 } } } }; @@ -785,7 +819,7 @@ retry: 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); @@ -947,7 +981,7 @@ static int amd_flash_write(struct mtd_info *mtd, loff_t to , size_t len, 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) @@ -1020,7 +1054,7 @@ static int amd_flash_write(struct mtd_info *mtd, loff_t to , size_t len, 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--) { @@ -1141,7 +1175,7 @@ retry: __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 --git a/drivers/mtd/chips/cfi_cmdset_0001.c b/drivers/mtd/chips/cfi_cmdset_0001.c index 460b9e03330b..e395f642b50a 100644 --- a/drivers/mtd/chips/cfi_cmdset_0001.c +++ b/drivers/mtd/chips/cfi_cmdset_0001.c @@ -4,7 +4,7 @@ * * (C) 2000 Red Hat. GPL'd * - * $Id: cfi_cmdset_0001.c,v 1.87 2001/10/02 15:05:11 dwmw2 Exp $ + * $Id: cfi_cmdset_0001.c,v 1.123 2003/05/28 12:51:48 dwmw2 Exp $ * * * 10/10/2000 Nicolas Pitre <nico@cam.org> @@ -13,12 +13,15 @@ * - scalability vs code size is completely set at compile-time * (see include/linux/mtd/cfi.h for selection) * - optimized write buffer method + * 02/05/2002 Christopher Hoover <ch@hpl.hp.com>/<ch@murgatroid.com> + * - reworked lock/unlock/erase support for var size flash */ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/init.h> #include <asm/io.h> #include <asm/byteorder.h> @@ -27,10 +30,16 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/mtd/map.h> -#include <linux/mtd/cfi.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/compatmac.h> +#include <linux/mtd/cfi.h> + +// 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 *); +static int cfi_intelext_read_fact_prot_reg (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int cfi_intelext_write_words(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); static int cfi_intelext_erase_varsize(struct mtd_info *, struct erase_info *); @@ -46,6 +55,16 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *, int); static struct mtd_info *cfi_intelext_setup (struct map_info *); +static int cfi_intelext_point (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char **mtdbuf); +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, @@ -59,6 +78,7 @@ static struct mtd_chip_driver cfi_intelext_chipdrv = { #ifdef DEBUG_CFI_FEATURES static void cfi_tell_features(struct cfi_pri_intelext *extp) { + int i; printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); @@ -110,7 +130,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) int i; __u32 base = cfi->chips[0].start; - if (cfi->cfi_mode) { + 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 @@ -140,7 +160,7 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) } if (extp->MajorVersion != '1' || - (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { + (extp->MinorVersion < '0' || extp->MinorVersion > '3')) { printk(KERN_WARNING " Unknown IntelExt Extended Query " "version %c.%c.\n", extp->MajorVersion, extp->MinorVersion); @@ -149,26 +169,38 @@ struct mtd_info *cfi_cmdset_0001(struct map_info *map, int primary) } /* Do some byteswapping if necessary */ - extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); - extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask); - + extp->FeatureSupport = le32_to_cpu(extp->FeatureSupport); + extp->BlkStatusRegMask = le16_to_cpu(extp->BlkStatusRegMask); + extp->ProtRegAddr = le16_to_cpu(extp->ProtRegAddr); + #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; - } + cfi->cmdset_priv = extp; + } for (i=0; i< cfi->numchips; i++) { - cfi->chips[i].word_write_time = 128; - cfi->chips[i].buffer_write_time = 128; - cfi->chips[i].erase_time = 1024; + cfi->chips[i].word_write_time = 1<<cfi->cfiq->WordWriteTimeoutTyp; + cfi->chips[i].buffer_write_time = 1<<cfi->cfiq->BufWriteTimeoutTyp; + cfi->chips[i].erase_time = 1<<cfi->cfiq->BlockEraseTimeoutTyp; + cfi->chips[i].ref_point_counter = 0; } map->fldrv = &cfi_intelext_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); @@ -188,8 +220,7 @@ static struct mtd_info *cfi_intelext_setup(struct map_info *map) if (!mtd) { printk(KERN_ERR "Failed to allocate memory for MTD device\n"); - kfree(cfi->cmdset_priv); - return NULL; + goto setup_err; } memset(mtd, 0, sizeof(*mtd)); @@ -202,9 +233,7 @@ static struct mtd_info *cfi_intelext_setup(struct map_info *map) * mtd->numeraseregions, GFP_KERNEL); if (!mtd->eraseregions) { printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); - kfree(cfi->cmdset_priv); - kfree(mtd); - return NULL; + goto setup_err; } for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { @@ -221,34 +250,39 @@ static struct mtd_info *cfi_intelext_setup(struct map_info *map) mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; } offset += (ersize * ernum); - } + } - if (offset != devsize) { - /* Argh */ - 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; - } + if (offset != devsize) { + /* Argh */ + printk(KERN_WARNING "Sum of regions (%lx) != total size of set of interleaved chips (%lx)\n", offset, devsize); + goto setup_err; + } - for (i=0; i<mtd->numeraseregions;i++){ - printk(KERN_DEBUG "%d: offset=0x%x,size=0x%x,blocks=%d\n", - i,mtd->eraseregions[i].offset, - mtd->eraseregions[i].erasesize, - mtd->eraseregions[i].numblocks); - } + for (i=0; i<mtd->numeraseregions;i++){ + printk(KERN_DEBUG "%d: offset=0x%x,size=0x%x,blocks=%d\n", + i,mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + } /* Also select the correct geometry setup too */ - mtd->erase = cfi_intelext_erase_varsize; + mtd->erase = cfi_intelext_erase_varsize; mtd->read = cfi_intelext_read; - if ( cfi->cfiq->BufWriteTimeoutTyp ) { - //printk(KERN_INFO "Using buffer write method\n" ); + + if (map_is_linear(map)) { + mtd->point = cfi_intelext_point; + mtd->unpoint = cfi_intelext_unpoint; + } + + if ( cfi->cfiq->BufWriteTimeoutTyp && !FORCE_WORD_WRITE) { + printk(KERN_INFO "Using buffer write method\n" ); mtd->write = cfi_intelext_write_buffers; } else { - //printk(KERN_INFO "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; + mtd->read_fact_prot_reg = cfi_intelext_read_fact_prot_reg; mtd->sync = cfi_intelext_sync; mtd->lock = cfi_intelext_lock; mtd->unlock = cfi_intelext_unlock; @@ -256,127 +290,130 @@ static struct mtd_info *cfi_intelext_setup(struct map_info *map) 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: + if(mtd) { + if(mtd->eraseregions) + kfree(mtd->eraseregions); + kfree(mtd); + } + kfree(cfi->cmdset_priv); + kfree(cfi->cfiq); + return NULL; } +/* + * *********** CHIP ACCESS FUNCTIONS *********** + */ -static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +static int get_chip(struct map_info *map, struct flchip *chip, unsigned long adr, int mode) { - __u32 status, status_OK; - unsigned long timeo; DECLARE_WAITQUEUE(wait, current); - int suspended = 0; - 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_bh(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_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: + return 0; + case FL_ERASING: - 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); + 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), cmd_addr); + cfi_write(map, CMD(0x70), adr); chip->oldstate = FL_ERASING; chip->state = FL_ERASE_SUSPENDING; - // printk("Erase suspending at 0x%lx\n", cmd_addr); + chip->erase_suspended = 1; for (;;) { - status = cfi_read(map, cmd_addr); + status = cfi_read(map, adr); if ((status & status_OK) == status_OK) - break; - + 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); + /* 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; - spin_unlock_bh(chip->mutex); + chip->oldstate = FL_READY; printk(KERN_ERR "Chip not ready after erase " "suspended: status = 0x%x\n", status); return -EIO; } - - spin_unlock_bh(chip->mutex); + + spin_unlock(chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); + /* Nobody will touch it while it's in state FL_ERASE_SUSPENDING. + So we can just loop here. */ } - - suspended = 1; - cfi_write(map, CMD(0xff), cmd_addr); - chip->state = FL_READY; - break; - -#if 0 - case FL_WRITING: - /* Not quite yet */ -#endif - - case FL_READY: - break; - - 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; - break; - } - - /* Urgh. Chip not yet ready to talk to us. */ - if (time_after(jiffies, timeo)) { - spin_unlock_bh(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %x\n", status); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - cfi_udelay(1); - goto retry; + case FL_POINT: + /* Only if there's no operation suspended... */ + if (mode == FL_READY && chip->oldstate == FL_READY) + return 0; 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_bh(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; + spin_lock(chip->mutex); + goto resettime; } +} - map->copy_from(map, buf, adr, len); +static void put_chip(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; - if (suspended) { + 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 @@ -387,12 +424,167 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof 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); - } + cfi_write(map, CMD(0xd0), adr); + cfi_write(map, CMD(0x70), adr); + chip->oldstate = FL_READY; + chip->state = FL_ERASING; + break; + case FL_READY: + /* 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); - spin_unlock_bh(chip->mutex); +} + +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 ret; +} + +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; + unsigned long ofs; + int chipnum; + int ret = 0; + + if (from + len > mtd->size) + return -EINVAL; + + *mtdbuf = (void *)map->virt + from; + if(*mtdbuf == NULL) + return -EINVAL; /* can not point this region */ + *retlen = 0; + + /* Now lock the chip(s) to POINT state */ + + /* ofs: offset within the first chip that the first read should start */ + chipnum = (from >> cfi->chipshift); + ofs = from - (chipnum << cfi->chipshift); + + while (len) { + unsigned long thislen; + + if (chipnum >= cfi->numchips) + break; + + if ((len + ofs -1) >> cfi->chipshift) + thislen = (1<<cfi->chipshift) - ofs; + else + thislen = len; + + ret = do_point_onechip(map, &cfi->chips[chipnum], ofs, thislen); + if (ret) + break; + + *retlen += thislen; + len -= thislen; + + ofs = 0; + chipnum++; + } + return 0; +} + +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; + + /* Now unlock the chip(s) POINT state */ + + /* ofs: offset within the first chip that the first read should start */ + chipnum = (from >> cfi->chipshift); + ofs = from - (chipnum << cfi->chipshift); + + while (len) { + unsigned long thislen; + struct flchip *chip; + + chip = &cfi->chips[chipnum]; + if (chipnum >= cfi->numchips) + break; + + if ((len + ofs -1) >> cfi->chipshift) + thislen = (1<<cfi->chipshift) - ofs; + else + thislen = len; + + spin_lock(chip->mutex); + if (chip->state == FL_POINT) { + chip->ref_point_counter--; + if(chip->ref_point_counter == 0) + chip->state = FL_READY; + } else + 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; + ofs = 0; + chipnum++; + } +} + +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); + + spin_lock(chip->mutex); + ret = get_chip(map, chip, cmd_addr, FL_READY); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } + + if (chip->state != FL_POINT && chip->state != FL_READY) { + cfi_write(map, CMD(0xff), cmd_addr); + + chip->state = FL_READY; + } + + map_copy_from(map, buf, adr, len); + + put_chip(map, chip, cmd_addr); + + spin_unlock(chip->mutex); return 0; } @@ -435,64 +627,115 @@ static int cfi_intelext_read (struct mtd_info *mtd, loff_t from, size_t len, siz return ret; } -static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum) +static int cfi_intelext_read_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf, int base_offst, int reg_sz) { + struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; - __u32 status, status_OK; - unsigned long timeo; - DECLARE_WAITQUEUE(wait, current); - int z; + struct cfi_pri_intelext *extp = cfi->cmdset_priv; + struct flchip *chip; + int ofs_factor = cfi->interleave * cfi->device_type; + int count = len; + int chip_num, offst; + int ret; - adr += chip->start; + chip_num = ((unsigned int)from/reg_sz); + offst = from - (reg_sz*chip_num)+base_offst; - /* Let's determine this according to the interleave only once */ - status_OK = CMD(0x80); + while (count) { + /* Calculate which chip & protection register offset we need */ - timeo = jiffies + HZ; - retry: - spin_lock_bh(chip->mutex); + if (chip_num >= cfi->numchips) + goto out; - /* 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; + chip = &cfi->chips[chip_num]; - case FL_CFI_QUERY: - case FL_JEDEC_QUERY: - cfi_write(map, CMD(0x70), adr); - chip->state = FL_STATUS; + spin_lock(chip->mutex); + ret = get_chip(map, chip, chip->start, FL_JEDEC_QUERY); + if (ret) { + spin_unlock(chip->mutex); + return (len-count)?:ret; + } - 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_bh(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in read\n"); - return -EIO; + if (chip->state != FL_JEDEC_QUERY) { + cfi_write(map, CMD(0x90), chip->start); + chip->state = FL_JEDEC_QUERY; } - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - cfi_udelay(1); - goto retry; + while (count && ((offst-base_offst) < reg_sz)) { + *buf = map_read8(map,(chip->start+((extp->ProtRegAddr+1)*ofs_factor)+offst)); + buf++; + offst++; + count--; + } - 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); - spin_unlock_bh(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; + put_chip(map, chip, chip->start); + spin_unlock(chip->mutex); + + /* Move on to the next chip */ + chip_num++; + offst = base_offst; + } + + out: + return len-count; +} + +static int cfi_intelext_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp=cfi->cmdset_priv; + int base_offst,reg_sz; + + /* Check that we actually have some protection registers */ + if(!(extp->FeatureSupport&64)){ + printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); + return 0; + } + + base_offst=(1<<extp->FactProtRegSize); + reg_sz=(1<<extp->UserProtRegSize); + + return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); +} + +static int cfi_intelext_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + struct cfi_pri_intelext *extp=cfi->cmdset_priv; + int base_offst,reg_sz; + + /* Check that we actually have some protection registers */ + if(!(extp->FeatureSupport&64)){ + printk(KERN_WARNING "%s: This flash device has no protection data to read!\n",map->name); + return 0; + } + + base_offst=0; + reg_sz=(1<<extp->FactProtRegSize); + + return cfi_intelext_read_prot_reg(mtd, from, len, retlen, buf, base_offst, reg_sz); +} + + +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; + cfi_word status, status_OK; + unsigned long timeo; + int z, ret=0; + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_WRITING); + if (ret) { + spin_unlock(chip->mutex); + return ret; } ENABLE_VPP(map); @@ -500,22 +743,24 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned cfi_write(map, datum, adr); chip->state = FL_WRITING; - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); cfi_udelay(chip->word_write_time); - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); timeo = jiffies + (HZ/2); z = 0; 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_bh(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); continue; } @@ -526,17 +771,16 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; - DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in word write\n"); - return -EIO; + ret = -EIO; + goto out; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); z++; cfi_udelay(1); - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); } if (!z) { chip->word_write_time--; @@ -547,7 +791,6 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned chip->word_write_time++; /* Done and happy. */ - DISABLE_VPP(map); chip->state = FL_STATUS; /* check for lock bit */ if (status & CMD(0x02)) { @@ -555,13 +798,13 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned cfi_write(map, CMD(0x50), adr); /* put back into read status register mode */ cfi_write(map, CMD(0x70), adr); - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - return -EROFS; + ret = -EROFS; } - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - return 0; + out: + put_chip(map, chip, adr); + spin_unlock(chip->mutex); + + return ret; } @@ -585,8 +828,8 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le unsigned long bus_ofs = ofs & ~(CFIDEV_BUSWIDTH-1); int gap = ofs - bus_ofs; int i = 0, n = 0; - u_char tmp_buf[4]; - __u32 datum; + u_char tmp_buf[8]; + cfi_word datum; while (gap--) tmp_buf[i++] = 0xff; @@ -599,6 +842,8 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le datum = *(__u16*)tmp_buf; } else if (cfi_buswidth_is_4()) { datum = *(__u32*)tmp_buf; + } else if (cfi_buswidth_is_8()) { + datum = *(__u64*)tmp_buf; } else { return -EINVAL; /* should never happen, but be safe */ } @@ -621,7 +866,7 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le } while(len >= CFIDEV_BUSWIDTH) { - __u32 datum; + cfi_word datum; if (cfi_buswidth_is_1()) { datum = *(__u8*)buf; @@ -629,6 +874,8 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le datum = *(__u16*)buf; } else if (cfi_buswidth_is_4()) { datum = *(__u32*)buf; + } else if (cfi_buswidth_is_8()) { + datum = *(__u64*)buf; } else { return -EINVAL; } @@ -653,8 +900,8 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le 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; while (len--) tmp_buf[i++] = buf[n++]; @@ -665,6 +912,8 @@ static int cfi_intelext_write_words (struct mtd_info *mtd, loff_t to , size_t le datum = *(__u16*)tmp_buf; } else if (cfi_buswidth_is_4()) { datum = *(__u32*)tmp_buf; + } else if (cfi_buswidth_is_8()) { + datum = *(__u64*)tmp_buf; } else { return -EINVAL; /* should never happen, but be safe */ } @@ -685,10 +934,9 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, unsigned long adr, const u_char *buf, int len) { struct cfi_private *cfi = map->fldrv_priv; - __u32 status, status_OK; + cfi_word status, status_OK; unsigned long cmd_adr, timeo; - DECLARE_WAITQUEUE(wait, current); - int wbufsize, z; + int wbufsize, z, ret=0; wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; adr += chip->start; @@ -697,74 +945,52 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); - timeo = jiffies + HZ; - retry: - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); + ret = get_chip(map, chip, cmd_adr, FL_WRITING); + if (ret) { + spin_unlock(chip->mutex); + return ret; + } - /* 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: + if (chip->state != FL_STATUS) 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)) { - spin_unlock_bh(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in buffer write\n"); - return -EIO; - } - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - cfi_udelay(1); - goto retry; + status = cfi_read(map, cmd_adr); - 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); - spin_unlock_bh(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; + /* §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 + we're just pissing in the wind */ + if (status & CMD(0x30)) { + printk(KERN_WARNING "SR.4 or SR.5 bits set in buffer write (status %x). Clearing.\n", status); + cfi_write(map, CMD(0x50), cmd_adr); + cfi_write(map, CMD(0x70), cmd_adr); } - ENABLE_VPP(map); - cfi_write(map, CMD(0xe8), cmd_adr); chip->state = FL_WRITING_TO_BUFFER; z = 0; for (;;) { + cfi_write(map, CMD(0xe8), cmd_adr); + status = cfi_read(map, cmd_adr); if ((status & status_OK) == status_OK) break; - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); if (++z > 20) { /* Argh. Not ready for write to buffer */ cfi_write(map, CMD(0x70), cmd_adr); chip->state = FL_STATUS; - DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); - printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %x, status = %x\n", status, cfi_read(map, cmd_adr)); - return -EIO; + 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); + cfi_write(map, CMD(0x70), cmd_adr); + ret = -EIO; + goto out; } } @@ -774,36 +1000,39 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* 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 if (cfi_buswidth_is_8()) { + map_write64 (map, *((__u64*)buf)++, adr+z); } else { - DISABLE_VPP(map); - return -EINVAL; + ret = -EINVAL; + goto out; } } /* GO GO GO */ cfi_write(map, CMD(0xd0), cmd_adr); chip->state = FL_WRITING; - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); cfi_udelay(chip->buffer_write_time); - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); timeo = jiffies + (HZ/2); z = 0; 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_bh(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); timeo = jiffies + (HZ / 2); /* FIXME */ - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); continue; } @@ -814,17 +1043,16 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, /* OK Still waiting */ if (time_after(jiffies, timeo)) { chip->state = FL_STATUS; - DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); - return -EIO; + ret = -EIO; + goto out; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); cfi_udelay(1); z++; - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); } if (!z) { chip->buffer_write_time--; @@ -835,21 +1063,21 @@ static inline int do_write_buffer(struct map_info *map, struct flchip *chip, chip->buffer_write_time++; /* Done and happy. */ - DISABLE_VPP(map); - chip->state = FL_STATUS; + chip->state = FL_STATUS; + /* check for lock bit */ if (status & CMD(0x02)) { /* clear status */ cfi_write(map, CMD(0x50), cmd_adr); /* put back into read status register mode */ cfi_write(map, CMD(0x70), adr); - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - return -EROFS; + ret = -EROFS; } - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); - return 0; + + out: + put_chip(map, chip, cmd_adr); + spin_unlock(chip->mutex); + return ret; } static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, @@ -928,11 +1156,102 @@ static int cfi_intelext_write_buffers (struct mtd_info *mtd, loff_t to, return 0; } +typedef int (*varsize_frob_t)(struct map_info *map, struct flchip *chip, + unsigned long adr, void *thunk); + +static int cfi_intelext_varsize_frob(struct mtd_info *mtd, varsize_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; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + + if (ofs > mtd->size) + return -EINVAL; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + */ + + i = 0; + + /* Skip all erase regions which are ended before the start of + the requested erase. Actually, to save on the calculations, + we skip to the first erase region which starts after the + start of the requested erase, and then go back one. + */ + + while (i < mtd->numeraseregions && ofs >= regions[i].offset) + i++; + i--; + + /* OK, now i is pointing at the erase region in which this + erase request starts. Check the start of the requested + erase range is aligned with the erase size which is in + effect here. + */ + + if (ofs & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ + first = i; + + /* Next, check that the end of the requested erase is aligned + * with the erase region at that address. + */ + + while (i<mtd->numeraseregions && (ofs + len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which + the address actually falls + */ + i--; + + if ((ofs + len) & (regions[i].erasesize-1)) + return -EINVAL; + + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); + + i=first; + + while(len) { + ret = (*frob)(map, &cfi->chips[chipnum], adr, thunk); + + if (ret) + return ret; + + adr += regions[i].erasesize; + len -= regions[i].erasesize; + + if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) + i++; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + return 0; +} + -static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +static int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; - __u32 status, status_OK; + cfi_word status, status_OK; unsigned long timeo; int retries = 3; DECLARE_WAITQUEUE(wait, current); @@ -943,45 +1262,12 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u /* Let's determine this according to the interleave only once */ status_OK = CMD(0x80); - timeo = jiffies + HZ; -retry: - spin_lock_bh(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_bh(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - 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); - spin_unlock_bh(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; + retry: + spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_ERASING); + if (ret) { + spin_unlock(chip->mutex); + return ret; } ENABLE_VPP(map); @@ -992,10 +1278,12 @@ retry: cfi_write(map, CMD(0x20), adr); cfi_write(map, CMD(0xD0), adr); chip->state = FL_ERASING; - - spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); - spin_lock_bh(chip->mutex); + chip->erase_suspended = 0; + + spin_unlock(chip->mutex); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout((chip->erase_time*HZ)/(2*1000)); + spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ @@ -1006,13 +1294,18 @@ retry: /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); add_wait_queue(&chip->wq, &wait); - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); schedule(); remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + (HZ*20); /* FIXME */ - spin_lock_bh(chip->mutex); + 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; + } status = cfi_read(map, adr); if ((status & status_OK) == status_OK) @@ -1022,16 +1315,21 @@ retry: if (time_after(jiffies, timeo)) { cfi_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + printk(KERN_ERR "waiting for erase at %08lx to complete timed out. Xstatus = %llx, status = %llx.\n", + adr, (__u64)status, (__u64)cfi_read(map, adr)); + /* Clear status bits */ + cfi_write(map, CMD(0x50), adr); + cfi_write(map, CMD(0x70), adr); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); return -EIO; } /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - cfi_udelay(1); - spin_lock_bh(chip->mutex); + spin_unlock(chip->mutex); + set_current_state(TASK_UNINTERRUPTIBLE); + schedule_timeout(1); + spin_lock(chip->mutex); } DISABLE_VPP(map); @@ -1050,124 +1348,52 @@ retry: for (i = 1; i<CFIDEV_INTERLEAVE; i++) { chipstatus |= status >> (cfi->device_type * 8); } - printk(KERN_WARNING "Status is not identical for all chips: 0x%x. Merging to give 0x%02x\n", status, chipstatus); + printk(KERN_WARNING "Status is not identical for all chips: 0x%llx. Merging to give 0x%02x\n", (__u64)status, chipstatus); } /* Reset the error bits */ cfi_write(map, CMD(0x50), adr); cfi_write(map, CMD(0x70), adr); if ((chipstatus & 0x30) == 0x30) { - printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status); + printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%llx\n", (__u64)status); ret = -EIO; } else if (chipstatus & 0x02) { /* Protection bit set */ ret = -EROFS; } else if (chipstatus & 0x8) { /* Voltage */ - printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", status); + printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%llx\n", (__u64)status); ret = -EIO; } else if (chipstatus & 0x20) { if (retries--) { - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, status); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx. Retrying...\n", adr, (__u64)status); timeo = jiffies + HZ; chip->state = FL_STATUS; - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); goto retry; } - printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, status); + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%llx\n", adr, (__u64)status); ret = -EIO; } } wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); return ret; } int cfi_intelext_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) -{ struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr, len; - int chipnum, ret = 0; - int i, first; - struct mtd_erase_region_info *regions = mtd->eraseregions; - - if (instr->addr > mtd->size) - return -EINVAL; - - if ((instr->len + instr->addr) > mtd->size) - return -EINVAL; - - /* Check that both start and end of the requested erase are - * aligned with the erasesize at the appropriate addresses. - */ - - i = 0; - - /* Skip all erase regions which are ended before the start of - the requested erase. Actually, to save on the calculations, - we skip to the first erase region which starts after the - start of the requested erase, and then go back one. - */ - - while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) - i++; - i--; - - /* OK, now i is pointing at the erase region in which this - erase request starts. Check the start of the requested - erase range is aligned with the erase size which is in - effect here. - */ - - if (instr->addr & (regions[i].erasesize-1)) - return -EINVAL; - - /* Remember the erase region we start on */ - first = i; - - /* Next, check that the end of the requested erase is aligned - * with the erase region at that address. - */ - - while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset) - i++; - - /* As before, drop back one to point at the region in which - the address actually falls - */ - i--; - - if ((instr->addr + instr->len) & (regions[i].erasesize-1)) - return -EINVAL; +{ + unsigned long ofs, len; + int ret; - chipnum = instr->addr >> cfi->chipshift; - adr = instr->addr - (chipnum << cfi->chipshift); + ofs = instr->addr; len = instr->len; - i=first; - - while(len) { - ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); - - if (ret) - return ret; - - adr += regions[i].erasesize; - len -= regions[i].erasesize; + ret = cfi_intelext_varsize_frob(mtd, do_erase_oneblock, ofs, len, 0); + if (ret) + return ret; - if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) - i++; - - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - instr->state = MTD_ERASE_DONE; if (instr->callback) instr->callback(instr); @@ -1182,38 +1408,20 @@ static void cfi_intelext_sync (struct mtd_info *mtd) int i; struct flchip *chip; int ret = 0; - DECLARE_WAITQUEUE(wait, current); for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - retry: - spin_lock_bh(chip->mutex); + 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_bh(chip->mutex); - break; - - default: - /* Not an idle state */ - add_wait_queue(&chip->wq, &wait); - - spin_unlock_bh(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - - goto retry; } } @@ -1222,231 +1430,73 @@ static void cfi_intelext_sync (struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); if (chip->state == FL_SYNCING) { chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); } } -static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +#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; - __u32 status, status_OK; - unsigned long timeo = jiffies + HZ; - DECLARE_WAITQUEUE(wait, current); - - adr += chip->start; - - /* Let's determine this according to the interleave only once */ - status_OK = CMD(0x80); - - timeo = jiffies + HZ; -retry: - spin_lock_bh(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_bh(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in lock\n"); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - 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); - spin_unlock_bh(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; - } - - ENABLE_VPP(map); - cfi_write(map, CMD(0x60), adr); - cfi_write(map, CMD(0x01), adr); - chip->state = FL_LOCKING; - - spin_unlock_bh(chip->mutex); - schedule_timeout(HZ); - spin_lock_bh(chip->mutex); - - /* FIXME. Use a timer to check this, and return immediately. */ - /* Once the state machine's known to be working I'll do that */ - - timeo = jiffies + (HZ*2); - for (;;) { + int ofs_factor = cfi->interleave * cfi->device_type; - status = cfi_read(map, adr); - if ((status & status_OK) == status_OK) - break; - - /* OK Still waiting */ - if (time_after(jiffies, timeo)) { - cfi_write(map, CMD(0x70), adr); - chip->state = FL_STATUS; - printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); - DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - cfi_udelay(1); - spin_lock_bh(chip->mutex); - } + 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); - /* Done and happy. */ - chip->state = FL_STATUS; - DISABLE_VPP(map); - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); return 0; } -static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) -{ - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr; - int chipnum, ret = 0; -#ifdef DEBUG_LOCK_BITS - int ofs_factor = cfi->interleave * cfi->device_type; #endif - if (ofs & (mtd->erasesize - 1)) - return -EINVAL; - - if (len & (mtd->erasesize -1)) - return -EINVAL; - - if ((len + ofs) > mtd->size) - return -EINVAL; +#define DO_XXLOCK_ONEBLOCK_LOCK ((void *) 1) +#define DO_XXLOCK_ONEBLOCK_UNLOCK ((void *) 2) - chipnum = ofs >> cfi->chipshift; - adr = ofs - (chipnum << cfi->chipshift); - - while(len) { - -#ifdef DEBUG_LOCK_BITS - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - printk("before lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); -#endif - - ret = do_lock_oneblock(map, &cfi->chips[chipnum], adr); - -#ifdef DEBUG_LOCK_BITS - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - printk("after lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); -#endif - - if (ret) - return ret; - - adr += mtd->erasesize; - len -= mtd->erasesize; - - if (adr >> cfi->chipshift) { - adr = 0; - chipnum++; - - if (chipnum >= cfi->numchips) - break; - } - } - return 0; -} -static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +static int do_xxlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr, void *thunk) { struct cfi_private *cfi = map->fldrv_priv; - __u32 status, status_OK; + 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_bh(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_bh(chip->mutex); - printk(KERN_ERR "waiting for chip to be ready timed out in unlock\n"); - return -EIO; - } - - /* Latency issues. Drop the lock, wait a while and retry */ - spin_unlock_bh(chip->mutex); - 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); - spin_unlock_bh(chip->mutex); - schedule(); - remove_wait_queue(&chip->wq, &wait); - timeo = jiffies + HZ; - goto retry; + spin_lock(chip->mutex); + ret = get_chip(map, chip, adr, FL_LOCKING); + if (ret) { + spin_unlock(chip->mutex); + return ret; } ENABLE_VPP(map); cfi_write(map, CMD(0x60), adr); - cfi_write(map, CMD(0xD0), adr); - chip->state = FL_UNLOCKING; - - spin_unlock_bh(chip->mutex); + + if (thunk == DO_XXLOCK_ONEBLOCK_LOCK) { + cfi_write(map, CMD(0x01), adr); + chip->state = FL_LOCKING; + } else if (thunk == DO_XXLOCK_ONEBLOCK_UNLOCK) { + cfi_write(map, CMD(0xD0), adr); + chip->state = FL_UNLOCKING; + } else + BUG(); + + spin_unlock(chip->mutex); schedule_timeout(HZ); - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); /* FIXME. Use a timer to check this, and return immediately. */ /* Once the state machine's known to be working I'll do that */ - timeo = jiffies + (HZ*2); + timeo = jiffies + (HZ*20); for (;;) { status = cfi_read(map, adr); @@ -1457,59 +1507,67 @@ retry: if (time_after(jiffies, timeo)) { cfi_write(map, CMD(0x70), adr); chip->state = FL_STATUS; - printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %llx, status = %llx.\n", (__u64)status, (__u64)cfi_read(map, adr)); DISABLE_VPP(map); - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); return -EIO; } - /* Latency issues. Drop the unlock, wait a while and retry */ - spin_unlock_bh(chip->mutex); + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock(chip->mutex); cfi_udelay(1); - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); } /* Done and happy. */ chip->state = FL_STATUS; - DISABLE_VPP(map); - wake_up(&chip->wq); - spin_unlock_bh(chip->mutex); + put_chip(map, chip, adr); + spin_unlock(chip->mutex); return 0; } -static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) + +static int cfi_intelext_lock(struct mtd_info *mtd, loff_t ofs, size_t len) { - struct map_info *map = mtd->priv; - struct cfi_private *cfi = map->fldrv_priv; - unsigned long adr; - int chipnum, ret = 0; + int ret; + #ifdef DEBUG_LOCK_BITS - int ofs_factor = cfi->interleave * cfi->device_type; + printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", + __FUNCTION__, ofs, len); + cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); #endif - chipnum = ofs >> cfi->chipshift; - adr = ofs - (chipnum << cfi->chipshift); - + ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, + ofs, len, DO_XXLOCK_ONEBLOCK_LOCK); + #ifdef DEBUG_LOCK_BITS - { - unsigned long temp_adr = adr; - unsigned long temp_len = len; - - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - while (temp_len) { - printk("before unlock %x: block status register is %x\n",temp_adr,cfi_read_query(map, temp_adr+(2*ofs_factor))); - temp_adr += mtd->erasesize; - temp_len -= mtd->erasesize; - } - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); - } + printk(KERN_DEBUG __FUNCTION__ + "%s: lock status after, ret=%d\n", __FUNCTION__, ret); + cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); #endif - ret = do_unlock_oneblock(map, &cfi->chips[chipnum], adr); + return ret; +} + +static int cfi_intelext_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + int ret; #ifdef DEBUG_LOCK_BITS - cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); - printk("after unlock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); - cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk(KERN_DEBUG "%s: lock status before, ofs=0x%08llx, len=0x%08X\n", + __FUNCTION__, ofs, len); + cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); +#endif + + ret = cfi_intelext_varsize_frob(mtd, do_xxlock_oneblock, + ofs, len, DO_XXLOCK_ONEBLOCK_UNLOCK); + +#ifdef DEBUG_LOCK_BITS + printk(KERN_DEBUG "%s: lock status after, ret=%d\n", __FUNCTION__, ret); + cfi_intelext_varsize_frob(mtd, do_printlockstatus_oneblock, + ofs, len, 0); #endif return ret; @@ -1526,27 +1584,28 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); - switch(chip->state) { + switch (chip->state) { case FL_READY: case FL_STATUS: case FL_CFI_QUERY: case FL_JEDEC_QUERY: - 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: + 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. + */ + } break; - default: ret = -EAGAIN; + case FL_PM_SUSPENDED: break; } - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); } /* Unlock the chips again */ @@ -1555,7 +1614,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) for (i--; i >=0; i--) { chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); if (chip->state == FL_PM_SUSPENDED) { /* No need to force it into a known state here, @@ -1564,7 +1623,7 @@ static int cfi_intelext_suspend(struct mtd_info *mtd) chip->state = chip->oldstate; wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); } } @@ -1582,7 +1641,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) chip = &cfi->chips[i]; - spin_lock_bh(chip->mutex); + spin_lock(chip->mutex); /* Go to known state. Chip may have been power cycled */ if (chip->state == FL_PM_SUSPENDED) { @@ -1591,7 +1650,7 @@ static void cfi_intelext_resume(struct mtd_info *mtd) wake_up(&chip->wq); } - spin_unlock_bh(chip->mutex); + spin_unlock(chip->mutex); } } @@ -1600,7 +1659,9 @@ static void cfi_intelext_destroy(struct mtd_info *mtd) struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; kfree(cfi->cmdset_priv); + kfree(cfi->cfiq); kfree(cfi); + kfree(mtd->eraseregions); } static char im_name_1[]="cfi_cmdset_0001"; diff --git a/drivers/mtd/chips/cfi_cmdset_0002.c b/drivers/mtd/chips/cfi_cmdset_0002.c index 87dad90209cc..df3309048290 100644 --- a/drivers/mtd/chips/cfi_cmdset_0002.c +++ b/drivers/mtd/chips/cfi_cmdset_0002.c @@ -8,7 +8,7 @@ * * This code is GPL * - * $Id: cfi_cmdset_0002.c,v 1.52 2001/10/24 09:37:30 dwmw2 Exp $ + * $Id: cfi_cmdset_0002.c,v 1.74 2003/05/28 12:51:48 dwmw2 Exp $ * */ @@ -16,6 +16,7 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/sched.h> +#include <linux/init.h> #include <asm/io.h> #include <asm/byteorder.h> @@ -24,17 +25,21 @@ #include <linux/delay.h> #include <linux/interrupt.h> #include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/cfi.h> +#include <linux/mtd/compatmac.h> #define AMD_BOOTLOC_BUG 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_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 void cfi_amdstd_sync (struct mtd_info *); static int cfi_amdstd_suspend (struct mtd_info *); static void cfi_amdstd_resume (struct mtd_info *); +static int cfi_amdstd_secsi_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static void cfi_amdstd_destroy(struct mtd_info *); @@ -49,6 +54,7 @@ static struct mtd_chip_driver cfi_amdstd_chipdrv = { .module = THIS_MODULE }; + struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) { struct cfi_private *cfi = map->fldrv_priv; @@ -58,7 +64,7 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) __u8 major, minor; __u32 base = cfi->chips[0].start; - if (cfi->cfi_mode==1){ + if (cfi->cfi_mode==CFI_MODE_CFI){ __u16 adr = primary?cfi->cfiq->P_ADR:cfi->cfiq->A_ADR; cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); @@ -73,8 +79,9 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) 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); + /* FIXME - should have a delay before continuing */ cfi->mfr = cfi_read_query(map, base); - cfi->id = cfi_read_query(map, base + ofs_factor); + cfi->id = cfi_read_query(map, base + ofs_factor); /* Wheee. Bring me the head of someone at AMD. */ #ifdef AMD_BOOTLOC_BUG @@ -104,6 +111,10 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) cfi->cfiq->EraseRegionInfo[j] = swap; } } + /* + * FIXME - These might already be setup (more correctly) + * buy jedec_probe.c. + */ switch (cfi->device_type) { case CFI_DEVICETYPE_X8: cfi->addr_unlock1 = 0x555; @@ -135,7 +146,6 @@ struct mtd_info *cfi_cmdset_0002(struct map_info *map, int primary) } map->fldrv = &cfi_amdstd_chipdrv; - MOD_INC_USE_COUNT; cfi_send_gen_cmd(0xf0, 0x55, base, map, cfi, cfi->device_type, NULL); return cfi_amdstd_setup(map); @@ -148,12 +158,12 @@ static struct mtd_info *cfi_amdstd_setup(struct map_info *map) unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); - printk(KERN_NOTICE "number of %s chips: %d\n", (cfi->cfi_mode)?"CFI":"JEDEC",cfi->numchips); + printk(KERN_NOTICE "number of %s chips: %d\n", + (cfi->cfi_mode == CFI_MODE_CFI)?"CFI":"JEDEC",cfi->numchips); if (!mtd) { printk(KERN_WARNING "Failed to allocate memory for MTD device\n"); - kfree(cfi->cmdset_priv); - return NULL; + goto setup_err; } memset(mtd, 0, sizeof(*mtd)); @@ -173,9 +183,7 @@ static struct mtd_info *cfi_amdstd_setup(struct map_info *map) 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"); - kfree(cfi->cmdset_priv); - kfree(mtd); - return NULL; + goto setup_err; } for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { @@ -196,10 +204,7 @@ static struct mtd_info *cfi_amdstd_setup(struct map_info *map) if (offset != devsize) { /* Argh */ 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; + goto setup_err; } #if 0 // debug @@ -222,6 +227,9 @@ static struct mtd_info *cfi_amdstd_setup(struct map_info *map) mtd->erase = cfi_amdstd_erase_varsize; else #endif + if (((cfi->cfiq->EraseRegionInfo[0] & 0xffff) + 1) == 1) + mtd->erase = cfi_amdstd_erase_chip; + else mtd->erase = cfi_amdstd_erase_onesize; mtd->read = cfi_amdstd_read; mtd->write = cfi_amdstd_write; @@ -229,19 +237,56 @@ static struct mtd_info *cfi_amdstd_setup(struct map_info *map) default: printk(KERN_WARNING "Unsupported buswidth\n"); - kfree(mtd); - kfree(cfi->cmdset_priv); - return NULL; + goto setup_err; break; } + if (cfi->fast_prog) { + /* In cfi_amdstd_write() 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; + } + + + /* does this chip have a secsi area? */ + if(cfi->mfr==1){ + + switch(cfi->id){ + case 0x50: + case 0x53: + case 0x55: + case 0x56: + case 0x5C: + case 0x5F: + /* Yes */ + mtd->read_user_prot_reg = cfi_amdstd_secsi_read; + mtd->read_fact_prot_reg = cfi_amdstd_secsi_read; + default: + ; + } + } + + mtd->sync = cfi_amdstd_sync; mtd->suspend = cfi_amdstd_suspend; mtd->resume = cfi_amdstd_resume; 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: + if(mtd) { + if(mtd->eraseregions) + kfree(mtd->eraseregions); + kfree(mtd); + } + kfree(cfi->cmdset_priv); + kfree(cfi->cfiq); + return NULL; } static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) @@ -276,7 +321,7 @@ static inline int do_read_onechip(struct map_info *map, struct flchip *chip, lof 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); @@ -325,19 +370,122 @@ static int cfi_amdstd_read (struct mtd_info *mtd, loff_t from, size_t len, size_ return ret; } -static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned long adr, __u32 datum, int fast) +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); unsigned long timeo = jiffies + HZ; - unsigned int Last[4]; - unsigned long Count = 0; struct cfi_private *cfi = map->fldrv_priv; + + retry: + cfi_spin_lock(chip->mutex); + + 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); + + 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; + } + + adr += chip->start; + + chip->state = FL_READY; + + 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); + + 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); + cfi_send_gen_cmd(0x00, cfi->addr_unlock1, chip->start, map, cfi, cfi->device_type, NULL); + + wake_up(&chip->wq); + cfi_spin_unlock(chip->mutex); + + return 0; +} + +static int cfi_amdstd_secsi_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long ofs; + int chipnum; + int ret = 0; + + + /* ofs: offset within the first chip that the first read should start */ + + /* 8 secsi bytes per chip */ + chipnum=from>>3; + ofs=from & 7; + + + *retlen = 0; + + while (len) { + unsigned long thislen; + + if (chipnum >= cfi->numchips) + break; + + if ((len + ofs -1) >> 3) + thislen = (1<<3) - ofs; + else + thislen = len; + + ret = do_read_secsi_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); + if (ret) + break; + + *retlen += thislen; + len -= thislen; + buf += thislen; + + ofs = 0; + chipnum++; + } + return ret; +} + +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, prev_oldstatus, prev_status; + unsigned int dq6; + struct cfi_private *cfi = map->fldrv_priv; + /* 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. Using a static variable allows makes us save the costly + divide operation at each word write.*/ + static unsigned long uWriteTimeout = ( HZ / 1000 ) + 1; DECLARE_WAITQUEUE(wait, current); int ret = 0; + int ta = 0; retry: cfi_spin_lock(chip->mutex); - if (chip->state != FL_READY){ + if (chip->state != FL_READY) { #if 0 printk(KERN_DEBUG "Waiting for chip to write, status = %d\n", chip->state); #endif @@ -361,6 +509,9 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned chip->state = FL_WRITING; adr += chip->start; + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): WRITE 0x%.8lx(0x%.8x)\n", + __func__, adr, datum ); + ENABLE_VPP(map); if (fast) { /* Unlock bypass */ cfi_send_gen_cmd(0xA0, 0, chip->start, map, cfi, cfi->device_type, NULL); @@ -370,40 +521,147 @@ static int do_write_oneword(struct map_info *map, struct flchip *chip, unsigned 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); cfi_spin_unlock(chip->mutex); cfi_udelay(chip->word_write_time); cfi_spin_lock(chip->mutex); - Last[0] = cfi_read(map, adr); - // printk("Last[0] is %x\n", Last[0]); - Last[1] = cfi_read(map, adr); - // printk("Last[1] is %x\n", Last[1]); - Last[2] = cfi_read(map, adr); - // printk("Last[2] is %x\n", Last[2]); - - for (Count = 3; Last[(Count - 1) % 4] != Last[(Count - 2) % 4] && Count < 10000; Count++){ - cfi_spin_unlock(chip->mutex); - cfi_udelay(10); - 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. + * + * It appears tha 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 transition + * and are in a weird state. This will produce 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. If the comparison fails, error + * state can then be decoded. + * + * - Thayne Harbaugh + */ + dq6 = CMD(1<<6); + /* See comment above for timeout value. */ + timeo = jiffies + uWriteTimeout; - Last[Count % 4] = cfi_read(map, adr); - // printk("Last[%d%%4] is %x\n", Count, Last[Count%4]); + 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 ); + + /* + * This only checks if dq6 is still toggling and that our + * timer hasn't expired. We purposefully ignore the chips + * 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. + */ + while( ( ( status ^ oldstatus ) & dq6 ) + && ! ( ta = time_after(jiffies, timeo) ) ) { + + if (need_resched()) { + cfi_spin_unlock(chip->mutex); + yield(); + cfi_spin_lock(chip->mutex); + } else + udelay(1); + + 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 (Last[(Count - 1) % 4] != datum){ - printk(KERN_WARNING "Last[%ld] is %x, datum is %x\n",(Count - 1) % 4,Last[(Count - 1) % 4],datum); - cfi_send_gen_cmd(0xF0, 0, chip->start, map, cfi, cfi->device_type, NULL); - DISABLE_VPP(map); - ret = -EIO; - } + + /* + * 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 == datum && status == datum ) { + /* success - do nothing */ + goto write_done; + } + + if ( ta ) { + int 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 write_failed; + } + + /* + * 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. + */ + printk(KERN_WARNING + "MTD %s(): Wacky! Unable to decode failure status\n", + __func__ ); + + 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); + + write_failed: + ret = -EIO; + /* reset on all failures. */ + cfi_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ + + write_done: DISABLE_VPP(map); chip->state = FL_READY; wake_up(&chip->wq); cfi_spin_unlock(chip->mutex); - + return ret; } @@ -428,10 +686,10 @@ static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_ 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; - 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--; @@ -444,7 +702,7 @@ static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_ } ret = do_write_oneword(map, &cfi->chips[chipnum], - bus_ofs, datum, 0); + bus_ofs, datum, 0); if (ret) return ret; @@ -460,14 +718,16 @@ static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_ } } - /* Go into unlock bypass mode */ - cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); - cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + if (cfi->fast_prog) { + /* Go into unlock bypass mode */ + cfi_send_gen_cmd(0xAA, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x55, cfi->addr_unlock2, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + cfi_send_gen_cmd(0x20, cfi->addr_unlock1, chipstart, map, cfi, CFI_DEVICETYPE_X8, NULL); + } /* 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; @@ -521,12 +781,13 @@ static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_ cfi_send_gen_cmd(0x00, 0, chipstart, map, cfi, cfi->device_type, NULL); } + /* 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; - map->copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); + map_copy_from(map, tmp_buf, ofs + cfi->chips[chipnum].start, CFIDEV_BUSWIDTH); while (len--) tmp_buf[i++] = buf[n++]; @@ -549,13 +810,197 @@ static int cfi_amdstd_write (struct mtd_info *mtd, loff_t to , size_t len, size_ return 0; } +static inline int do_erase_chip(struct map_info *map, struct flchip *chip) +{ + unsigned int oldstatus, status, prev_oldstatus, prev_status; + unsigned int dq6; + unsigned long timeo = jiffies + HZ; + unsigned long int adr; + struct cfi_private *cfi = map->fldrv_priv; + DECLARE_WAITQUEUE(wait, current); + int ret = 0; + int ta = 0; + cfi_word ones = 0; + + retry: + cfi_spin_lock(chip->mutex); + + if (chip->state != FL_READY){ + 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; + + goto retry; + } + + chip->state = FL_ERASING; + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", + __func__, chip->start ); + + /* 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; + + /* 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 it's 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. + * After bit5 is set you can kill the operation by sending a reset + * command to the chip. + */ + /* see comments in do_write_oneword */ + dq6 = CMD(1<<6); + + 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 ^ oldstatus ) & dq6 ) + && ! ( ta = time_after(jiffies, timeo) ) ) { + int wait_reps; + + /* an initial short sleep */ + cfi_spin_unlock(chip->mutex); + schedule_timeout(HZ/100); + cfi_spin_lock(chip->mutex); + + 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("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; + } + + /* Busy wait for 1/10 of a milisecond */ + for(wait_reps = 0; + (wait_reps < 100) + && ( ( status ^ oldstatus ) & dq6 ); + wait_reps++) { + + /* 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); + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): Check 0x%.8x 0x%.8x\n", + __func__, oldstatus, 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 ); + } + + 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() ) { + ones = (__u8)~0; + } else if ( cfi_buswidth_is_2() ) { + ones = (__u16)~0; + } else if ( cfi_buswidth_is_4() ) { + ones = (__u32)~0; + } else { + printk(KERN_WARNING "Unsupported buswidth\n"); + goto erase_failed; + } + + if ( oldstatus == ones && status == ones ) { + /* success - do nothing */ + goto erase_done; + } + + if ( ta ) { + int 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 erase_failed; + } + + printk(KERN_WARNING + "MTD %s(): Wacky! Unable to decode failure status\n", + __func__ ); + + printk(KERN_WARNING + "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", + __func__, adr, ones, + prev_oldstatus, prev_status, + oldstatus, status); + + erase_failed: + ret = -EIO; + /* reset on all failures. */ + cfi_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ + + erase_done: + DISABLE_VPP(map); + chip->state = FL_READY; + wake_up(&chip->wq); + cfi_spin_unlock(chip->mutex); + return ret; +} + + static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) { - unsigned int status; + unsigned int oldstatus, status, prev_oldstatus, prev_status; + unsigned int dq6; unsigned long timeo = jiffies + HZ; struct cfi_private *cfi = map->fldrv_priv; - unsigned int rdy_mask; DECLARE_WAITQUEUE(wait, current); + int ret = 0; + int ta = 0; + cfi_word ones = 0; retry: cfi_spin_lock(chip->mutex); @@ -580,28 +1025,46 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u chip->state = FL_ERASING; adr += chip->start; + DEBUG( MTD_DEBUG_LEVEL3, "MTD %s(): ERASE 0x%.8lx\n", + __func__, adr ); + 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); - cfi_spin_unlock(chip->mutex); - schedule_timeout(HZ); - cfi_spin_lock(chip->mutex); - - rdy_mask = CMD(0x80); + /* 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 it's 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. + * After bit5 is set you can kill the operation by sending a reset + * command to the chip. + */ + /* see comments in do_write_oneword */ + dq6 = CMD(1<<6); - /* FIXME. Use a timer to check this, and return immediately. */ - /* Once the state machine's known to be working I'll do that */ + 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 = cfi_read(map,adr)) & rdy_mask ) != rdy_mask ) { - static int z=0; + while( ( ( status ^ oldstatus ) & dq6 ) + && ! ( ta = time_after(jiffies, timeo) ) ) { + int wait_reps; + /* an initial short sleep */ + cfi_spin_unlock(chip->mutex); + schedule_timeout(HZ/100); + cfi_spin_lock(chip->mutex); + if (chip->state != FL_ERASING) { /* Someone's suspended the erase. Sleep */ set_current_state(TASK_UNINTERRUPTIBLE); @@ -621,34 +1084,90 @@ static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, u continue; } - /* OK Still waiting */ - if (time_after(jiffies, timeo)) { - chip->state = FL_READY; + /* Busy wait for 1/10 of a milisecond */ + for(wait_reps = 0; + (wait_reps < 100) + && ( ( status ^ oldstatus ) & dq6 ); + wait_reps++) { + + /* Latency issues. Drop the lock, wait a while and retry */ cfi_spin_unlock(chip->mutex); - printk(KERN_WARNING "waiting for erase to complete timed out."); - DISABLE_VPP(map); - return -EIO; - } + + cfi_udelay(1); - /* Latency issues. Drop the lock, wait a while and retry */ - cfi_spin_unlock(chip->mutex); + cfi_spin_lock(chip->mutex); + 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 ); + } + 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 ); + } - z++; - if ( 0 && !(z % 100 )) - printk(KERN_WARNING "chip not ready yet after erase. looping\n"); + 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() ) { + ones = (__u8)~0; + } else if ( cfi_buswidth_is_2() ) { + ones = (__u16)~0; + } else if ( cfi_buswidth_is_4() ) { + ones = (__u32)~0; + } else { + printk(KERN_WARNING "Unsupported buswidth\n"); + goto erase_failed; + } - cfi_udelay(1); - - cfi_spin_lock(chip->mutex); - continue; + if ( oldstatus == ones && status == ones ) { + /* success - do nothing */ + goto erase_done; } - - /* Done and happy. */ + + if ( ta ) { + int 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 erase_failed; + } + + printk(KERN_WARNING + "MTD %s(): Wacky! Unable to decode failure status\n", + __func__ ); + + printk(KERN_WARNING + "MTD %s(): 0x%.8lx(0x%.8x): 0x%.8x 0x%.8x 0x%.8x 0x%.8x\n", + __func__, adr, ones, + prev_oldstatus, prev_status, + oldstatus, status); + + erase_failed: + ret = -EIO; + /* reset on all failures. */ + cfi_write( map, CMD(0xF0), chip->start ); + /* FIXME - should have reset delay before continuing */ + + erase_done: DISABLE_VPP(map); chip->state = FL_READY; wake_up(&chip->wq); cfi_spin_unlock(chip->mutex); - return 0; + return ret; } static int cfi_amdstd_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) @@ -788,6 +1307,29 @@ static int cfi_amdstd_erase_onesize(struct mtd_info *mtd, struct erase_info *ins return 0; } +static int cfi_amdstd_erase_chip(struct mtd_info *mtd, struct erase_info *instr) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int ret = 0; + + if (instr->addr != 0) + return -EINVAL; + + if (instr->len != mtd->size) + return -EINVAL; + + ret = do_erase_chip(map, &cfi->chips[0]); + if (ret) + return ret; + + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + + return 0; +} + static void cfi_amdstd_sync (struct mtd_info *mtd) { struct map_info *map = mtd->priv; @@ -855,7 +1397,6 @@ static int cfi_amdstd_suspend(struct mtd_info *mtd) int i; struct flchip *chip; int ret = 0; -//printk("suspend\n"); for (i=0; !ret && i<cfi->numchips; i++) { chip = &cfi->chips[i]; @@ -908,7 +1449,6 @@ static void cfi_amdstd_resume(struct mtd_info *mtd) struct cfi_private *cfi = map->fldrv_priv; int i; struct flchip *chip; -//printk("resume\n"); for (i=0; i<cfi->numchips; i++) { @@ -933,7 +1473,9 @@ static void cfi_amdstd_destroy(struct mtd_info *mtd) struct map_info *map = mtd->priv; struct cfi_private *cfi = map->fldrv_priv; kfree(cfi->cmdset_priv); + kfree(cfi->cfiq); kfree(cfi); + kfree(mtd->eraseregions); } static char im_name[]="cfi_cmdset_0002"; @@ -955,3 +1497,4 @@ module_exit(cfi_amdstd_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Crossnet Co. <info@crossnet.co.jp> et al."); MODULE_DESCRIPTION("MTD chip driver for AMD/Fujitsu flash chips"); + diff --git a/drivers/mtd/chips/cfi_cmdset_0020.c b/drivers/mtd/chips/cfi_cmdset_0020.c new file mode 100644 index 000000000000..943e52b368d5 --- /dev/null +++ b/drivers/mtd/chips/cfi_cmdset_0020.c @@ -0,0 +1,1451 @@ +/* + * Common Flash Interface support: + * ST Advanced Architecture Command Set (ID 0x0020) + * + * (C) 2000 Red Hat. GPL'd + * + * + * 10/10/2000 Nicolas Pitre <nico@cam.org> + * - completely revamped method functions so they are aware and + * independent of the flash geometry (buswidth, interleave, etc.) + * - scalability vs code size is completely set at compile-time + * (see include/linux/mtd/cfi.h for selection) + * - optimized write buffer method + * 06/21/2002 Joern Engel <joern@wh.fh-wedel.de> and others + * - modified Intel Command Set 0x0001 to support ST Advanced Architecture + * (command set 0x0020) + * - added a writev function + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <asm/io.h> +#include <asm/byteorder.h> + +#include <linux/errno.h> +#include <linux/init.h> +#include <linux/slab.h> +#include <linux/delay.h> +#include <linux/interrupt.h> +#include <linux/mtd/map.h> +#include <linux/mtd/cfi.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/compatmac.h> + + +static int cfi_staa_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); +static int cfi_staa_write_buffers(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); +static int cfi_staa_writev(struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen); +static int cfi_staa_erase_varsize(struct mtd_info *, struct erase_info *); +static void cfi_staa_sync (struct mtd_info *); +static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len); +static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len); +static int cfi_staa_suspend (struct mtd_info *); +static void cfi_staa_resume (struct mtd_info *); + +static void cfi_staa_destroy(struct mtd_info *); + +struct mtd_info *cfi_cmdset_0020(struct map_info *, int); + +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 +}; + +/* #define DEBUG_LOCK_BITS */ +//#define DEBUG_CFI_FEATURES + +#ifdef DEBUG_CFI_FEATURES +static void cfi_tell_features(struct cfi_pri_intelext *extp) +{ + int i; + printk(" Feature/Command Support: %4.4X\n", extp->FeatureSupport); + printk(" - Chip Erase: %s\n", extp->FeatureSupport&1?"supported":"unsupported"); + printk(" - Suspend Erase: %s\n", extp->FeatureSupport&2?"supported":"unsupported"); + printk(" - Suspend Program: %s\n", extp->FeatureSupport&4?"supported":"unsupported"); + printk(" - Legacy Lock/Unlock: %s\n", extp->FeatureSupport&8?"supported":"unsupported"); + printk(" - Queued Erase: %s\n", extp->FeatureSupport&16?"supported":"unsupported"); + printk(" - Instant block lock: %s\n", extp->FeatureSupport&32?"supported":"unsupported"); + printk(" - Protection Bits: %s\n", extp->FeatureSupport&64?"supported":"unsupported"); + printk(" - Page-mode read: %s\n", extp->FeatureSupport&128?"supported":"unsupported"); + printk(" - Synchronous read: %s\n", extp->FeatureSupport&256?"supported":"unsupported"); + for (i=9; i<32; i++) { + if (extp->FeatureSupport & (1<<i)) + printk(" - Unknown Bit %X: supported\n", i); + } + + printk(" Supported functions after Suspend: %2.2X\n", extp->SuspendCmdSupport); + printk(" - Program after Erase Suspend: %s\n", extp->SuspendCmdSupport&1?"supported":"unsupported"); + for (i=1; i<8; i++) { + if (extp->SuspendCmdSupport & (1<<i)) + printk(" - Unknown Bit %X: supported\n", i); + } + + printk(" Block Status Register Mask: %4.4X\n", extp->BlkStatusRegMask); + printk(" - Lock Bit Active: %s\n", extp->BlkStatusRegMask&1?"yes":"no"); + printk(" - Valid Bit Active: %s\n", extp->BlkStatusRegMask&2?"yes":"no"); + for (i=2; i<16; i++) { + if (extp->BlkStatusRegMask & (1<<i)) + printk(" - Unknown Bit %X Active: yes\n",i); + } + + printk(" Vcc Logic Supply Optimum Program/Erase Voltage: %d.%d V\n", + extp->VccOptimal >> 8, extp->VccOptimal & 0xf); + if (extp->VppOptimal) + printk(" Vpp Programming Supply Optimum Program/Erase Voltage: %d.%d V\n", + extp->VppOptimal >> 8, extp->VppOptimal & 0xf); +} +#endif + +/* 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 cfi are valid as long as the use count of + * this module is non-zero, i.e. between inter_module_get and + * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. + */ +struct mtd_info *cfi_cmdset_0020(struct map_info *map, int primary) +{ + struct cfi_private *cfi = map->fldrv_priv; + int i; + __u32 base = cfi->chips[0].start; + + if (cfi->cfi_mode) { + /* + * 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_intelext *extp; + int ofs_factor = cfi->interleave * cfi->device_type; + + printk(" ST Microelectronics Extended Query Table at 0x%4.4X\n", adr); + if (!adr) + return NULL; + + /* Switch it into Query Mode */ + cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); + + extp = kmalloc(sizeof(*extp), GFP_KERNEL); + if (!extp) { + printk(KERN_ERR "Failed to allocate memory\n"); + return NULL; + } + + /* Read in the Extended Query Table */ + for (i=0; i<sizeof(*extp); i++) { + ((unsigned char *)extp)[i] = + cfi_read_query(map, (base+((adr+i)*ofs_factor))); + } + + if (extp->MajorVersion != '1' || + (extp->MinorVersion < '0' || extp->MinorVersion > '2')) { + printk(KERN_WARNING " Unknown staa Extended Query " + "version %c.%c.\n", extp->MajorVersion, + extp->MinorVersion); + kfree(extp); + return NULL; + } + + /* Do some byteswapping if necessary */ + extp->FeatureSupport = cfi32_to_cpu(extp->FeatureSupport); + extp->BlkStatusRegMask = cfi32_to_cpu(extp->BlkStatusRegMask); + +#ifdef DEBUG_CFI_FEATURES + /* Tell the user about it in lots of lovely detail */ + cfi_tell_features(extp); +#endif + + /* Install our own private info structure */ + cfi->cmdset_priv = extp; + } + + for (i=0; i< cfi->numchips; i++) { + cfi->chips[i].word_write_time = 128; + cfi->chips[i].buffer_write_time = 128; + cfi->chips[i].erase_time = 1024; + } + + /* 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); +} + +static struct mtd_info *cfi_staa_setup(struct map_info *map) +{ + struct cfi_private *cfi = map->fldrv_priv; + struct mtd_info *mtd; + unsigned long offset = 0; + int i,j; + unsigned long devsize = (1<<cfi->cfiq->DevSize) * cfi->interleave; + + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + //printk(KERN_DEBUG "number of CFI chips: %d\n", cfi->numchips); + + if (!mtd) { + printk(KERN_ERR "Failed to allocate memory for MTD device\n"); + kfree(cfi->cmdset_priv); + return NULL; + } + + memset(mtd, 0, sizeof(*mtd)); + mtd->priv = map; + mtd->type = MTD_NORFLASH; + mtd->size = devsize * cfi->numchips; + + mtd->numeraseregions = cfi->cfiq->NumEraseRegions * cfi->numchips; + mtd->eraseregions = kmalloc(sizeof(struct mtd_erase_region_info) + * mtd->numeraseregions, GFP_KERNEL); + if (!mtd->eraseregions) { + printk(KERN_ERR "Failed to allocate memory for MTD erase region info\n"); + kfree(cfi->cmdset_priv); + return NULL; + } + + for (i=0; i<cfi->cfiq->NumEraseRegions; i++) { + unsigned long ernum, ersize; + ersize = ((cfi->cfiq->EraseRegionInfo[i] >> 8) & ~0xff) * cfi->interleave; + ernum = (cfi->cfiq->EraseRegionInfo[i] & 0xffff) + 1; + + if (mtd->erasesize < ersize) { + mtd->erasesize = ersize; + } + for (j=0; j<cfi->numchips; j++) { + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].offset = (j*devsize)+offset; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].erasesize = ersize; + mtd->eraseregions[(j*cfi->cfiq->NumEraseRegions)+i].numblocks = ernum; + } + offset += (ersize * ernum); + } + + if (offset != devsize) { + /* Argh */ + 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); + return NULL; + } + + for (i=0; i<mtd->numeraseregions;i++){ + printk(KERN_DEBUG "%d: offset=0x%x,size=0x%x,blocks=%d\n", + i,mtd->eraseregions[i].offset, + mtd->eraseregions[i].erasesize, + mtd->eraseregions[i].numblocks); + } + + /* Also select the correct geometry setup too */ + mtd->erase = cfi_staa_erase_varsize; + mtd->read = cfi_staa_read; + mtd->write = cfi_staa_write_buffers; + mtd->writev = cfi_staa_writev; + mtd->sync = cfi_staa_sync; + mtd->lock = cfi_staa_lock; + mtd->unlock = cfi_staa_unlock; + mtd->suspend = cfi_staa_suspend; + mtd->resume = cfi_staa_resume; + mtd->flags = MTD_CAP_NORFLASH; + 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; + __module_get(THIS_MODULE); + mtd->name = map->name; + return mtd; +} + + +static inline int do_read_onechip(struct map_info *map, struct flchip *chip, loff_t adr, size_t len, u_char *buf) +{ + __u32 status, status_OK; + unsigned long timeo; + DECLARE_WAITQUEUE(wait, current); + int suspended = 0; + unsigned long cmd_addr; + struct cfi_private *cfi = map->fldrv_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); + + timeo = jiffies + HZ; + retry: + spin_lock_bh(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 (!((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_bh(chip->mutex); + printk(KERN_ERR "Chip not ready after erase " + "suspended: status = 0x%x\n", status); + return -EIO; + } + + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + } + + suspended = 1; + cfi_write(map, CMD(0xff), cmd_addr); + chip->state = FL_READY; + break; + +#if 0 + case FL_WRITING: + /* Not quite yet */ +#endif + + case FL_READY: + 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_bh(chip->mutex); + printk(KERN_ERR "waiting for chip to be ready timed out in read. WSM status = %x\n", status); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + goto retry; + + 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_bh(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_bh(chip->mutex); + return 0; +} + +static int cfi_staa_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long ofs; + int chipnum; + int ret = 0; + + /* ofs: offset within the first chip that the first read should start */ + chipnum = (from >> cfi->chipshift); + ofs = from - (chipnum << cfi->chipshift); + + *retlen = 0; + + while (len) { + unsigned long thislen; + + if (chipnum >= cfi->numchips) + break; + + if ((len + ofs -1) >> cfi->chipshift) + thislen = (1<<cfi->chipshift) - ofs; + else + thislen = len; + + ret = do_read_onechip(map, &cfi->chips[chipnum], ofs, thislen, buf); + if (ret) + break; + + *retlen += thislen; + len -= thislen; + buf += thislen; + + ofs = 0; + chipnum++; + } + return ret; +} + +static inline int do_write_buffer(struct map_info *map, struct flchip *chip, + unsigned long adr, const u_char *buf, int len) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 status, status_OK; + unsigned long cmd_adr, timeo; + DECLARE_WAITQUEUE(wait, current); + int wbufsize, z; + + /* M58LW064A requires bus alignment for buffer wriets -- saw */ + if (adr & (CFIDEV_BUSWIDTH-1)) + return -EINVAL; + + wbufsize = CFIDEV_INTERLEAVE << cfi->cfiq->MaxBufWriteSize; + adr += chip->start; + cmd_adr = adr & ~(wbufsize-1); + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + timeo = jiffies + HZ; + retry: + +#ifdef DEBUG_CFI_FEATURES + printk("%s: chip->state[%d]\n", __FUNCTION__, chip->state); +#endif + spin_lock_bh(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), cmd_adr); + chip->state = FL_STATUS; +#ifdef DEBUG_CFI_FEATURES + printk("%s: 1 status[%x]\n", __FUNCTION__, cfi_read(map, cmd_adr)); +#endif + + 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)) { + spin_unlock_bh(chip->mutex); + printk(KERN_ERR "waiting for chip to be ready timed out in buffer write Xstatus = %x, status = %x\n", + status, cfi_read(map, cmd_adr)); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(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); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + ENABLE_VPP(map); + cfi_write(map, CMD(0xe8), cmd_adr); + chip->state = FL_WRITING_TO_BUFFER; + + z = 0; + for (;;) { + status = cfi_read(map, cmd_adr); + if ((status & status_OK) == status_OK) + break; + + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + + if (++z > 100) { + /* Argh. Not ready for write to buffer */ + DISABLE_VPP(map); + cfi_write(map, CMD(0x70), cmd_adr); + chip->state = FL_STATUS; + spin_unlock_bh(chip->mutex); + printk(KERN_ERR "Chip not ready for buffer write. Xstatus = %x\n", status); + return -EIO; + } + } + + /* Write length of data to come */ + cfi_write(map, CMD(len/CFIDEV_BUSWIDTH-1), cmd_adr ); + + /* Write data */ + for (z = 0; z < len; z += CFIDEV_BUSWIDTH) { + if (cfi_buswidth_is_1()) { + map_write8 (map, *((__u8*)buf)++, adr+z); + } else if (cfi_buswidth_is_2()) { + map_write16 (map, *((__u16*)buf)++, adr+z); + } else if (cfi_buswidth_is_4()) { + map_write32 (map, *((__u32*)buf)++, adr+z); + } else { + DISABLE_VPP(map); + return -EINVAL; + } + } + /* GO GO GO */ + cfi_write(map, CMD(0xd0), cmd_adr); + chip->state = FL_WRITING; + + spin_unlock_bh(chip->mutex); + cfi_udelay(chip->buffer_write_time); + spin_lock_bh(chip->mutex); + + timeo = jiffies + (HZ/2); + z = 0; + for (;;) { + if (chip->state != FL_WRITING) { + /* Someone's suspended the write. Sleep */ + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&chip->wq, &wait); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + (HZ / 2); /* FIXME */ + spin_lock_bh(chip->mutex); + continue; + } + + status = cfi_read(map, cmd_adr); + if ((status & status_OK) == status_OK) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + /* clear status */ + cfi_write(map, CMD(0x50), cmd_adr); + /* put back into read status register mode */ + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + printk(KERN_ERR "waiting for chip to be ready timed out in bufwrite\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + z++; + spin_lock_bh(chip->mutex); + } + if (!z) { + chip->buffer_write_time--; + if (!chip->buffer_write_time) + chip->buffer_write_time++; + } + if (z > 1) + chip->buffer_write_time++; + + /* Done and happy. */ + DISABLE_VPP(map); + chip->state = FL_STATUS; + + /* check for errors: 'lock bit', 'VPP', 'dead cell'/'unerased cell' or 'incorrect cmd' -- saw */ + if ((status & CMD(0x02)) || (status & CMD(0x08)) || + (status & CMD(0x10)) || (status & CMD(0x20))) { +#ifdef DEBUG_CFI_FEATURES + printk("%s: 2 status[%x]\n", __FUNCTION__, status); +#endif + /* clear status */ + cfi_write(map, CMD(0x50), cmd_adr); + /* put back into read status register mode */ + cfi_write(map, CMD(0x70), adr); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return (status & CMD(0x02)) ? -EROFS : -EIO; + } + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + + return 0; +} + +static int cfi_staa_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); + +#ifdef DEBUG_CFI_FEATURES + printk("%s: CFIDEV_BUSWIDTH[%x]\n", __FUNCTION__, CFIDEV_BUSWIDTH); + printk("%s: chipnum[%x] wbufsize[%x]\n", __FUNCTION__, chipnum, wbufsize); + printk("%s: ofs[%x] len[%x]\n", __FUNCTION__, ofs, len); +#endif + + /* Write buffer is worth it only if more than one word to write... */ + while (len > 0) { + /* 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; + } + } + + return 0; +} + +/* + * Writev for ECC-Flashes is a little more complicated. We need to maintain + * a small buffer for this. + * XXX: If the buffer size is not a multiple of 2, this will break + */ +#define ECCBUF_SIZE (mtd->eccsize) +#define ECCBUF_DIV(x) ((x) & ~(ECCBUF_SIZE - 1)) +#define ECCBUF_MOD(x) ((x) & (ECCBUF_SIZE - 1)) +static int +cfi_staa_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; + size_t buflen = 0; + static char *buffer; + + if (!ECCBUF_SIZE) { + /* We should fall back to a general writev implementation. + * Until that is written, just break. + */ + return -EIO; + } + buffer = kmalloc(ECCBUF_SIZE, GFP_KERNEL); + if (!buffer) + return -ENOMEM; + + for (i=0; i<count; i++) { + size_t elem_len = vecs[i].iov_len; + void *elem_base = vecs[i].iov_base; + if (!elem_len) /* FIXME: Might be unnecessary. Check that */ + continue; + if (buflen) { /* cut off head */ + if (buflen + elem_len < ECCBUF_SIZE) { /* just accumulate */ + memcpy(buffer+buflen, elem_base, elem_len); + buflen += elem_len; + continue; + } + memcpy(buffer+buflen, elem_base, ECCBUF_SIZE-buflen); + ret = mtd->write(mtd, to, ECCBUF_SIZE, &thislen, buffer); + totlen += thislen; + if (ret || thislen != ECCBUF_SIZE) + goto write_error; + elem_len -= thislen-buflen; + elem_base += thislen-buflen; + to += ECCBUF_SIZE; + } + if (ECCBUF_DIV(elem_len)) { /* write clean aligned data */ + ret = mtd->write(mtd, to, ECCBUF_DIV(elem_len), &thislen, elem_base); + totlen += thislen; + if (ret || thislen != ECCBUF_DIV(elem_len)) + goto write_error; + to += thislen; + } + buflen = ECCBUF_MOD(elem_len); /* cut off tail */ + if (buflen) { + memset(buffer, 0xff, ECCBUF_SIZE); + memcpy(buffer, elem_base + thislen, buflen); + } + } + if (buflen) { /* flush last page, even if not full */ + /* This is sometimes intended behaviour, really */ + ret = mtd->write(mtd, to, buflen, &thislen, buffer); + totlen += thislen; + if (ret || thislen != ECCBUF_SIZE) + goto write_error; + } +write_error: + if (retlen) + *retlen = totlen; + return ret; +} + + +static inline int do_erase_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 status, status_OK; + unsigned long timeo; + int retries = 3; + DECLARE_WAITQUEUE(wait, current); + int 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_bh(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_bh(chip->mutex); + printk(KERN_ERR "waiting for chip to be ready timed out in erase\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + 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); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + ENABLE_VPP(map); + /* Clear the status register first */ + cfi_write(map, CMD(0x50), adr); + + /* Now erase */ + cfi_write(map, CMD(0x20), adr); + cfi_write(map, CMD(0xD0), adr); + chip->state = FL_ERASING; + + spin_unlock_bh(chip->mutex); + schedule_timeout(HZ); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ + /* Once the state machine's known to be working I'll do that */ + + 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); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + (HZ*20); /* FIXME */ + spin_lock_bh(chip->mutex); + continue; + } + + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + printk(KERN_ERR "waiting for erase to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + } + + DISABLE_VPP(map); + ret = 0; + + /* We've broken this before. It doesn't hurt to be safe */ + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + status = cfi_read(map, adr); + + /* check for lock bit */ + if (status & CMD(0x3a)) { + unsigned char chipstatus = status; + if (status != CMD(status & 0xff)) { + int i; + for (i = 1; i<CFIDEV_INTERLEAVE; i++) { + chipstatus |= status >> (cfi->device_type * 8); + } + printk(KERN_WARNING "Status is not identical for all chips: 0x%x. Merging to give 0x%02x\n", status, chipstatus); + } + /* Reset the error bits */ + cfi_write(map, CMD(0x50), adr); + cfi_write(map, CMD(0x70), adr); + + if ((chipstatus & 0x30) == 0x30) { + printk(KERN_NOTICE "Chip reports improper command sequence: status 0x%x\n", status); + ret = -EIO; + } else if (chipstatus & 0x02) { + /* Protection bit set */ + ret = -EROFS; + } else if (chipstatus & 0x8) { + /* Voltage */ + printk(KERN_WARNING "Chip reports voltage low on erase: status 0x%x\n", status); + ret = -EIO; + } else if (chipstatus & 0x20) { + if (retries--) { + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x. Retrying...\n", adr, status); + timeo = jiffies + HZ; + chip->state = FL_STATUS; + spin_unlock_bh(chip->mutex); + goto retry; + } + printk(KERN_DEBUG "Chip erase failed at 0x%08lx: status 0x%x\n", adr, status); + ret = -EIO; + } + } + + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return ret; +} + +int cfi_staa_erase_varsize(struct mtd_info *mtd, struct erase_info *instr) +{ struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr, len; + int chipnum, ret = 0; + int i, first; + struct mtd_erase_region_info *regions = mtd->eraseregions; + + if (instr->addr > mtd->size) + return -EINVAL; + + if ((instr->len + instr->addr) > mtd->size) + return -EINVAL; + + /* Check that both start and end of the requested erase are + * aligned with the erasesize at the appropriate addresses. + */ + + i = 0; + + /* Skip all erase regions which are ended before the start of + the requested erase. Actually, to save on the calculations, + we skip to the first erase region which starts after the + start of the requested erase, and then go back one. + */ + + while (i < mtd->numeraseregions && instr->addr >= regions[i].offset) + i++; + i--; + + /* OK, now i is pointing at the erase region in which this + erase request starts. Check the start of the requested + erase range is aligned with the erase size which is in + effect here. + */ + + if (instr->addr & (regions[i].erasesize-1)) + return -EINVAL; + + /* Remember the erase region we start on */ + first = i; + + /* Next, check that the end of the requested erase is aligned + * with the erase region at that address. + */ + + while (i<mtd->numeraseregions && (instr->addr + instr->len) >= regions[i].offset) + i++; + + /* As before, drop back one to point at the region in which + the address actually falls + */ + i--; + + if ((instr->addr + instr->len) & (regions[i].erasesize-1)) + return -EINVAL; + + chipnum = instr->addr >> cfi->chipshift; + adr = instr->addr - (chipnum << cfi->chipshift); + len = instr->len; + + i=first; + + while(len) { + ret = do_erase_oneblock(map, &cfi->chips[chipnum], adr); + + if (ret) + return ret; + + adr += regions[i].erasesize; + len -= regions[i].erasesize; + + if (adr % (1<< cfi->chipshift) == ((regions[i].offset + (regions[i].erasesize * regions[i].numblocks)) %( 1<< cfi->chipshift))) + i++; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + + return 0; +} + +static void cfi_staa_sync (struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + int ret = 0; + DECLARE_WAITQUEUE(wait, current); + + for (i=0; !ret && i<cfi->numchips; i++) { + chip = &cfi->chips[i]; + + retry: + spin_lock_bh(chip->mutex); + + switch(chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + 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_bh(chip->mutex); + break; + + default: + /* Not an idle state */ + add_wait_queue(&chip->wq, &wait); + + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + + goto retry; + } + } + + /* Unlock the chips again */ + + for (i--; i >=0; i--) { + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + if (chip->state == FL_SYNCING) { + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + spin_unlock_bh(chip->mutex); + } +} + +static inline int do_lock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 status, status_OK; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + timeo = jiffies + HZ; +retry: + spin_lock_bh(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_bh(chip->mutex); + printk(KERN_ERR "waiting for chip to be ready timed out in lock\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + 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); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + ENABLE_VPP(map); + cfi_write(map, CMD(0x60), adr); + cfi_write(map, CMD(0x01), adr); + chip->state = FL_LOCKING; + + spin_unlock_bh(chip->mutex); + schedule_timeout(HZ); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ + /* Once the state machine's known to be working I'll do that */ + + timeo = jiffies + (HZ*2); + for (;;) { + + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + printk(KERN_ERR "waiting for lock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + } + + /* Done and happy. */ + chip->state = FL_STATUS; + DISABLE_VPP(map); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return 0; +} +static int cfi_staa_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr; + int chipnum, ret = 0; +#ifdef DEBUG_LOCK_BITS + int ofs_factor = cfi->interleave * cfi->device_type; +#endif + + if (ofs & (mtd->erasesize - 1)) + return -EINVAL; + + if (len & (mtd->erasesize -1)) + return -EINVAL; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); + + while(len) { + +#ifdef DEBUG_LOCK_BITS + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk("before lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); +#endif + + ret = do_lock_oneblock(map, &cfi->chips[chipnum], adr); + +#ifdef DEBUG_LOCK_BITS + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk("after lock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); +#endif + + if (ret) + return ret; + + adr += mtd->erasesize; + len -= mtd->erasesize; + + if (adr >> cfi->chipshift) { + adr = 0; + chipnum++; + + if (chipnum >= cfi->numchips) + break; + } + } + return 0; +} +static inline int do_unlock_oneblock(struct map_info *map, struct flchip *chip, unsigned long adr) +{ + struct cfi_private *cfi = map->fldrv_priv; + __u32 status, status_OK; + unsigned long timeo = jiffies + HZ; + DECLARE_WAITQUEUE(wait, current); + + adr += chip->start; + + /* Let's determine this according to the interleave only once */ + status_OK = CMD(0x80); + + timeo = jiffies + HZ; +retry: + spin_lock_bh(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_bh(chip->mutex); + printk(KERN_ERR "waiting for chip to be ready timed out in unlock\n"); + return -EIO; + } + + /* Latency issues. Drop the lock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + 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); + spin_unlock_bh(chip->mutex); + schedule(); + remove_wait_queue(&chip->wq, &wait); + timeo = jiffies + HZ; + goto retry; + } + + ENABLE_VPP(map); + cfi_write(map, CMD(0x60), adr); + cfi_write(map, CMD(0xD0), adr); + chip->state = FL_UNLOCKING; + + spin_unlock_bh(chip->mutex); + schedule_timeout(HZ); + spin_lock_bh(chip->mutex); + + /* FIXME. Use a timer to check this, and return immediately. */ + /* Once the state machine's known to be working I'll do that */ + + timeo = jiffies + (HZ*2); + for (;;) { + + status = cfi_read(map, adr); + if ((status & status_OK) == status_OK) + break; + + /* OK Still waiting */ + if (time_after(jiffies, timeo)) { + cfi_write(map, CMD(0x70), adr); + chip->state = FL_STATUS; + printk(KERN_ERR "waiting for unlock to complete timed out. Xstatus = %x, status = %x.\n", status, cfi_read(map, adr)); + DISABLE_VPP(map); + spin_unlock_bh(chip->mutex); + return -EIO; + } + + /* Latency issues. Drop the unlock, wait a while and retry */ + spin_unlock_bh(chip->mutex); + cfi_udelay(1); + spin_lock_bh(chip->mutex); + } + + /* Done and happy. */ + chip->state = FL_STATUS; + DISABLE_VPP(map); + wake_up(&chip->wq); + spin_unlock_bh(chip->mutex); + return 0; +} +static int cfi_staa_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + unsigned long adr; + int chipnum, ret = 0; +#ifdef DEBUG_LOCK_BITS + int ofs_factor = cfi->interleave * cfi->device_type; +#endif + + chipnum = ofs >> cfi->chipshift; + adr = ofs - (chipnum << cfi->chipshift); + +#ifdef DEBUG_LOCK_BITS + { + unsigned long temp_adr = adr; + unsigned long temp_len = len; + + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + while (temp_len) { + printk("before unlock %x: block status register is %x\n",temp_adr,cfi_read_query(map, temp_adr+(2*ofs_factor))); + temp_adr += mtd->erasesize; + temp_len -= mtd->erasesize; + } + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); + } +#endif + + ret = do_unlock_oneblock(map, &cfi->chips[chipnum], adr); + +#ifdef DEBUG_LOCK_BITS + cfi_send_gen_cmd(0x90, 0x55, 0, map, cfi, cfi->device_type, NULL); + printk("after unlock: block status register is %x\n",cfi_read_query(map, adr+(2*ofs_factor))); + cfi_send_gen_cmd(0xff, 0x55, 0, map, cfi, cfi->device_type, NULL); +#endif + + return ret; +} + +static int cfi_staa_suspend(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + int ret = 0; + + for (i=0; !ret && i<cfi->numchips; i++) { + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + switch(chip->state) { + case FL_READY: + case FL_STATUS: + case FL_CFI_QUERY: + case FL_JEDEC_QUERY: + 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; + break; + } + spin_unlock_bh(chip->mutex); + } + + /* Unlock the chips again */ + + if (ret) { + for (i--; i >=0; i--) { + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + if (chip->state == FL_PM_SUSPENDED) { + /* No need to force it into a known state here, + because we're returning failure, and it didn't + get power cycled */ + chip->state = chip->oldstate; + wake_up(&chip->wq); + } + spin_unlock_bh(chip->mutex); + } + } + + return ret; +} + +static void cfi_staa_resume(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + int i; + struct flchip *chip; + + for (i=0; i<cfi->numchips; i++) { + + chip = &cfi->chips[i]; + + spin_lock_bh(chip->mutex); + + /* Go to known state. Chip may have been power cycled */ + if (chip->state == FL_PM_SUSPENDED) { + cfi_write(map, CMD(0xFF), 0); + chip->state = FL_READY; + wake_up(&chip->wq); + } + + spin_unlock_bh(chip->mutex); + } +} + +static void cfi_staa_destroy(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + kfree(cfi->cmdset_priv); + kfree(cfi); +} + +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define cfi_staa_init init_module +#define cfi_staa_exit cleanup_module +#endif + +static char im_name[]="cfi_cmdset_0020"; + +int __init cfi_staa_init(void) +{ + inter_module_register(im_name, THIS_MODULE, &cfi_cmdset_0020); + return 0; +} + +static void __exit cfi_staa_exit(void) +{ + inter_module_unregister(im_name); +} + +module_init(cfi_staa_init); +module_exit(cfi_staa_exit); diff --git a/drivers/mtd/chips/cfi_probe.c b/drivers/mtd/chips/cfi_probe.c index 0471edd27ed9..fba4ddf0fc6a 100644 --- a/drivers/mtd/chips/cfi_probe.c +++ b/drivers/mtd/chips/cfi_probe.c @@ -1,13 +1,14 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: cfi_probe.c,v 1.66 2001/10/02 15:05:12 dwmw2 Exp $ + $Id: cfi_probe.c,v 1.71 2003/05/28 12:51:48 dwmw2 Exp $ */ #include <linux/config.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <asm/byteorder.h> #include <linux/errno.h> @@ -24,16 +25,13 @@ static void print_cfi_ident(struct cfi_ident *); #endif -int cfi_jedec_setup(struct cfi_private *p_cfi, int index); -int cfi_jedec_lookup(int index, int mfr_id, int dev_id); - static int cfi_probe_chip(struct map_info *map, __u32 base, struct flchip *chips, 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); -/* check for QRY, or search for jedec id. +/* check for QRY. in: interleave,type,mode ret: table index, <0 for error */ @@ -55,6 +53,18 @@ static int cfi_probe_chip(struct map_info *map, __u32 base, { int i; + if ((base + 0) >= map->size) { + printk(KERN_NOTICE + "Probe at base[0x00](0x%08lx) past the end of the map(0x%08lx)\n", + (unsigned long)base, map->size -1); + return 0; + } + if ((base + 0xff) >= map->size) { + printk(KERN_NOTICE + "Probe at base[0x55](0x%08lx) past the end of the map(0x%08lx)\n", + (unsigned long)base + 0x55, map->size -1); + return 0; + } cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); cfi_send_gen_cmd(0x98, 0x55, base, map, cfi, cfi->device_type, NULL); @@ -139,7 +149,7 @@ static int cfi_chip_setup(struct map_info *map, memset(cfi->cfiq,0,sizeof(struct cfi_ident)); - cfi->cfi_mode = 1; + cfi->cfi_mode = CFI_MODE_CFI; cfi->fast_prog=1; /* CFI supports fast programming */ /* Read the CFI info structure */ @@ -250,11 +260,11 @@ static void print_cfi_ident(struct cfi_ident *cfip) else printk("Full buffer write not supported\n"); - printk("Typical block erase timeout: %d µs\n", 1<<cfip->BlockEraseTimeoutTyp); - printk("Maximum block erase timeout: %d µs\n", (1<<cfip->BlockEraseTimeoutMax) * (1<<cfip->BlockEraseTimeoutTyp)); + printk("Typical block erase timeout: %d ms\n", 1<<cfip->BlockEraseTimeoutTyp); + printk("Maximum block erase timeout: %d ms\n", (1<<cfip->BlockEraseTimeoutMax) * (1<<cfip->BlockEraseTimeoutTyp)); if (cfip->ChipEraseTimeoutTyp || cfip->ChipEraseTimeoutMax) { - printk("Typical chip erase timeout: %d µs\n", 1<<cfip->ChipEraseTimeoutTyp); - printk("Maximum chip erase timeout: %d µs\n", (1<<cfip->ChipEraseTimeoutMax) * (1<<cfip->ChipEraseTimeoutTyp)); + printk("Typical chip erase timeout: %d ms\n", 1<<cfip->ChipEraseTimeoutTyp); + printk("Maximum chip erase timeout: %d ms\n", (1<<cfip->ChipEraseTimeoutMax) * (1<<cfip->ChipEraseTimeoutTyp)); } else printk("Chip erase not supported\n"); diff --git a/drivers/mtd/chips/chipreg.c b/drivers/mtd/chips/chipreg.c index 788b55283d32..3bc9199d2b78 100644 --- a/drivers/mtd/chips/chipreg.c +++ b/drivers/mtd/chips/chipreg.c @@ -1,5 +1,5 @@ /* - * $Id: chipreg.c,v 1.12 2001/10/02 15:29:53 dwmw2 Exp $ + * $Id: chipreg.c,v 1.15 2003/05/21 15:15:05 dwmw2 Exp $ * * Registration for chip drivers * @@ -7,10 +7,13 @@ #include <linux/kernel.h> #include <linux/config.h> +#include <linux/module.h> #include <linux/kmod.h> #include <linux/spinlock.h> -#include <linux/mtd/compatmac.h> +#include <linux/slab.h> #include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/compatmac.h> spinlock_t chip_drvs_lock = SPIN_LOCK_UNLOCKED; static LIST_HEAD(chip_drvs_list); @@ -29,7 +32,7 @@ void unregister_mtd_chip_driver(struct mtd_chip_driver *drv) spin_unlock(&chip_drvs_lock); } -static struct mtd_chip_driver *get_mtd_chip_driver (char *name) +static struct mtd_chip_driver *get_mtd_chip_driver (const char *name) { struct list_head *pos; struct mtd_chip_driver *ret = NULL, *this; @@ -44,10 +47,8 @@ static struct mtd_chip_driver *get_mtd_chip_driver (char *name) break; } } - if (ret && !try_module_get(ret->module)) { - /* Eep. Failed. */ + if (ret && !try_module_get(ret->module)) ret = NULL; - } spin_unlock(&chip_drvs_lock); @@ -57,7 +58,7 @@ static struct mtd_chip_driver *get_mtd_chip_driver (char *name) /* Hide all the horrid details, like some silly person taking get_module_symbol() away from us, from the caller. */ -struct mtd_info *do_map_probe(char *name, struct map_info *map) +struct mtd_info *do_map_probe(const char *name, struct map_info *map) { struct mtd_chip_driver *drv; struct mtd_info *ret; @@ -84,10 +85,26 @@ struct mtd_info *do_map_probe(char *name, struct map_info *map) return NULL; } +/* + * Destroy an MTD device which was created for a map device. + * Make sure the MTD device is already unregistered before calling this + */ +void map_destroy(struct mtd_info *mtd) +{ + struct map_info *map = mtd->priv; + + if (map->fldrv->destroy) + map->fldrv->destroy(mtd); + + module_put(map->fldrv->module); + + kfree(mtd); +} EXPORT_SYMBOL(register_mtd_chip_driver); EXPORT_SYMBOL(unregister_mtd_chip_driver); EXPORT_SYMBOL(do_map_probe); +EXPORT_SYMBOL(map_destroy); MODULE_LICENSE("GPL"); MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); diff --git a/drivers/mtd/chips/gen_probe.c b/drivers/mtd/chips/gen_probe.c index c6e815ecc8db..6be2a2b17480 100644 --- a/drivers/mtd/chips/gen_probe.c +++ b/drivers/mtd/chips/gen_probe.c @@ -2,13 +2,16 @@ * Routines common to all CFI-type probes. * (C) 2001, 2001 Red Hat, Inc. * GPL'd - * $Id: gen_probe.c,v 1.5 2001/10/02 15:05:12 dwmw2 Exp $ + * $Id: gen_probe.c,v 1.11 2003/05/21 15:15:05 dwmw2 Exp $ */ #include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/cfi.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/gen_probe.h> static struct mtd_info *check_cmd_set(struct map_info *, int); @@ -38,7 +41,7 @@ struct mtd_info *mtd_do_chip_probe(struct map_info *map, struct chip_probe *cp) if (mtd) return mtd; - printk(KERN_WARNING"cfi_probe: No supported Vendor Command Set found\n"); + printk(KERN_WARNING"gen_probe: No supported Vendor Command Set found\n"); kfree(cfi->cfiq); kfree(cfi); @@ -57,6 +60,7 @@ struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe int i; memset(&cfi, 0, sizeof(cfi)); + memset(&chip[0], 0, sizeof(chip)); /* Call the probetype-specific code with all permutations of interleave and device type, etc. */ @@ -106,6 +110,12 @@ struct cfi_private *genprobe_ident_chips(struct map_info *map, struct chip_probe * Now probe for other chips, checking sensibly for aliases while * we're at it. The new_chip probe above should have let the first * chip in read mode. + * + * NOTE: Here, we're checking if there is room for another chip + * the same size within the mapping. Therefore, + * base + chipsize <= map->size is the correct thing to do, + * because, base + chipsize would be the _first_ byte of the + * next chip, not the one we're currently pondering. */ for (base = (1<<cfi.chipshift); base + (1<<cfi.chipshift) <= map->size; @@ -224,6 +234,41 @@ static int genprobe_new_chip(struct map_info *map, struct chip_probe *cp, break; #endif /* CFIDEV_BUSWIDTH_4 */ +#ifdef CFIDEV_BUSWIDTH_8 + case CFIDEV_BUSWIDTH_8: +#if defined(CFIDEV_INTERLEAVE_2) && defined(SOMEONE_ACTUALLY_MAKES_THESE) + cfi->interleave = CFIDEV_INTERLEAVE_2; + + cfi->device_type = CFI_DEVICETYPE_X32; + if (cp->probe_chip(map, 0, NULL, cfi)) + return 1; +#endif /* CFIDEV_INTERLEAVE_2 */ +#ifdef CFIDEV_INTERLEAVE_4 + cfi->interleave = CFIDEV_INTERLEAVE_4; + +#ifdef SOMEONE_ACTUALLY_MAKES_THESE + cfi->device_type = CFI_DEVICETYPE_X32; + if (cp->probe_chip(map, 0, NULL, cfi)) + return 1; +#endif + cfi->device_type = CFI_DEVICETYPE_X16; + if (cp->probe_chip(map, 0, NULL, cfi)) + return 1; +#endif /* CFIDEV_INTERLEAVE_4 */ +#ifdef CFIDEV_INTERLEAVE_8 + cfi->interleave = CFIDEV_INTERLEAVE_8; + + cfi->device_type = CFI_DEVICETYPE_X16; + if (cp->probe_chip(map, 0, NULL, cfi)) + return 1; + + cfi->device_type = CFI_DEVICETYPE_X8; + if (cp->probe_chip(map, 0, NULL, cfi)) + return 1; +#endif /* CFIDEV_INTERLEAVE_8 */ + break; +#endif /* CFIDEV_BUSWIDTH_8 */ + default: printk(KERN_WARNING "genprobe_new_chip called with unsupported buswidth %d\n", map->buswidth); return 0; @@ -289,6 +334,10 @@ static struct mtd_info *check_cmd_set(struct map_info *map, int primary) case 0x0002: return cfi_cmdset_0002(map, primary); #endif +#ifdef CONFIG_MTD_CFI_STAA + case 0x0020: + return cfi_cmdset_0020(map, primary); +#endif } return cfi_cmdset_unknown(map, primary); diff --git a/drivers/mtd/chips/jedec.c b/drivers/mtd/chips/jedec.c index d621ddf6304d..7fd282f5e7e4 100644 --- a/drivers/mtd/chips/jedec.c +++ b/drivers/mtd/chips/jedec.c @@ -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.12 2001/11/06 14:37:35 dwmw2 Exp $ + * $Id: jedec.c,v 1.18 2003/05/28 12:51:48 dwmw2 Exp $ */ +#include <linux/init.h> +#include <linux/module.h> +#include <linux/kernel.h> #include <linux/mtd/jedec.h> +#include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/compatmac.h> static struct mtd_info *jedec_probe(struct map_info *); static int jedec_probe8(struct map_info *map,unsigned long base, @@ -168,7 +174,8 @@ static struct mtd_info *jedec_probe(struct map_info *map) /* Generate a part name that includes the number of different chips and other configuration information */ count = 1; - strlcpy(Part,map->name,sizeof(Part)-10); + strncpy(Part,map->name,sizeof(Part)-10); + Part[sizeof(Part)-11] = 0; strcat(Part," "); Uniq = 0; for (I = 0; priv->chips[I].jedec != 0 && I < MAX_JEDEC_CHIPS; I++) @@ -245,7 +252,8 @@ static struct mtd_info *jedec_probe(struct map_info *map) // printk("Part: '%s'\n",Part); memset(MTD,0,sizeof(*MTD)); - // strlcpy(MTD->name,Part,sizeof(MTD->name)); + // strncpy(MTD->name,Part,sizeof(MTD->name)); + // MTD->name[sizeof(MTD->name)-1] = 0; MTD->name = map->name; MTD->type = MTD_NORFLASH; MTD->flags = MTD_CAP_NORFLASH; @@ -264,7 +272,7 @@ static struct mtd_info *jedec_probe(struct map_info *map) MTD->priv = map; map->fldrv_priv = priv; map->fldrv = &jedec_chipdrv; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); return MTD; } @@ -386,8 +394,8 @@ static const struct JEDECTable *jedec_idtoinf(__u8 mfr,__u8 id) 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; @@ -446,8 +454,8 @@ static int jedec_probe16(struct map_info *map,unsigned long base, 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; @@ -500,8 +508,8 @@ static int jedec_probe32(struct map_info *map,unsigned long base, we call this routine with the JEDEC return still enabled, if two or more flashes have a truncated address space the probe test will still work */ - if (base + Size+0x555 < map->size && - base + Size+0x555 < (base & (~(my_bank_size-1))) + my_bank_size) + if (base + (Size<<2)+0x555 < map->size && + base + (Size<<2)+0x555 < (base & (~(my_bank_size-1))) + my_bank_size) { if (flread(base+Size) != flread(base+Size + 0x100) || flread(base+Size + 1) != flread(base+Size + 0x101)) @@ -525,7 +533,7 @@ static int jedec_read(struct mtd_info *mtd, loff_t from, size_t len, { 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; } @@ -549,7 +557,7 @@ static int jedec_read_banked(struct mtd_info *mtd, loff_t from, size_t len, 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; @@ -580,8 +588,8 @@ static void jedec_flash_failed(unsigned char code) static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) { // Does IO to the currently selected chip - #define flread(x) map->read8(map,chip->base+((x)<<chip->addrshift)) - #define flwrite(v,x) map->write8(map,v,chip->base+((x)<<chip->addrshift)) + #define flread(x) map_read8(map,chip->base+((x)<<chip->addrshift)) + #define flwrite(v,x) map_write8(map,v,chip->base+((x)<<chip->addrshift)) unsigned long Time = 0; unsigned long NoTime = 0; @@ -686,19 +694,19 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) 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; @@ -734,13 +742,13 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) 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++; @@ -773,6 +781,7 @@ static int flash_erase(struct mtd_info *mtd, struct erase_info *instr) } //printk("done\n"); + instr->state = MTD_ERASE_DONE; if (instr->callback) instr->callback(instr); return 0; @@ -790,9 +799,9 @@ static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, { /* Does IO to the currently selected chip. It takes the bank addressing base (which is divisible by the chip size) adds the necessary lower bits - of addrshift (interleve index) and then adds the control register index. */ - #define flread(x) map->read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) - #define flwrite(v,x) map->write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) + of addrshift (interleave index) and then adds the control register index. */ + #define flread(x) map_read8(map,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) + #define flwrite(v,x) map_write8(map,v,base+(off&((1<<chip->addrshift)-1))+((x)<<chip->addrshift)) struct map_info *map = (struct map_info *)mtd->priv; struct jedec_private *priv = (struct jedec_private *)map->fldrv_priv; @@ -828,7 +837,7 @@ static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, // 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; @@ -843,10 +852,10 @@ static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, 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 @@ -854,7 +863,7 @@ static int flash_write(struct mtd_info *mtd, loff_t start, size_t len, 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 --git a/drivers/mtd/chips/jedec_probe.c b/drivers/mtd/chips/jedec_probe.c index f1e47a69bec9..4831c9fcb109 100644 --- a/drivers/mtd/chips/jedec_probe.c +++ b/drivers/mtd/chips/jedec_probe.c @@ -1,7 +1,9 @@ /* Common Flash Interface probe code. (C) 2000 Red Hat. GPL'd. - $Id: jedec_probe.c,v 1.3 2001/10/02 15:05:12 dwmw2 Exp $ + $Id: jedec_probe.c,v 1.29 2003/05/28 13:57:46 dwmw2 Exp $ + See JEDEC (http://www.jedec.org/) standard JESD21C (section 3.5) + for the standard this probe goes back to. */ #include <linux/config.h> @@ -14,18 +16,23 @@ #include <linux/slab.h> #include <linux/interrupt.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/cfi.h> #include <linux/mtd/gen_probe.h> - /* Manufacturers */ #define MANUFACTURER_AMD 0x0001 -#define MANUFACTURER_FUJITSU 0x0004 #define MANUFACTURER_ATMEL 0x001f -#define MANUFACTURER_ST 0x0020 +#define MANUFACTURER_FUJITSU 0x0004 +#define MANUFACTURER_INTEL 0x0089 +#define MANUFACTURER_MACRONIX 0x00C2 +#define MANUFACTURER_PMC 0x009D #define MANUFACTURER_SST 0x00BF +#define MANUFACTURER_ST 0x0020 #define MANUFACTURER_TOSHIBA 0x0098 +#define MANUFACTURER_WINBOND 0x00da + /* AMD */ #define AM29F800BB 0x2258 @@ -34,27 +41,167 @@ #define AM29LV800BT 0x22DA #define AM29LV160DT 0x22C4 #define AM29LV160DB 0x2249 +#define AM29F017D 0x003D +#define AM29F016D 0x00AD +#define AM29F080 0x00D5 +#define AM29F040 0x00A4 +#define AM29LV040B 0x004F +#define AM29F032B 0x0041 /* Atmel */ -#define AT49BV16X4 0x00c0 -#define AT49BV16X4T 0x00c2 +#define AT49BV512 0x0003 +#define AT29LV512 0x003d +#define AT49BV16X 0x00C0 +#define AT49BV16XT 0x00C2 +#define AT49BV32X 0x00C8 +#define AT49BV32XT 0x00C9 /* Fujitsu */ +#define MBM29LV650UE 0x22D7 +#define MBM29LV320TE 0x22F6 +#define MBM29LV320BE 0x22F9 #define MBM29LV160TE 0x22C4 #define MBM29LV160BE 0x2249 +#define MBM29LV800BA 0x225B +#define MBM29LV800TA 0x22DA + +/* Intel */ +#define I28F004B3T 0x00d4 +#define I28F004B3B 0x00d5 +#define I28F400B3T 0x8894 +#define I28F400B3B 0x8895 +#define I28F008S5 0x00a6 +#define I28F016S5 0x00a0 +#define I28F008SA 0x00a2 +#define I28F008B3T 0x00d2 +#define I28F008B3B 0x00d3 +#define I28F800B3T 0x8892 +#define I28F800B3B 0x8893 +#define I28F016S3 0x00aa +#define I28F016B3T 0x00d0 +#define I28F016B3B 0x00d1 +#define I28F160B3T 0x8890 +#define I28F160B3B 0x8891 +#define I28F320B3T 0x8896 +#define I28F320B3B 0x8897 +#define I28F640B3T 0x8898 +#define I28F640B3B 0x8899 +#define I82802AB 0x00ad +#define I82802AC 0x00ac + +/* Macronix */ +#define MX29LV160T 0x22C4 +#define MX29LV160B 0x2249 +#define MX29F016 0x00AD +#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 /* SST */ +#define SST29EE512 0x005d +#define SST29LE512 0x003d #define SST39LF800 0x2781 #define SST39LF160 0x2782 +#define SST39LF512 0x00D4 +#define SST39LF010 0x00D5 +#define SST39LF020 0x00D6 +#define SST39LF040 0x00D7 +#define SST39SF010A 0x00B5 +#define SST39SF020A 0x00B6 +#define SST49LF030A 0x001C +#define SST49LF040A 0x0051 +#define SST49LF080A 0x005B /* Toshiba */ #define TC58FVT160 0x00C2 #define TC58FVB160 0x0043 +#define TC58FVT321 0x009A +#define TC58FVB321 0x009C +#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 { @@ -64,190 +211,1137 @@ struct amd_flash_info { const int DevSize; const int InterfaceDesc; const int NumEraseRegions; + const int CmdSet; + const __u8 uaddr[3]; /* unlock addrs for 8, 16, 32 modes */ const ulong regions[4]; }; #define ERASEINFO(size,blocks) (size<<8)|(blocks-1) -#define SIZE_1MiB 20 -#define SIZE_2MiB 21 -#define SIZE_4MiB 22 +#define SIZE_64KiB 16 +#define SIZE_128KiB 17 +#define SIZE_256KiB 18 +#define SIZE_512KiB 19 +#define SIZE_1MiB 20 +#define SIZE_2MiB 21 +#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 = AM29LV160DT, - .name = "AMD AM29LV160DT", - .DevSize = SIZE_2MiB, - .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, - .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, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x10000,31), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) - } - }, { - .mfr_id = MANUFACTURER_FUJITSU, - .dev_id = MBM29LV160TE, - .name = "Fujitsu MBM29LV160TE", - .DevSize = SIZE_2MiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x10000,31), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) - } - }, { - .mfr_id = MANUFACTURER_TOSHIBA, - .dev_id = TC58FVB160, - .name = "Toshiba TC58FVB160", - .DevSize = SIZE_2MiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x04000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x08000,1), - ERASEINFO(0x10000,31) - } - }, { - .mfr_id = MANUFACTURER_FUJITSU, - .dev_id = MBM29LV160BE, - .name = "Fujitsu MBM29LV160BE", - .DevSize = SIZE_2MiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x04000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x08000,1), - ERASEINFO(0x10000,31) - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29LV800BB, - .name = "AMD AM29LV800BB", - .DevSize = SIZE_1MiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x04000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x08000,1), - ERASEINFO(0x10000,15), - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29F800BB, - .name = "AMD AM29F800BB", - .DevSize = SIZE_1MiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x04000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x08000,1), - ERASEINFO(0x10000,15), - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29LV800BT, - .name = "AMD AM29LV800BT", - .DevSize = SIZE_1MiB, - .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, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x10000,15), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) - } - }, { - .mfr_id = MANUFACTURER_AMD, - .dev_id = AM29LV800BB, - .name = "AMD AM29LV800BB", - .DevSize = SIZE_1MiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x10000,15), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) - } - }, { - .mfr_id = MANUFACTURER_ST, - .dev_id = M29W800T, - .name = "ST M29W800T", - .DevSize = SIZE_1MiB, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x10000,15), - ERASEINFO(0x08000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x04000,1) - } - }, { - .mfr_id = MANUFACTURER_ST, - .dev_id = M29W160DT, - .name = "ST M29W160DT", - .DevSize = SIZE_2MiB, - .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, - .NumEraseRegions = 4, - .regions = {ERASEINFO(0x04000,1), - ERASEINFO(0x02000,2), - ERASEINFO(0x08000,1), - ERASEINFO(0x10000,31) - } - }, { - .mfr_id = MANUFACTURER_ATMEL, - .dev_id = AT49BV16X4, - .name = "Atmel AT49BV16X4", - .DevSize = SIZE_2MiB, - .NumEraseRegions = 3, - .regions = {ERASEINFO(0x02000,8), - ERASEINFO(0x08000,2), - ERASEINFO(0x10000,30) - } - }, { - .mfr_id = MANUFACTURER_ATMEL, - .dev_id = AT49BV16X4T, - .name = "Atmel AT49BV16X4T", - .DevSize = SIZE_2MiB, - .NumEraseRegions = 3, - .regions = {ERASEINFO(0x10000,30), - ERASEINFO(0x08000,2), - ERASEINFO(0x02000,8) - } + .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", + .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_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,15), + } + }, { + .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_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_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_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_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_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_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 = 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 = 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 = 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_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,31) + } + }, { + .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) + } + }, { + .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_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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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", + .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_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_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_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_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 = 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_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_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_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_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) + } + }, { + .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,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_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), + } } }; @@ -258,12 +1352,70 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, struct flchip *chips, struct cfi_private *cfi); struct mtd_info *jedec_probe(struct map_info *map); -#define jedec_read_mfr(map, base, osf) cfi_read(map, base) -#define jedec_read_id(map, base, osf) cfi_read(map, (base)+(osf)) + +static inline u32 jedec_read_mfr(struct map_info *map, __u32 base, + struct cfi_private *cfi) +{ + u32 result, mask; + mask = (1 << (cfi->device_type * 8)) -1; + result = cfi_read(map, base); + result &= mask; + return result; +} + +static inline u32 jedec_read_id(struct map_info *map, __u32 base, + struct cfi_private *cfi) +{ + int osf; + u32 result, mask; + osf = cfi->interleave *cfi->device_type; + mask = (1 << (cfi->device_type * 8)) -1; + result = cfi_read(map, base + osf); + result &= mask; + return result; +} + +static inline void jedec_reset(u32 base, struct map_info *map, + struct cfi_private *cfi) +{ + /* Reset */ + cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + /* Some misdesigned intel chips do not respond for 0xF0 for a reset, + * so ensure we're in read mode. Send both the Intel and the AMD command + * for this. Intel uses 0xff for this, AMD uses 0xff for NOP, so + * 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]; + + uaddr_done: + return uaddr; +} + static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) { int i,num_erase_regions; + __u8 uaddr; printk("Found: %s\n",jedec_table[index].name); @@ -276,29 +1428,142 @@ static int cfi_jedec_setup(struct cfi_private *p_cfi, int index) } memset(p_cfi->cfiq,0,sizeof(struct cfi_ident)); - - p_cfi->cfiq->P_ID = P_ID_AMD_STD; + + p_cfi->cfiq->P_ID = jedec_table[index].CmdSet; p_cfi->cfiq->NumEraseRegions = jedec_table[index].NumEraseRegions; p_cfi->cfiq->DevSize = jedec_table[index].DevSize; + p_cfi->cfi_mode = CFI_MODE_JEDEC; for (i=0; i<num_erase_regions; i++){ 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 ( MTD_UADDR_NOT_SUPPORTED ) { + kfree( p_cfi->cfiq ); + return 0; + } + p_cfi->addr_unlock1 = unlock_addrs[uaddr].addr1; + p_cfi->addr_unlock2 = unlock_addrs[uaddr].addr2; + return 1; /* ok */ } + +/* + * 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 rc = 0; /* failure until all tests pass */ + u32 mfr, id; + __u8 uaddr; + + /* The ID's must match */ + if ( cfi->mfr != finfo->mfr_id || cfi->id != finfo->dev_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 + ( 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 ( MTD_UADDR_NOT_SUPPORTED ) { + goto match_done; + } + + 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 != cfi->addr_unlock1 + || unlock_addrs[uaddr].addr2 != cfi->addr_unlock2 ) ) { + DEBUG( MTD_DEBUG_LEVEL3, + "MTD %s(): 0x%.4x 0x%.4x did not match\n", + __func__, + unlock_addrs[uaddr].addr1, + unlock_addrs[uaddr].addr2 ); + 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, struct flchip *chips, struct cfi_private *cfi) { int i; - int osf = cfi->interleave * cfi->device_type; - int retried = 0; + int unlockpass = 0; + /* + * FIXME - eventually replace these unlock address seeds with + * information from unlock_addrs[]. + */ if (!cfi->numchips) { switch (cfi->device_type) { case CFI_DEVICETYPE_X8: - cfi->addr_unlock1 = 0x555; - cfi->addr_unlock2 = 0x2aa; + cfi->addr_unlock1 = 0x555; + cfi->addr_unlock2 = 0x2aa; break; case CFI_DEVICETYPE_X16: cfi->addr_unlock1 = 0xaaa; @@ -320,48 +1585,98 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, } retry: + /* Make certain we aren't probing past the end of map */ + if (base >= map->size) { + printk(KERN_NOTICE + "Probe at base(0x%08x) past the end of the map(0x%08lx)\n", + base, map->size -1); + return 0; + + } + if ((base + cfi->addr_unlock1) >= map->size) { + printk(KERN_NOTICE + "Probe at addr_unlock1(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n", + base, cfi->addr_unlock1, map->size -1); + + return 0; + } + if ((base + cfi->addr_unlock2) >= map->size) { + printk(KERN_NOTICE + "Probe at addr_unlock2(0x%08x + 0x%08x) past the end of the map(0x%08lx)\n", + base, cfi->addr_unlock2, map->size -1); + return 0; + + } + /* Reset */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + jedec_reset(base, map, cfi); /* Autoselect Mode */ - 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); + 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 */ if (!cfi->numchips) { /* This is the first time we're called. Set up the CFI stuff accordingly and return */ - cfi->mfr = jedec_read_mfr(map, base, osf); - cfi->id = jedec_read_id(map, base, osf); - + 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", + cfi->mfr, cfi->id, cfi->interleave, cfi->device_type); for (i=0; i<sizeof(jedec_table)/sizeof(jedec_table[0]); i++) { - if (cfi->mfr == jedec_table[i].mfr_id && - cfi->id == jedec_table[i].dev_id) - return cfi_jedec_setup(cfi, i); - } - if (!retried++) { - /* Deal with whichever strange chips these were */ - cfi->addr_unlock1 |= cfi->addr_unlock1 << 8; - cfi->addr_unlock2 |= cfi->addr_unlock2 << 8; + 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; + + /* Make sure it is a chip of the same manufacturer and id */ + mfr = jedec_read_mfr(map, base, cfi); + id = jedec_read_id(map, base, cfi); + + if ((mfr != cfi->mfr) || (id != cfi->id)) { + printk(KERN_DEBUG "%s: Found different chip or no chip at all (mfr 0x%x, id 0x%x) at 0x%x\n", + map->name, mfr, id, base); + jedec_reset(base, map, cfi); + return 0; + } } /* Check each previous chip to see if it's an alias */ for (i=0; i<cfi->numchips; i++) { /* This chip should be in read mode if it's one we've already touched. */ - if (jedec_read_mfr(map, base, osf) == cfi->mfr && - jedec_read_id(map, base, osf) == cfi->id) { + if (jedec_read_mfr(map, chips[i].start, cfi) == cfi->mfr && + jedec_read_id(map, chips[i].start, cfi) == cfi->id) { /* Eep. This chip also looks like it's in autoselect mode. Is it an alias for the new one? */ - - cfi_send_gen_cmd(0xF0, 0, chips[i].start, map, cfi, cfi->device_type, NULL); + jedec_reset(chips[i].start, map, cfi); + /* If the device IDs go away, it's an alias */ - if (jedec_read_mfr(map, base, osf) != cfi->mfr || - jedec_read_id(map, base, osf) != cfi->id) { + 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); return 0; @@ -371,9 +1686,9 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, * unfortunate. Stick the new chip in read mode * too and if it's the same, assume it's an alias. */ /* FIXME: Use other modes to do a proper check */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); - if (jedec_read_mfr(map, base, osf) == cfi->mfr && - jedec_read_id(map, base, osf) == cfi->id) { + jedec_reset(base, map, cfi); + 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); return 0; @@ -392,8 +1707,9 @@ static int jedec_probe_chip(struct map_info *map, __u32 base, chips[cfi->numchips].state = FL_READY; cfi->numchips++; +ok_out: /* Put it back into Read Mode */ - cfi_send_gen_cmd(0xF0, 0, base, map, cfi, cfi->device_type, NULL); + jedec_reset(base, map, cfi); 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, diff --git a/drivers/mtd/chips/map_absent.c b/drivers/mtd/chips/map_absent.c index 1c884572e2cc..f9ff6f02c79b 100644 --- a/drivers/mtd/chips/map_absent.c +++ b/drivers/mtd/chips/map_absent.c @@ -1,7 +1,7 @@ /* * Common code to handle absent "placeholder" devices * Copyright 2001 Resilience Corporation <ebrower@resilience.com> - * $Id: map_absent.c,v 1.2 2001/10/02 15:05:12 dwmw2 Exp $ + * $Id: map_absent.c,v 1.4 2003/05/28 12:51:49 dwmw2 Exp $ * * This map driver is used to allocate "placeholder" MTD * devices on systems that have socketed/removable media. @@ -23,9 +23,10 @@ #include <linux/kernel.h> #include <linux/errno.h> #include <linux/slab.h> - +#include <linux/init.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/map.h> - +#include <linux/mtd/compatmac.h> static int map_absent_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int map_absent_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -36,10 +37,10 @@ static void map_absent_destroy (struct mtd_info *); static struct mtd_chip_driver map_absent_chipdrv = { - .probe = map_absent_probe, + .probe = map_absent_probe, .destroy = map_absent_destroy, - .name = "map_absent", - .module = THIS_MODULE + .name = "map_absent", + .module = THIS_MODULE }; static struct mtd_info *map_absent_probe(struct map_info *map) @@ -65,7 +66,7 @@ static struct mtd_info *map_absent_probe(struct map_info *map) mtd->flags = 0; mtd->erasesize = PAGE_SIZE; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); return mtd; } diff --git a/drivers/mtd/chips/map_ram.c b/drivers/mtd/chips/map_ram.c index 872ee9e280fd..7e17b452e1c2 100644 --- a/drivers/mtd/chips/map_ram.c +++ b/drivers/mtd/chips/map_ram.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple RAM * (C) 2000 Red Hat. GPL'd. - * $Id: map_ram.c,v 1.14 2001/10/02 15:05:12 dwmw2 Exp $ + * $Id: map_ram.c,v 1.17 2003/05/28 12:51:49 dwmw2 Exp $ */ #include <linux/module.h> @@ -11,8 +11,10 @@ #include <asm/byteorder.h> #include <linux/errno.h> #include <linux/slab.h> - +#include <linux/init.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/map.h> +#include <linux/mtd/compatmac.h> static int mapram_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); @@ -34,21 +36,21 @@ static struct mtd_info *map_ram_probe(struct map_info *map) /* 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 @@ static struct mtd_info *map_ram_probe(struct map_info *map) while(mtd->size & (mtd->erasesize - 1)) mtd->erasesize >>= 1; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); return mtd; } @@ -83,7 +85,7 @@ static int mapram_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *r { 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 @@ static int mapram_write (struct mtd_info *mtd, loff_t to, size_t len, size_t *re { 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 @@ static int mapram_erase (struct mtd_info *mtd, struct erase_info *instr) unsigned long i; for (i=0; i<instr->len; i++) - map->write8(map, 0xFF, instr->addr + i); + map_write8(map, 0xFF, instr->addr + i); if (instr->callback) instr->callback(instr); diff --git a/drivers/mtd/chips/map_rom.c b/drivers/mtd/chips/map_rom.c index 6d7f5146c97e..ace69ac0aa80 100644 --- a/drivers/mtd/chips/map_rom.c +++ b/drivers/mtd/chips/map_rom.c @@ -1,7 +1,7 @@ /* * Common code to handle map devices which are simple ROM * (C) 2000 Red Hat. GPL'd. - * $Id: map_rom.c,v 1.17 2001/10/02 15:05:12 dwmw2 Exp $ + * $Id: map_rom.c,v 1.20 2003/05/28 12:51:49 dwmw2 Exp $ */ #include <linux/version.h> @@ -12,8 +12,10 @@ #include <asm/byteorder.h> #include <linux/errno.h> #include <linux/slab.h> - +#include <linux/init.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/map.h> +#include <linux/mtd/compatmac.h> static int maprom_read (struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int maprom_write (struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -49,7 +51,7 @@ struct mtd_info *map_rom_probe(struct map_info *map) while(mtd->size & (mtd->erasesize - 1)) mtd->erasesize >>= 1; - MOD_INC_USE_COUNT; + __module_get(THIS_MODULE); return mtd; } @@ -58,7 +60,7 @@ static int maprom_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *r { 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 --git a/drivers/mtd/chips/sharp.c b/drivers/mtd/chips/sharp.c index 0b1d2ef38059..f918479e1709 100644 --- a/drivers/mtd/chips/sharp.c +++ b/drivers/mtd/chips/sharp.c @@ -4,7 +4,7 @@ * Copyright 2000,2001 David A. Schleef <ds@schleef.org> * 2000,2001 Lineo, Inc. * - * $Id: sharp.c,v 1.6 2001/10/02 15:05:12 dwmw2 Exp $ + * $Id: sharp.c,v 1.11 2003/05/28 12:51:49 dwmw2 Exp $ * * Devices supported: * LH28F016SCT Symmetrical block flash memory, 2Mx8 @@ -165,12 +165,12 @@ static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd) 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){ @@ -198,10 +198,10 @@ static int sharp_probe_map(struct map_info *map,struct mtd_info *mtd) 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); @@ -223,10 +223,10 @@ retry: 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); @@ -254,7 +254,7 @@ retry: goto retry; } - map->write32(map,CMD_RESET, adr); + map_write32(map,CMD_RESET, adr); chip->state = FL_READY; @@ -295,7 +295,7 @@ static int sharp_read(struct mtd_info *mtd, loff_t from, size_t len, if(ret<0) break; - map->copy_from(map,buf,ofs,thislen); + map_copy_from(map,buf,ofs,thislen); sharp_release(&sharp->chips[chipnum]); @@ -356,17 +356,17 @@ static int sharp_write_oneword(struct map_info *map, struct flchip *chip, 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; } @@ -379,9 +379,9 @@ static int sharp_write_oneword(struct map_info *map, struct flchip *chip, 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); @@ -423,6 +423,7 @@ static int sharp_erase(struct mtd_info *mtd, struct erase_info *instr) } } + instr->state = MTD_ERASE_DONE; if(instr->callback) instr->callback(instr); @@ -433,18 +434,18 @@ static int sharp_do_wait_for_ready(struct map_info *map, struct flchip *chip, unsigned long adr) { int ret; - unsigned long timeo; + int timeo; 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; @@ -486,26 +487,26 @@ static int sharp_erase_oneblock(struct map_info *map, struct flchip *chip, 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); @@ -519,17 +520,17 @@ static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip, 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); @@ -539,13 +540,13 @@ static void sharp_unlock_oneblock(struct map_info *map, struct flchip *chip, } 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 --git a/drivers/mtd/cmdline.c b/drivers/mtd/cmdlinepart.c index 74d1c7698777..867fe084f05d 100644 --- a/drivers/mtd/cmdline.c +++ b/drivers/mtd/cmdlinepart.c @@ -1,5 +1,5 @@ /* - * $Id: cmdline.c,v 1.5 2002/11/06 22:40:04 rmk Exp $ + * $Id: cmdlinepart.c,v 1.9 2003/05/16 17:08:24 dwmw2 Exp $ * * Read flash partition table from command line * @@ -92,11 +92,6 @@ static struct mtd_partition * newpart(char *s, else { size = memparse(s, &s); - if (!size) - { - printk(KERN_ERR ERRP "couldn't parse number from input string\n"); - return 0; - } if (size < PAGE_SIZE) { printk(KERN_ERR ERRP "partition size too small (%lx)\n", size); @@ -112,17 +107,13 @@ static struct mtd_partition * newpart(char *s, { s++; offset = memparse(s, &s); - if (!offset) - { - printk(KERN_ERR ERRP "couldn't parse number from input string\n"); - return 0; - } } /* now look for name */ if (*s == '(') { delim = ')'; } + if (delim) { char *p; @@ -295,17 +286,19 @@ static int mtdpart_setup_real(char *s) * Main function to be called from the MTD mapping driver/device to * obtain the partitioning information. At this point the command line * arguments will actually be parsed and turned to struct mtd_partition - * information. + * 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) + if(!cmdline) return -EINVAL; /* parse command line */ @@ -314,7 +307,7 @@ int parse_cmdline_partitions(struct mtd_info *master, for(part = partitions; part; part = part->next) { - if (!strcmp(part->mtd_id, mtd_id)) + if ((!mtd_id) || (!strcmp(part->mtd_id, mtd_id))) { for(i = 0, offset = 0; i < part->num_parts; i++) { @@ -328,7 +321,7 @@ int parse_cmdline_partitions(struct mtd_info *master, { printk(KERN_WARNING ERRP "%s: partitioning exceeds flash size, truncating\n", - mtd_id); + part->mtd_id); part->parts[i].size = master->size - offset; part->num_parts = i; } @@ -355,7 +348,25 @@ static int __init mtdpart_setup(char *s) __setup("mtdparts=", mtdpart_setup); -EXPORT_SYMBOL(parse_cmdline_partitions); +static struct mtd_part_parser cmdline_parser = { + .owner = THIS_MODULE, + .parse_fn = parse_cmdline_partitions, + .name = "cmdlinepart", +}; + +static int __init cmdline_parser_init(void) +{ + return register_mtd_parser(&cmdline_parser); +} + +static void __exit cmdline_parser_exit(void) +{ + deregister_mtd_parser(&cmdline_parser); +} + +module_init(cmdline_parser_init); +module_exit(cmdline_parser_exit); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); diff --git a/drivers/mtd/devices/Kconfig b/drivers/mtd/devices/Kconfig index 41a0bb33bda5..9f17bc5eeae7 100644 --- a/drivers/mtd/devices/Kconfig +++ b/drivers/mtd/devices/Kconfig @@ -1,5 +1,5 @@ -# drivers/mtd/maps/Config.in -# $Id: Config.in,v 1.5 2001/09/23 15:33:10 dwmw2 Exp $ +# drivers/mtd/maps/Kconfig +# $Id: Kconfig,v 1.3 2003/05/28 10:54:23 dwmw2 Exp $ menu "Self-contained MTD device drivers" depends on MTD!=n @@ -38,6 +38,12 @@ config MTD_PMC551_DEBUG is only really useful if you are developing on this driver or suspect a possible hardware or driver bug. If unsure say N. +config MTD_MS02NV + tristate "DEC MS02-NV NVRAM module support" + depends on CONFIG_DECSTATION + help + Support for NVRAM module on DECstation. + config MTD_SLRAM tristate "Uncached system RAM" depends on MTD @@ -108,13 +114,6 @@ config MTD_BLKMTD comment "Disk-On-Chip Device Drivers" -config MTD_DOC1000 - tristate "M-Systems Disk-On-Chip 1000" - depends on MTD - help - This provides an MTD device driver for the M-Systems DiskOnChip - 1000 devices, which are obsolete so you probably want to say 'N'. - config MTD_DOC2000 tristate "M-Systems Disk-On-Chip 2000 and Millennium" depends on MTD @@ -148,10 +147,26 @@ config MTD_DOC2001 emulate a block device by using a kind of file system on the flash chips. +config MTD_DOC2001PLUS + tristate "M-Systems Disk-On-Chip Millennium-only alternative driver (see help)" + depends on MTD + ---help--- + This provides an alternative MTD device driver for the M-Systems + DiskOnChip Millennium devices. Use this if you have problems with + the combined DiskOnChip 2000 and Millennium driver above. To get + the DiskOnChip probe code to load and use this driver instead of + the other one, you will need to undefine DOC_SINGLE_DRIVER near + the beginning of <file:drivers/mtd/devices/docprobe.c>. + + If you use this device, you probably also want to enable the NFTL + 'NAND Flash Translation Layer' option below, which is used to + emulate a block device by using a kind of file system on the flash + chips. + config MTD_DOCPROBE tristate - default m if MTD_DOC2001!=y && MTD_DOC2000!=y && (MTD_DOC2001=m || MTD_DOC2000=m) - default y if MTD_DOC2001=y || MTD_DOC2000=y + default m if MTD_DOC2001!=y && MTD_DOC2000!=y && MTD_DOC2001PLUS!=y && (MTD_DOC2001=m || MTD_DOC2000=m || MOD_DOC2001PLUS=m) + default y if MTD_DOC2001=y || MTD_DOC2000=y || MTD_DOC2001PLUS=y help This isn't a real config option, it's derived. @@ -200,7 +215,7 @@ config MTD_DOCPROBE_55AA continue with probing if it is absent. The signature will always be present for a DiskOnChip 2000 or a normal DiskOnChip Millennium. Only if you have overwritten the first block of a DiskOnChip - Millennium will it be absent. Enable this option if you are using + Millennium will it be absent. Enable this option if you are using LinuxBIOS or if you need to recover a DiskOnChip Millennium on which you have managed to wipe the first block. diff --git a/drivers/mtd/devices/Makefile b/drivers/mtd/devices/Makefile index 7c68dc858147..8ab580b91066 100644 --- a/drivers/mtd/devices/Makefile +++ b/drivers/mtd/devices/Makefile @@ -1,7 +1,7 @@ # # linux/drivers/devices/Makefile # -# $Id: Makefile,v 1.4 2001/06/26 21:10:05 spse Exp $ +# $Id: Makefile.common,v 1.3 2003/05/28 10:54:23 dwmw2 Exp $ # *** BIG UGLY NOTE *** # @@ -10,12 +10,13 @@ # 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_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 diff --git a/drivers/mtd/devices/doc2000.c b/drivers/mtd/devices/doc2000.c index 36ad7dee6918..7365633bbb89 100644 --- a/drivers/mtd/devices/doc2000.c +++ b/drivers/mtd/devices/doc2000.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> * - * $Id: doc2000.c,v 1.46 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: doc2000.c,v 1.52 2003/05/20 21:03:07 dwmw2 Exp $ */ #include <linux/kernel.h> @@ -22,7 +22,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> -#include <linux/mtd/nand_ids.h> #include <linux/mtd/doc2000.h> #define DOC_SUPPORT_2000 @@ -54,9 +53,9 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, 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); + 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); + size_t *retlen, const u_char *buf, u_char *eccbuf, int 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, @@ -97,12 +96,8 @@ static int _DoC_WaitReady(struct DiskOnChip *doc) DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); return -EIO; } - if (need_resched()) { - set_current_state(TASK_UNINTERRUPTIBLE); - schedule_timeout(1); - } - else - udelay(1); + udelay(1); + cond_resched(); } return 0; @@ -320,7 +315,7 @@ static inline int DoC_SelectFloor(struct DiskOnChip *doc, int floor) static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) { - int mfr, id, i; + int mfr, id, i, j; volatile char dummy; /* Page in the required floor/chip */ @@ -378,12 +373,16 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) /* Print and store the manufacturer and ID codes. */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (mfr == nand_flash_ids[i].manufacture_id && - id == nand_flash_ids[i].model_id) { + 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)\n", mfr, id, - nand_flash_ids[i].name); + "Chip ID: %2.2X (%s:%s)\n", mfr, id, + nand_manuf_ids[j].name, nand_flash_ids[i].name); if (!doc->mfr) { doc->mfr = mfr; doc->id = id; @@ -391,7 +390,7 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) nand_flash_ids[i].chipshift; doc->page256 = nand_flash_ids[i].page256; doc->pageadrlen = - nand_flash_ids[i].pageadrlen; + nand_flash_ids[i].chipshift > 25 ? 3 : 2; doc->erasesize = nand_flash_ids[i].erasesize; return 1; @@ -558,7 +557,7 @@ static void DoC2k_init(struct mtd_info *mtd) 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; @@ -597,11 +596,11 @@ 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); + return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0); } static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, - size_t * retlen, u_char * buf, u_char * eccbuf) + size_t * retlen, u_char * buf, u_char * eccbuf, int oobsel) { struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; unsigned long docptr; @@ -745,12 +744,12 @@ 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); + return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0); } 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) + u_char * eccbuf, int oobsel) { struct DiskOnChip *this = (struct DiskOnChip *) mtd->priv; int di; /* Yes, DI is a hangover from when I was disassembling the binary driver */ diff --git a/drivers/mtd/devices/doc2001.c b/drivers/mtd/devices/doc2001.c index b34591ad7e50..6cd56efbaf5f 100644 --- a/drivers/mtd/devices/doc2001.c +++ b/drivers/mtd/devices/doc2001.c @@ -4,7 +4,7 @@ * (c) 1999 Machine Vision Holdings, Inc. * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> * - * $Id: doc2001.c,v 1.35 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: doc2001.c,v 1.40 2003/05/20 21:03:07 dwmw2 Exp $ */ #include <linux/kernel.h> @@ -22,7 +22,6 @@ #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> -#include <linux/mtd/nand_ids.h> #include <linux/mtd/doc2000.h> /* #define ECC_DEBUG */ @@ -38,9 +37,9 @@ static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, 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); + 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); + size_t *retlen, const u_char *buf, u_char *eccbuf, int 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, @@ -182,7 +181,7 @@ static int DoC_SelectFloor(unsigned long docptr, int floor) /* 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; + int mfr, id, i, j; volatile char dummy; /* Page in the required floor/chip @@ -216,11 +215,15 @@ static int DoC_IdentChip(struct DiskOnChip *doc, int floor, int chip) /* FIXME: to deal with multi-flash on multi-Millennium case more carefully */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (mfr == nand_flash_ids[i].manufacture_id && - id == nand_flash_ids[i].model_id) { + 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)\n", - mfr, id, nand_flash_ids[i].name); + "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; @@ -363,7 +366,7 @@ static void DoCMil_init(struct mtd_info *mtd) mtd->oobblock = 512; mtd->oobsize = 16; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; mtd->erase = doc_erase; mtd->point = NULL; mtd->unpoint = NULL; @@ -399,11 +402,11 @@ 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); + return doc_read_ecc(mtd, from, len, retlen, buf, NULL, 0); } static int doc_read_ecc (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf, u_char *eccbuf) + size_t *retlen, u_char *buf, u_char *eccbuf, int oobsel) { int i, ret; volatile char dummy; @@ -525,11 +528,11 @@ 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); + return doc_write_ecc(mtd, to, len, retlen, buf, eccbuf, 0); } 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) + size_t *retlen, const u_char *buf, u_char *eccbuf, int oobsel) { int i,ret = 0; volatile char dummy; diff --git a/drivers/mtd/devices/doc2001plus.c b/drivers/mtd/devices/doc2001plus.c new file mode 100644 index 000000000000..dca160a5842e --- /dev/null +++ b/drivers/mtd/devices/doc2001plus.c @@ -0,0 +1,1120 @@ +/* + * Linux driver for Disk-On-Chip Millennium Plus + * + * (c) 2002-2003 Greg Ungerer <gerg@snapgear.com> + * (c) 2002-2003 SnapGear Inc + * (c) 1999 Machine Vision Holdings, Inc. + * (c) 1999, 2000 David Woodhouse <dwmw2@infradead.org> + * + * $Id: doc2001plus.c,v 1.4 2003/05/23 11:28:46 dwmw2 Exp $ + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/types.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/doc2000.h> + +/* #define ECC_DEBUG */ + +/* I have no idea why some DoC chips can not use memcop_form|to_io(). + * This may be due to the different revisions of the ASIC controller built-in or + * simplily a QA/Bug issue. Who knows ?? If you have trouble, please uncomment + * this:*/ +#undef USE_MEMCPY + +static int doc_read(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf); +static int doc_write(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf); +static int doc_read_ecc(struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf, u_char *eccbuf, + struct nand_oobinfo *oobsel); +static int doc_write_ecc(struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf, u_char *eccbuf, + struct nand_oobinfo *oobsel); +static int doc_read_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t *retlen, u_char *buf); +static int doc_write_oob(struct mtd_info *mtd, loff_t ofs, size_t len, + size_t *retlen, const u_char *buf); +static int doc_erase (struct mtd_info *mtd, struct erase_info *instr); + +static struct mtd_info *docmilpluslist = NULL; + + +/* Perform the required delay cycles by writing to the NOP register */ +static void DoC_Delay(unsigned long docptr, int cycles) +{ + int i; + + for (i = 0; (i < cycles); i++) + WriteDOC(0, docptr, Mplus_NOP); +} + +#define CDSN_CTRL_FR_B_MASK (CDSN_CTRL_FR_B0 | CDSN_CTRL_FR_B1) + +/* DOC_WaitReady: Wait for RDY line to be asserted by the flash chip */ +static int _DoC_WaitReady(unsigned long docptr) +{ + unsigned int c = 0xffff; + + DEBUG(MTD_DEBUG_LEVEL3, + "_DoC_WaitReady called for out-of-line wait\n"); + + /* Out-of-line routine to wait for chip response */ + while (((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) && --c) + ; + + if (c == 0) + DEBUG(MTD_DEBUG_LEVEL2, "_DoC_WaitReady timed out.\n"); + + return (c == 0); +} + +static inline int DoC_WaitReady(unsigned long docptr) +{ + /* This is inline, to optimise the common case, where it's ready instantly */ + int ret = 0; + + /* read form NOP register should be issued prior to the read from CDSNControl + see Software Requirement 11.4 item 2. */ + DoC_Delay(docptr, 4); + + if ((ReadDOC(docptr, Mplus_FlashControl) & CDSN_CTRL_FR_B_MASK) != CDSN_CTRL_FR_B_MASK) + /* Call the out-of-line routine to wait */ + ret = _DoC_WaitReady(docptr); + + return ret; +} + +/* For some reason the Millennium Plus seems to occassionally put itself + * into reset mode. For me this happens randomly, with no pattern that I + * can detect. M-systems suggest always check this on any block level + * operation and setting to normal mode if in reset mode. + */ +static inline void DoC_CheckASIC(unsigned long docptr) +{ + /* Make sure the DoC is in normal mode */ + if ((ReadDOC(docptr, Mplus_DOCControl) & DOC_MODE_NORMAL) == 0) { + WriteDOC((DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_DOCControl); + WriteDOC(~(DOC_MODE_NORMAL | DOC_MODE_MDWREN), docptr, Mplus_CtrlConfirm); + } +} + +/* DoC_Command: Send a flash command to the flash chip through the Flash + * command register. Need 2 Write Pipeline Terminates to complete send. + */ +static inline void DoC_Command(unsigned long docptr, unsigned char command, + unsigned char xtraflags) +{ + WriteDOC(command, docptr, Mplus_FlashCmd); + WriteDOC(command, docptr, Mplus_WritePipeTerm); + WriteDOC(command, docptr, Mplus_WritePipeTerm); +} + +/* DoC_Address: Set the current address for the flash chip through the Flash + * Address register. Need 2 Write Pipeline Terminates to complete send. + */ +static inline void DoC_Address(struct DiskOnChip *doc, int numbytes, + unsigned long ofs, unsigned char xtraflags1, + unsigned char xtraflags2) +{ + unsigned long docptr = doc->virtadr; + + /* Allow for possible Mill Plus internal flash interleaving */ + ofs >>= doc->interleave; + + switch (numbytes) { + case 1: + /* Send single byte, bits 0-7. */ + WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); + break; + case 2: + /* Send bits 9-16 followed by 17-23 */ + WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); + WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); + break; + case 3: + /* Send 0-7, 9-16, then 17-23 */ + WriteDOC(ofs & 0xff, docptr, Mplus_FlashAddress); + WriteDOC((ofs >> 9) & 0xff, docptr, Mplus_FlashAddress); + WriteDOC((ofs >> 17) & 0xff, docptr, Mplus_FlashAddress); + break; + default: + return; + } + + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); +} + +/* DoC_SelectChip: Select a given flash chip within the current floor */ +static int DoC_SelectChip(unsigned long docptr, int chip) +{ + /* No choice for flash chip on Millennium Plus */ + return 0; +} + +/* DoC_SelectFloor: Select a given floor (bank of flash chips) */ +static int DoC_SelectFloor(unsigned long docptr, int floor) +{ + WriteDOC((floor & 0x3), docptr, Mplus_DeviceSelect); + return 0; +} + +/* + * Translate the given offset into the appropriate command and offset. + * This does the mapping using the 16bit interleave layout defined by + * M-Systems, and looks like this for a sector pair: + * +-----------+-------+-------+-------+--------------+---------+-----------+ + * | 0 --- 511 |512-517|518-519|520-521| 522 --- 1033 |1034-1039|1040 - 1055| + * +-----------+-------+-------+-------+--------------+---------+-----------+ + * | Data 0 | ECC 0 |Flags0 |Flags1 | Data 1 |ECC 1 | OOB 1 + 2 | + * +-----------+-------+-------+-------+--------------+---------+-----------+ + */ +static unsigned int DoC_GetDataOffset(struct mtd_info *mtd, loff_t *from) +{ + 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; +} + +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); + dummy = ReadDOC(docptr, Mil_CDSN_IO); /* 2 way interleave */ + + id = ReadDOC(docptr, Mil_CDSN_IO); + 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->interleave = 0; + if (doc->ChipID == DOC_ChipID_DocMilPlus32) + doc->interleave = 1; + 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; + + /* For each floor, find the number of valid chips it contains */ + for (floor = 0,ret = 1; floor < MAX_FLOORS_MPLUS; floor++) { + numchips[floor] = 0; + for (chip = 0; chip < MAX_CHIPS_MPLUS && ret != 0; chip++) { + ret = DoC_IdentChip(this, floor, chip); + if (ret) { + numchips[floor]++; + this->numchips++; + } + } + } + /* If there are none at all that we recognise, bail */ + if (!this->numchips) { + printk("No flash chips recognised.\n"); + return; + } + + /* Allocate an array to hold the information for each chip */ + this->chips = kmalloc(sizeof(struct Nand) * this->numchips, GFP_KERNEL); + if (!this->chips){ + printk("MTD: No memory for allocating chip info structures\n"); + return; + } + + /* Fill out the chip array with {floor, chipno} for each + * detected chip in the device. */ + for (floor = 0, ret = 0; floor < MAX_FLOORS_MPLUS; floor++) { + for (chip = 0 ; chip < numchips[floor] ; chip++) { + this->chips[ret].floor = floor; + this->chips[ret].chip = chip; + this->chips[ret].curadr = 0; + this->chips[ret].curmode = 0x50; + ret++; + } + } + + /* Calculate and print the total size of the device */ + this->totlen = this->numchips * (1 << this->chipshift); + printk(KERN_INFO "%d flash chips found. Total DiskOnChip size: %ld MiB\n", + this->numchips ,this->totlen >> 20); +} + +static int DoCMilPlus_is_alias(struct DiskOnChip *doc1, struct DiskOnChip *doc2) +{ + int tmp1, tmp2, retval; + + if (doc1->physadr == doc2->physadr) + return 1; + + /* Use the alias resolution register which was set aside for this + * purpose. If it's value is the same on both chips, they might + * be the same chip, and we write to one and check for a change in + * the other. It's unclear if this register is usuable in the + * DoC 2000 (it's in the Millennium docs), but it seems to work. */ + tmp1 = ReadDOC(doc1->virtadr, Mplus_AliasResolution); + tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); + if (tmp1 != tmp2) + return 0; + + WriteDOC((tmp1+1) % 0xff, doc1->virtadr, Mplus_AliasResolution); + tmp2 = ReadDOC(doc2->virtadr, Mplus_AliasResolution); + if (tmp2 == (tmp1+1) % 0xff) + retval = 1; + else + retval = 0; + + /* Restore register contents. May not be necessary, but do it just to + * be safe. */ + WriteDOC(tmp1, doc1->virtadr, Mplus_AliasResolution); + + return retval; +} + +static const char im_name[] = "DoCMilPlus_init"; + +/* This routine is made available to other mtd code via + * inter_module_register. It must only be accessed through + * inter_module_get which will bump the use count of this module. The + * addresses passed back in mtd are valid as long as the use count of + * this module is non-zero, i.e. between inter_module_get and + * inter_module_put. Keith Owens <kaos@ocs.com.au> 29 Oct 2000. + */ +static void DoCMilPlus_init(struct mtd_info *mtd) +{ + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + struct DiskOnChip *old = NULL; + + /* We must avoid being called twice for the same device. */ + if (docmilpluslist) + old = (struct DiskOnChip *)docmilpluslist->priv; + + while (old) { + if (DoCMilPlus_is_alias(this, old)) { + printk(KERN_NOTICE "Ignoring DiskOnChip Millennium " + "Plus at 0x%lX - already configured\n", + this->physadr); + iounmap((void *)this->virtadr); + kfree(mtd); + return; + } + if (old->nextdoc) + old = (struct DiskOnChip *)old->nextdoc->priv; + else + old = NULL; + } + + mtd->name = "DiskOnChip Millennium Plus"; + printk(KERN_NOTICE "DiskOnChip Millennium Plus found at " + "address 0x%lX\n", this->physadr); + + mtd->type = MTD_NANDFLASH; + mtd->flags = MTD_CAP_NANDFLASH; + mtd->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 = 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 (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 (base < 6) { + WriteDOC(DoC_GetECCOffset(mtd, &fofs), docptr, Mplus_FlashCmd); + size = 6 - base; + } else if (base < 8) { + WriteDOC(DoC_GetFlagsOffset(mtd, &fofs), docptr, Mplus_FlashCmd); + size = 8 - base; + } else { + WriteDOC(DoC_GetHdrOffset(mtd, &fofs), docptr, Mplus_FlashCmd); + size = 16 - base; + } + if (size > want) + size = want; + + /* Issue the Serial Data In command to initial the Page Program process */ + DoC_Command(docptr, NAND_CMD_SEQIN, 0x00); + DoC_Address(this, 3, fofs, 0, 0x00); + + /* Disable the ECC engine */ + WriteDOC(DOC_ECC_RESET, docptr, Mplus_ECCConf); + + /* Write the data via the internal pipeline through CDSN IO + register, see Pipelined Write Operations 11.2 */ + MemWriteDOC(docptr, (unsigned char *) &buf[got], size); + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + WriteDOC(0x00, docptr, Mplus_WritePipeTerm); + + /* Commit the Page Program command and wait for ready + see Software Requirement 11.4 item 1.*/ + DoC_Command(docptr, NAND_CMD_PAGEPROG, 0x00); + DoC_WaitReady(docptr); + + /* Read the status of the flash device through CDSN IO register + see Software Requirement 11.4 item 5.*/ + DoC_Command(docptr, NAND_CMD_STATUS, 0x00); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + DoC_Delay(docptr, 2); + if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { + printk("MTD: Error 0x%x programming oob at 0x%x\n", + dummy, (int)ofs); + /* FIXME: implement Bad Block Replacement */ + *retlen = 0; + ret = -EIO; + } + dummy = ReadDOC(docptr, Mplus_LastDataRead); + + ofs += size; + got += size; + want -= size; + } + + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + + *retlen = len; + return ret; +} + +int doc_erase(struct mtd_info *mtd, struct erase_info *instr) +{ + volatile char dummy; + struct DiskOnChip *this = (struct DiskOnChip *)mtd->priv; + __u32 ofs = instr->addr; + __u32 len = instr->len; + unsigned long docptr = this->virtadr; + struct Nand *mychip = &this->chips[ofs >> this->chipshift]; + + DoC_CheckASIC(docptr); + + if (len != mtd->erasesize) + printk(KERN_WARNING "MTD: Erase not right size (%x != %x)n", + len, mtd->erasesize); + + /* Find the chip which is to be used and select it */ + if (this->curfloor != mychip->floor) { + DoC_SelectFloor(docptr, mychip->floor); + DoC_SelectChip(docptr, mychip->chip); + } else if (this->curchip != mychip->chip) { + DoC_SelectChip(docptr, mychip->chip); + } + this->curfloor = mychip->floor; + this->curchip = mychip->chip; + + instr->state = MTD_ERASE_PENDING; + + /* Millennium Plus bus cycle sequence as per figure 2, section 2.4 */ + WriteDOC(DOC_FLASH_CE, docptr, Mplus_FlashSelect); + + DoC_Command(docptr, NAND_CMD_RESET, 0x00); + DoC_WaitReady(docptr); + + DoC_Command(docptr, NAND_CMD_ERASE1, 0); + DoC_Address(this, 2, ofs, 0, 0x00); + DoC_Command(docptr, NAND_CMD_ERASE2, 0); + DoC_WaitReady(docptr); + instr->state = MTD_ERASING; + + /* Read the status of the flash device through CDSN IO register + see Software Requirement 11.4 item 5. */ + DoC_Command(docptr, NAND_CMD_STATUS, 0); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + dummy = ReadDOC(docptr, Mplus_ReadPipeInit); + if ((dummy = ReadDOC(docptr, Mplus_LastDataRead)) & 1) { + printk("MTD: Error 0x%x erasing at 0x%x\n", dummy, ofs); + /* FIXME: implement Bad Block Replacement (in nftl.c ??) */ + instr->state = MTD_ERASE_FAILED; + } else { + instr->state = MTD_ERASE_DONE; + } + dummy = ReadDOC(docptr, Mplus_LastDataRead); + + /* Disable flash internally */ + WriteDOC(0, docptr, Mplus_FlashSelect); + + if (instr->callback) + instr->callback(instr); + + return 0; +} + +/**************************************************************************** + * + * Module stuff + * + ****************************************************************************/ + +int __init init_doc2001plus(void) +{ + inter_module_register(im_name, THIS_MODULE, &DoCMilPlus_init); + return 0; +} + +static void __exit cleanup_doc2001plus(void) +{ + struct mtd_info *mtd; + struct DiskOnChip *this; + + while ((mtd=docmilpluslist)) { + this = (struct DiskOnChip *)mtd->priv; + docmilpluslist = this->nextdoc; + + del_mtd_device(mtd); + + iounmap((void *)this->virtadr); + kfree(this->chips); + kfree(mtd); + } + inter_module_unregister(im_name); +} + +module_exit(cleanup_doc2001plus); +module_init(init_doc2001plus); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com> et al."); +MODULE_DESCRIPTION("Driver for DiskOnChip Millennium Plus"); diff --git a/drivers/mtd/devices/docecc.c b/drivers/mtd/devices/docecc.c index 67af87dbedee..933877ff4d88 100644 --- a/drivers/mtd/devices/docecc.c +++ b/drivers/mtd/devices/docecc.c @@ -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 @@ int doc_decode_ecc(unsigned char sector[SECTOR_SIZE], unsigned char ecc1[6]) return nb_errors; } +EXPORT_SYMBOL_GPL(doc_decode_ecc); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Fabrice Bellard <fabrice.bellard@netgem.com>"); MODULE_DESCRIPTION("ECC code for correcting errors detected by DiskOnChip 2000 and Millennium ECC hardware"); diff --git a/drivers/mtd/devices/docprobe.c b/drivers/mtd/devices/docprobe.c index 43ccd77ffad2..f7a91fa86995 100644 --- a/drivers/mtd/devices/docprobe.c +++ b/drivers/mtd/devices/docprobe.c @@ -1,9 +1,10 @@ /* Linux driver for Disk-On-Chip devices */ /* Probe routines common to all DoC devices */ -/* (c) 1999 Machine Vision Holdings, Inc. */ -/* Author: David Woodhouse <dwmw2@infradead.org> */ -/* $Id: docprobe.c,v 1.30 2001/10/02 15:05:13 dwmw2 Exp $ */ +/* (C) 1999 Machine Vision Holdings, Inc. */ +/* (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */ + +/* $Id: docprobe.c,v 1.36 2003/05/23 11:29:34 dwmw2 Exp $ */ @@ -30,14 +31,12 @@ /* DOC_SINGLE_DRIVER: Millennium driver has been merged into DOC2000 driver. - The newly-merged driver doesn't appear to work for writing. It's the - same with the DiskOnChip 2000 and the Millennium. If you have a - Millennium and you want write support to work, remove the definition - of DOC_SINGLE_DRIVER below to use the old doc2001-specific driver. - - Otherwise, it's left on in the hope that it'll annoy someone with - a Millennium enough that they go through and work out what the - difference is :) + The old Millennium-only driver has been retained just in case there + are problems with the new code. If the combined driver doesn't work + for you, you can try the old one by undefining DOC_SINGLE_DRIVER + below and also enabling it in your configuration. If this fixes the + problems, please send a report to the MTD mailing list at + <linux-mtd@lists.infradead.org>. */ #define DOC_SINGLE_DRIVER @@ -46,18 +45,15 @@ #include <linux/module.h> #include <asm/errno.h> #include <asm/io.h> -#include <asm/uaccess.h> -#include <linux/miscdevice.h> -#include <linux/pci.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/sched.h> #include <linux/init.h> #include <linux/types.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> #include <linux/mtd/doc2000.h> +#include <linux/mtd/compatmac.h> /* Where to look for the devices? */ #ifndef CONFIG_MTD_DOCPROBE_ADDRESS @@ -67,7 +63,7 @@ static unsigned long doc_config_location = CONFIG_MTD_DOCPROBE_ADDRESS; MODULE_PARM(doc_config_location, "l"); - +MODULE_PARM_DESC(doc_config_location, "Physical memory address at which to probe for DiskOnChip"); static unsigned long __initdata doc_locations[] = { #if defined (__alpha__) || defined(__i386__) || defined(__x86_64__) @@ -84,21 +80,24 @@ static unsigned long __initdata doc_locations[] = { 0xe0000, 0xe2000, 0xe4000, 0xe6000, 0xe8000, 0xea000, 0xec000, 0xee000, #endif /* CONFIG_MTD_DOCPROBE_HIGH */ -#elif defined(__ppc__) +#elif defined(__PPC__) 0xe4000000, #elif defined(CONFIG_MOMENCO_OCELOT) 0x2f000000, -#else + 0xff000000, +#elif defined(CONFIG_MOMENCO_OCELOT_G) || defined (CONFIG_MOMENCO_OCELOT_C) + 0xff000000, +##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 @@ -141,19 +140,65 @@ static inline int __init doccheck(unsigned long potential, unsigned long physadr 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) + tmp = ReadDOC(window, 2k_ECCStatus) & DOC_TOGGLE_BIT; + 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 the TOGGLE bit in the ECC register */ - tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; - if ((ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT) != tmp) + tmp = ReadDOC(window, ECCConf) & DOC_TOGGLE_BIT; + 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; + break; + 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", ChipID, physadr); @@ -233,6 +278,13 @@ static void __init DoC_Probe(unsigned long physadr) 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) @@ -244,6 +296,7 @@ static void __init DoC_Probe(unsigned long physadr) return; } printk(KERN_NOTICE "Cannot find driver for DiskOnChip %s at 0x%lX\n", name, physadr); + kfree(mtd); } iounmap((void *)docptr); } @@ -263,7 +316,7 @@ int __init init_doc(void) 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]); } } @@ -271,11 +324,7 @@ int __init init_doc(void) 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 --git a/drivers/mtd/devices/lart.c b/drivers/mtd/devices/lart.c index e049ace35922..e0b2763f948f 100644 --- a/drivers/mtd/devices/lart.c +++ b/drivers/mtd/devices/lart.c @@ -2,7 +2,7 @@ /* * MTD driver for the 28F160F3 Flash Memory (non-CFI) on LART. * - * $Id: lart.c,v 1.2 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: lart.c,v 1.5 2003/05/20 21:03:07 dwmw2 Exp $ * * Author: Abraham vd Merwe <abraham@2d3d.co.za> * @@ -584,46 +584,41 @@ static int flash_write (struct mtd_info *mtd,loff_t to,size_t len,size_t *retlen static struct mtd_info mtd; -static struct mtd_erase_region_info erase_regions[] = -{ - /* parameter blocks */ - { - 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 - } +static struct mtd_erase_region_info erase_regions[] = { + /* parameter blocks */ + { + .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, + } }; #ifdef HAVE_PARTITIONS -static struct mtd_partition lart_partitions[] = -{ - /* blob */ - { - name: "blob", - offset: BLOB_START, - size: BLOB_LEN, - mask_flags: 0 - }, - /* kernel */ - { - name: "kernel", - offset: KERNEL_START, /* MTDPART_OFS_APPEND */ - size: KERNEL_LEN, - mask_flags: 0 - }, - /* initial ramdisk / file system */ - { - name: "file system", - offset: INITRD_START, /* MTDPART_OFS_APPEND */ - size: INITRD_LEN, /* MTDPART_SIZ_FULL */ - mask_flags: 0 - } +static struct mtd_partition lart_partitions[] = { + /* blob */ + { + .name = "blob", + .offset = BLOB_START, + .size = BLOB_LEN, + }, + /* kernel */ + { + .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 */ + } }; #endif @@ -646,10 +641,10 @@ int __init lart_flash_init (void) 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 --git a/drivers/mtd/devices/ms02-nv.c b/drivers/mtd/devices/ms02-nv.c new file mode 100644 index 000000000000..e7f49a5ca5d3 --- /dev/null +++ b/drivers/mtd/devices/ms02-nv.c @@ -0,0 +1,326 @@ +/* + * Copyright (c) 2001 Maciej W. Rozycki + * + * 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. + * + * $Id: ms02-nv.c,v 1.4 2003/05/20 21:03:07 dwmw2 Exp $ + */ + +#include <linux/init.h> +#include <linux/ioport.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/slab.h> +#include <linux/types.h> + +#include <asm/addrspace.h> +#include <asm/bootinfo.h> +#include <asm/dec/ioasic_addrs.h> +#include <asm/dec/kn02.h> +#include <asm/dec/kn03.h> +#include <asm/io.h> +#include <asm/paccess.h> + +#include "ms02-nv.h" + + +static char version[] __initdata = + "ms02-nv.c: v.1.0.0 13 Aug 2001 Maciej W. Rozycki.\n"; + +MODULE_AUTHOR("Maciej W. Rozycki <macro@ds2.pg.gda.pl>"); +MODULE_DESCRIPTION("DEC MS02-NV NVRAM module driver"); +MODULE_LICENSE("GPL"); + + +/* + * Addresses we probe for an MS02-NV at. Modules may be located + * at any 8MB boundary within a 0MB up to 112MB range or at any 32MB + * boundary within a 0MB up to 448MB range. We don't support a module + * at 0MB, though. + */ +static ulong ms02nv_addrs[] __initdata = { + 0x07000000, 0x06800000, 0x06000000, 0x05800000, 0x05000000, + 0x04800000, 0x04000000, 0x03800000, 0x03000000, 0x02800000, + 0x02000000, 0x01800000, 0x01000000, 0x00800000 +}; + +static const char ms02nv_name[] = "DEC MS02-NV NVRAM"; +static const char ms02nv_res_diag_ram[] = "Diagnostic RAM"; +static const char ms02nv_res_user_ram[] = "General-purpose RAM"; +static const char ms02nv_res_csr[] = "Control and status register"; + +static struct mtd_info *root_ms02nv_mtd; + + +static int ms02nv_read(struct mtd_info *mtd, loff_t from, + size_t len, size_t *retlen, u_char *buf) +{ + struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + + if (from + len > mtd->size) + return -EINVAL; + + memcpy(buf, mp->uaddr + from, len); + *retlen = len; + + return 0; +} + +static int ms02nv_write(struct mtd_info *mtd, loff_t to, + size_t len, size_t *retlen, const u_char *buf) +{ + struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + + if (to + len > mtd->size) + return -EINVAL; + + memcpy(mp->uaddr + to, buf, len); + *retlen = len; + + return 0; +} + + +static inline uint ms02nv_probe_one(ulong addr) +{ + ms02nv_uint *ms02nv_diagp; + ms02nv_uint *ms02nv_magicp; + uint ms02nv_diag; + uint ms02nv_magic; + size_t size; + + int err; + + /* + * The firmware writes MS02NV_ID at MS02NV_MAGIC and also + * a diagnostic status at MS02NV_DIAG. + */ + ms02nv_diagp = (ms02nv_uint *)(KSEG1ADDR(addr + MS02NV_DIAG)); + ms02nv_magicp = (ms02nv_uint *)(KSEG1ADDR(addr + MS02NV_MAGIC)); + err = get_dbe(ms02nv_magic, ms02nv_magicp); + if (err) + return 0; + if (ms02nv_magic != MS02NV_ID) + return 0; + + ms02nv_diag = *ms02nv_diagp; + size = (ms02nv_diag & MS02NV_DIAG_SIZE_MASK) << MS02NV_DIAG_SIZE_SHIFT; + if (size > MS02NV_CSR) + size = MS02NV_CSR; + + return size; +} + +static int __init ms02nv_init_one(ulong addr) +{ + struct mtd_info *mtd; + struct ms02nv_private *mp; + struct resource *mod_res; + struct resource *diag_res; + struct resource *user_res; + struct resource *csr_res; + ulong fixaddr; + size_t size, fixsize; + + static int version_printed; + + int ret = -ENODEV; + + /* The module decodes 8MB of address space. */ + mod_res = kmalloc(sizeof(*mod_res), GFP_KERNEL); + if (!mod_res) + return -ENOMEM; + + memset(mod_res, 0, sizeof(*mod_res)); + mod_res->name = ms02nv_name; + mod_res->start = addr; + mod_res->end = addr + MS02NV_SLOT_SIZE - 1; + mod_res->flags = IORESOURCE_MEM | IORESOURCE_BUSY; + if (request_resource(&iomem_resource, mod_res) < 0) + goto err_out_mod_res; + + size = ms02nv_probe_one(addr); + if (!size) + goto err_out_mod_res_rel; + + if (!version_printed) { + printk(KERN_INFO "%s", version); + version_printed = 1; + } + + ret = -ENOMEM; + mtd = kmalloc(sizeof(*mtd), GFP_KERNEL); + if (!mtd) + goto err_out_mod_res_rel; + memset(mtd, 0, sizeof(*mtd)); + mp = kmalloc(sizeof(*mp), GFP_KERNEL); + if (!mp) + goto err_out_mtd; + memset(mp, 0, sizeof(*mp)); + + mtd->priv = mp; + mp->resource.module = mod_res; + + /* Firmware's diagnostic NVRAM area. */ + diag_res = kmalloc(sizeof(*diag_res), GFP_KERNEL); + if (!diag_res) + goto err_out_mp; + + memset(diag_res, 0, sizeof(*diag_res)); + diag_res->name = ms02nv_res_diag_ram; + diag_res->start = addr; + diag_res->end = addr + MS02NV_RAM - 1; + diag_res->flags = IORESOURCE_BUSY; + request_resource(mod_res, diag_res); + + mp->resource.diag_ram = diag_res; + + /* User-available general-purpose NVRAM area. */ + user_res = kmalloc(sizeof(*user_res), GFP_KERNEL); + if (!user_res) + goto err_out_diag_res; + + memset(user_res, 0, sizeof(*user_res)); + user_res->name = ms02nv_res_user_ram; + user_res->start = addr + MS02NV_RAM; + user_res->end = addr + size - 1; + user_res->flags = IORESOURCE_BUSY; + request_resource(mod_res, user_res); + + mp->resource.user_ram = user_res; + + /* Control and status register. */ + csr_res = kmalloc(sizeof(*csr_res), GFP_KERNEL); + if (!csr_res) + goto err_out_user_res; + + memset(csr_res, 0, sizeof(*csr_res)); + csr_res->name = ms02nv_res_csr; + csr_res->start = addr + MS02NV_CSR; + csr_res->end = addr + MS02NV_CSR + 3; + csr_res->flags = IORESOURCE_BUSY; + request_resource(mod_res, csr_res); + + mp->resource.csr = csr_res; + + mp->addr = phys_to_virt(addr); + mp->size = size; + + /* + * Hide the firmware's diagnostic area. It may get destroyed + * upon a reboot. Take paging into account for mapping support. + */ + fixaddr = (addr + MS02NV_RAM + PAGE_SIZE - 1) & ~(PAGE_SIZE - 1); + fixsize = (size - (fixaddr - addr)) & ~(PAGE_SIZE - 1); + mp->uaddr = phys_to_virt(fixaddr); + + mtd->type = MTD_RAM; + mtd->flags = MTD_CAP_RAM | MTD_XIP; + mtd->size = fixsize; + mtd->name = (char *)ms02nv_name; + mtd->owner = THIS_MODULE; + mtd->read = ms02nv_read; + mtd->write = ms02nv_write; + + ret = -EIO; + if (add_mtd_device(mtd)) { + printk(KERN_ERR + "ms02-nv: Unable to register MTD device, aborting!\n"); + goto err_out_csr_res; + } + + printk(KERN_INFO "mtd%d: %s at 0x%08lx, size %uMB.\n", + mtd->index, ms02nv_name, addr, size >> 20); + + mp->next = root_ms02nv_mtd; + root_ms02nv_mtd = mtd; + + return 0; + + +err_out_csr_res: + release_resource(csr_res); + kfree(csr_res); +err_out_user_res: + release_resource(user_res); + kfree(user_res); +err_out_diag_res: + release_resource(diag_res); + kfree(diag_res); +err_out_mp: + kfree(mp); +err_out_mtd: + kfree(mtd); +err_out_mod_res_rel: + release_resource(mod_res); +err_out_mod_res: + kfree(mod_res); + return ret; +} + +static void __exit ms02nv_remove_one(void) +{ + struct mtd_info *mtd = root_ms02nv_mtd; + struct ms02nv_private *mp = (struct ms02nv_private *)mtd->priv; + + root_ms02nv_mtd = mp->next; + + del_mtd_device(mtd); + + release_resource(mp->resource.csr); + kfree(mp->resource.csr); + release_resource(mp->resource.user_ram); + kfree(mp->resource.user_ram); + release_resource(mp->resource.diag_ram); + kfree(mp->resource.diag_ram); + release_resource(mp->resource.module); + kfree(mp->resource.module); + kfree(mp); + kfree(mtd); +} + + +static int __init ms02nv_init(void) +{ + volatile u32 *csr; + uint stride = 0; + int count = 0; + int i; + + switch (mips_machtype) { + case MACH_DS5000_200: + csr = (volatile u32 *)KN02_CSR_ADDR; + if (*csr & KN02_CSR_BNK32M) + stride = 2; + break; + case MACH_DS5000_2X0: + case MACH_DS5000: + csr = (volatile u32 *)KN03_MCR_BASE; + if (*csr & KN03_MCR_BNK32M) + stride = 2; + break; + default: + return -ENODEV; + break; + } + + for (i = 0; i < (sizeof(ms02nv_addrs) / sizeof(*ms02nv_addrs)); i++) + if (!ms02nv_init_one(ms02nv_addrs[i] << stride)) + count++; + + return (count > 0) ? 0 : -ENODEV; +} + +static void __exit ms02nv_cleanup(void) +{ + while (root_ms02nv_mtd) + ms02nv_remove_one(); +} + + +module_init(ms02nv_init); +module_exit(ms02nv_cleanup); diff --git a/drivers/mtd/devices/ms02-nv.h b/drivers/mtd/devices/ms02-nv.h new file mode 100644 index 000000000000..fb1ec1056409 --- /dev/null +++ b/drivers/mtd/devices/ms02-nv.h @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2001 Maciej W. Rozycki + * + * 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. + * + * $Id: ms02-nv.h,v 1.1 2002/09/13 13:46:55 dwmw2 Exp $ + */ + +#include <linux/ioport.h> +#include <linux/mtd/mtd.h> + +/* MS02-NV iomem register offsets. */ +#define MS02NV_CSR 0x400000 /* control & status register */ + +/* MS02-NV memory offsets. */ +#define MS02NV_DIAG 0x0003f8 /* diagnostic status */ +#define MS02NV_MAGIC 0x0003fc /* MS02-NV magic ID */ +#define MS02NV_RAM 0x000400 /* general-purpose RAM start */ + +/* MS02-NV diagnostic status constants. */ +#define MS02NV_DIAG_SIZE_MASK 0xf0 /* RAM size mask */ +#define MS02NV_DIAG_SIZE_SHIFT 0x10 /* RAM size shift (left) */ + +/* MS02-NV general constants. */ +#define MS02NV_ID 0x03021966 /* MS02-NV magic ID value */ +#define MS02NV_SLOT_SIZE 0x800000 /* size of the address space + decoded by the module */ + +typedef volatile u32 ms02nv_uint; + +struct ms02nv_private { + struct mtd_info *next; + struct { + struct resource *module; + struct resource *diag_ram; + struct resource *user_ram; + struct resource *csr; + } resource; + u_char *addr; + size_t size; + u_char *uaddr; +}; diff --git a/drivers/mtd/devices/mtdram.c b/drivers/mtd/devices/mtdram.c index e407f29891bf..64397ddf82c8 100644 --- a/drivers/mtd/devices/mtdram.c +++ b/drivers/mtd/devices/mtdram.c @@ -1,6 +1,6 @@ /* * mtdram - a test mtd device - * $Id: mtdram.c,v 1.25 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: mtdram.c,v 1.32 2003/05/21 15:15:07 dwmw2 Exp $ * Author: Alexander Larsson <alex@cendio.se> * * Copyright (c) 1999 Alexander Larsson <alex@cendio.se> @@ -13,6 +13,8 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/ioport.h> +#include <linux/vmalloc.h> +#include <linux/init.h> #include <linux/mtd/compatmac.h> #include <linux/mtd/mtd.h> @@ -28,7 +30,9 @@ static unsigned long total_size = CONFIG_MTDRAM_TOTAL_SIZE; static unsigned long erase_size = CONFIG_MTDRAM_ERASE_SIZE; MODULE_PARM(total_size,"l"); +MODULE_PARM_DESC(total_size, "Total device size in KiB"); MODULE_PARM(erase_size,"l"); +MODULE_PARM_DESC(erase_size, "Device erase block size in KiB"); #define MTDRAM_TOTAL_SIZE (total_size * 1024) #define MTDRAM_ERASE_SIZE (erase_size * 1024) #else @@ -69,7 +73,8 @@ static int ram_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *ret return 0; } -static void ram_unpoint (struct mtd_info *mtd, u_char *addr) +static void ram_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, + size_t len) { DEBUG(MTD_DEBUG_LEVEL2, "ram_unpoint\n"); } @@ -108,65 +113,119 @@ static void __exit cleanup_mtdram(void) { if (mtd_info) { del_mtd_device(mtd_info); +#if CONFIG_MTDRAM_TOTAL_SIZE > 0 if (mtd_info->priv) #if CONFIG_MTDRAM_ABS_POS > 0 iounmap(mtd_info->priv); #else vfree(mtd_info->priv); #endif +#endif kfree(mtd_info); } } +int mtdram_init_device(struct mtd_info *mtd, void *mapped_address, + unsigned long size, char *name) +{ + memset(mtd, 0, sizeof(*mtd)); + + /* Setup the MTD structure */ + mtd->name = name; + mtd->type = MTD_RAM; + mtd->flags = MTD_CAP_RAM; + mtd->size = size; + mtd->erasesize = MTDRAM_ERASE_SIZE; + mtd->priv = mapped_address; + + mtd->owner = THIS_MODULE; + mtd->erase = ram_erase; + mtd->point = ram_point; + mtd->unpoint = ram_unpoint; + mtd->read = ram_read; + mtd->write = ram_write; + + if (add_mtd_device(mtd)) { + return -EIO; + } + + return 0; +} + +#if CONFIG_MTDRAM_TOTAL_SIZE > 0 +#if CONFIG_MTDRAM_ABS_POS > 0 int __init init_mtdram(void) { - // Allocate some memory + void *addr; + int err; + /* Allocate some memory */ mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); if (!mtd_info) - return 0; + return -ENOMEM; - memset(mtd_info, 0, sizeof(*mtd_info)); - - // Setup the MTD structure - mtd_info->name = "mtdram test device"; - mtd_info->type = MTD_RAM; - mtd_info->flags = MTD_CAP_RAM; - mtd_info->size = MTDRAM_TOTAL_SIZE; - mtd_info->erasesize = MTDRAM_ERASE_SIZE; -#if CONFIG_MTDRAM_ABS_POS > 0 - mtd_info->priv = ioremap(CONFIG_MTDRAM_ABS_POS, MTDRAM_TOTAL_SIZE); -#else - mtd_info->priv = vmalloc(MTDRAM_TOTAL_SIZE); -#endif + addr = ioremap(CONFIG_MTDRAM_ABS_POS, MTDRAM_TOTAL_SIZE); + if (!addr) { + DEBUG(MTD_DEBUG_LEVEL1, + "Failed to ioremap) memory region of size %ld at ABS_POS:%ld\n", + (long)MTDRAM_TOTAL_SIZE, (long)CONFIG_MTDRAM_ABS_POS); + kfree(mtd_info); + mtd_info = NULL; + return -ENOMEM; + } + err = mtdram_init_device(mtd_info, addr, + MTDRAM_TOTAL_SIZE, "mtdram test device"); + if (err) + { + iounmap(addr); + kfree(mtd_info); + mtd_info = NULL; + return err; + } + memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); + return err; +} - if (!mtd_info->priv) { - DEBUG(MTD_DEBUG_LEVEL1, "Failed to vmalloc(/ioremap) memory region of size %ld (ABS_POS:%ld)\n", (long)MTDRAM_TOTAL_SIZE, (long)CONFIG_MTDRAM_ABS_POS); - kfree(mtd_info); - mtd_info = NULL; +#else /* CONFIG_MTDRAM_ABS_POS > 0 */ + +int __init init_mtdram(void) +{ + void *addr; + int err; + /* Allocate some memory */ + mtd_info = (struct mtd_info *)kmalloc(sizeof(struct mtd_info), GFP_KERNEL); + if (!mtd_info) return -ENOMEM; - } - memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); - mtd_info->module = THIS_MODULE; - mtd_info->erase = ram_erase; - mtd_info->point = ram_point; - mtd_info->unpoint = ram_unpoint; - mtd_info->read = ram_read; - mtd_info->write = ram_write; + addr = vmalloc(MTDRAM_TOTAL_SIZE); + if (!addr) { + DEBUG(MTD_DEBUG_LEVEL1, + "Failed to vmalloc memory region of size %ld\n", + (long)MTDRAM_TOTAL_SIZE); + kfree(mtd_info); + mtd_info = NULL; + return -ENOMEM; + } + err = mtdram_init_device(mtd_info, addr, + MTDRAM_TOTAL_SIZE, "mtdram test device"); + if (err) + { + vfree(addr); + kfree(mtd_info); + mtd_info = NULL; + return err; + } + memset(mtd_info->priv, 0xff, MTDRAM_TOTAL_SIZE); + return err; +} +#endif /* !(CONFIG_MTDRAM_ABS_POS > 0) */ - if (add_mtd_device(mtd_info)) { -#if CONFIG_MTDRAM_ABS_POS > 0 - iounmap(mtd_info->priv); -#else - vfree(mtd_info->priv); -#endif - kfree(mtd_info); - mtd_info = NULL; - return -EIO; - } - - return 0; +#else /* CONFIG_MTDRAM_TOTAL_SIZE > 0 */ + +int __init init_mtdram(void) +{ + return 0; } +#endif /* !(CONFIG_MTDRAM_TOTAL_SIZE > 0) */ module_init(init_mtdram); module_exit(cleanup_mtdram); diff --git a/drivers/mtd/devices/pmc551.c b/drivers/mtd/devices/pmc551.c index d063d46d6a94..f18da3e7d8f0 100644 --- a/drivers/mtd/devices/pmc551.c +++ b/drivers/mtd/devices/pmc551.c @@ -1,5 +1,5 @@ /* - * $Id: pmc551.c,v 1.19 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: pmc551.c,v 1.24 2003/05/20 21:03:08 dwmw2 Exp $ * * PMC551 PCI Mezzanine Ram Device * @@ -30,7 +30,7 @@ * * Notes: * Due to what I assume is more buggy SROM, the 64M PMC551 I - * have available claims that all 4 of its DRAM banks have 64M + * have available claims that all 4 of it's DRAM banks have 64M * of ram configured (making a grand total of 256M onboard). * This is slightly annoying since the BAR0 size reflects the * aperture size, not the dram size, and the V370PDC supplies no @@ -98,7 +98,6 @@ #include <linux/ioctl.h> #include <asm/io.h> #include <asm/system.h> -#include <stdarg.h> #include <linux/pci.h> #ifndef CONFIG_PCI @@ -215,7 +214,7 @@ static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t * } -static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr) +static void pmc551_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) { #ifdef CONFIG_MTD_PMC551_DEBUG printk(KERN_DEBUG "pmc551_unpoint()\n"); @@ -788,10 +787,10 @@ int __init init_pmc551(void) 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 --git a/drivers/mtd/devices/slram.c b/drivers/mtd/devices/slram.c index bcb3d183d634..4dbcfcfd68fc 100644 --- a/drivers/mtd/devices/slram.c +++ b/drivers/mtd/devices/slram.c @@ -1,6 +1,32 @@ /*====================================================================== - $Id: slram.c,v 1.25 2001/10/02 15:05:13 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 + use slram at least mtdblock or mtdchar is required (for block or + character device access). + + Usage: + + if compiled as loadable module: + modprobe slram map=<name>,<start>,<end/offset> + if statically linked into the kernel use the following kernel cmd.line + slram=<name>,<start>,<end/offset> + + <name>: name of the device that will be listed in /proc/mtd + <start>: start of the memory region, decimal or hex (0xabcdef) + <end/offset>: end of the memory region. It's possible to use +0x1234 + to specify the offset instead of the absolute address + + NOTE: + With slram it's only possible to map a contigous memory region. Therfore + if there's a device mapped somewhere in the region specified slram will + fail to load (see kernel log if modprobe fails). + + - + + Jochen Schaeuble <psionic@psionic.de> ======================================================================*/ @@ -20,7 +46,6 @@ #include <linux/init.h> #include <asm/io.h> #include <asm/system.h> -#include <stdarg.h> #include <linux/mtd/mtd.h> @@ -52,7 +77,7 @@ static slram_mtd_list_t *slram_mtdlist = NULL; int slram_erase(struct mtd_info *, struct erase_info *); int slram_point(struct mtd_info *, loff_t, size_t, size_t *, u_char **); -void slram_unpoint(struct mtd_info *, u_char *); +void slram_unpoint(struct mtd_info *, u_char *, loff_t, size_t); int slram_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); int slram_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); @@ -93,7 +118,7 @@ int slram_point(struct mtd_info *mtd, loff_t from, size_t len, return(0); } -void slram_unpoint(struct mtd_info *mtd, u_char *addr) +void slram_unpoint(struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) { } @@ -174,9 +199,9 @@ int register_device(char *name, unsigned long start, unsigned long length) (*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 = 0x10000; + (*curmtd)->mtdinfo->erasesize = 0x0; if (add_mtd_device((*curmtd)->mtdinfo)) { E("slram: Failed to register new device\n"); diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index fa49bd7d4511..a2ef3efb88f6 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1,5 +1,5 @@ /* This version ported to the Linux-MTD system by dwmw2@infradead.org - * $Id: ftl.c,v 1.39 2001/10/02 15:05:11 dwmw2 Exp $ + * $Id: ftl.c,v 1.50 2003/05/21 10:49:47 dwmw2 Exp $ * * Fixes: Arnaldo Carvalho de Melo <acme@conectiva.com.br> * - fixes some leaks on failure in build_maps and ftl_notify_add, cleanups @@ -55,30 +55,27 @@ contact M-Systems (http://www.m-sys.com) directly. ======================================================================*/ +#include <linux/mtd/blktrans.h> #include <linux/module.h> -#include <linux/mtd/compatmac.h> #include <linux/mtd/mtd.h> /*#define PSYCHO_DEBUG */ #include <linux/kernel.h> -#include <linux/jiffies.h> +#include <linux/sched.h> #include <linux/ptrace.h> #include <linux/slab.h> #include <linux/string.h> #include <linux/timer.h> #include <linux/major.h> #include <linux/fs.h> -#include <linux/ioctl.h> +#include <linux/init.h> #include <linux/hdreg.h> -#include <stdarg.h> - #include <linux/vmalloc.h> #include <linux/blkpg.h> +#include <asm/uaccess.h> #include <linux/mtd/ftl.h> -#define request_arg_t request_queue_t *q - /*====================================================================*/ /* Parameters that can be set with 'insmod' */ @@ -92,10 +89,6 @@ MODULE_PARM(shuffle_freq, "i"); #define FTL_MAJOR 44 #endif -/* Funky stuff for setting up a block device */ -#define MAJOR_NR FTL_MAJOR - -#include <linux/blk.h> /*====================================================================*/ @@ -106,8 +99,7 @@ MODULE_PARM(shuffle_freq, "i"); #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 @@ -118,8 +110,7 @@ MODULE_PARM(shuffle_freq, "i"); /* Each memory region corresponds to a minor device */ typedef struct partition_t { - struct mtd_info *mtd; - struct gendisk *disk; + struct mtd_blktrans_dev mbd; u_int32_t state; u_int32_t *VirtualBlockMap; u_int32_t *VirtualPageMap; @@ -144,21 +135,10 @@ typedef struct partition_t { 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 @@ -171,21 +151,9 @@ static struct mtd_notifier ftl_notifier = { /*====================================================================*/ -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_revalidate(struct gendisk *disk); static void ftl_erase_callback(struct erase_info *done); -static struct block_device_operations ftl_blk_fops = { - .owner = THIS_MODULE, - .open = ftl_open, - .release = ftl_close, - .ioctl = ftl_ioctl, - .revalidate_disk= ftl_revalidate, -}; /*====================================================================== @@ -201,13 +169,13 @@ static int scan_header(partition_t *part) loff_t offset, max_offset; int ret; part->header.FormattedSize = 0; - max_offset = (0x100000<part->mtd->size)?0x100000:part->mtd->size; + max_offset = (0x100000<part->mbd.mtd->size)?0x100000:part->mbd.mtd->size; /* Search first megabyte for a valid FTL header */ for (offset = 0; (offset + sizeof(header)) < max_offset; - offset += part->mtd->erasesize ? : 0x2000) { + offset += part->mbd.mtd->erasesize ? : 0x2000) { - ret = part->mtd->read(part->mtd, offset, sizeof(header), &ret, + ret = part->mbd.mtd->read(part->mbd.mtd, offset, sizeof(header), &ret, (unsigned char *)&header); if (ret) @@ -226,9 +194,9 @@ static int scan_header(partition_t *part) 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; @@ -263,7 +231,7 @@ static int build_maps(partition_t *part) 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) @@ -328,7 +296,7 @@ static int build_maps(partition_t *part) 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); @@ -393,7 +361,7 @@ static int erase_xfer(partition_t *part, 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++; @@ -460,7 +428,7 @@ static int prepare_xfer(partition_t *part, int i) 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) { @@ -476,7 +444,7 @@ static int prepare_xfer(partition_t *part, int i) 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) @@ -523,7 +491,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, 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)); @@ -541,7 +509,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, 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) { @@ -561,7 +529,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, 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"); @@ -569,7 +537,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, } - 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"); @@ -588,7 +556,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, } /* 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) { @@ -598,7 +566,7 @@ static int copy_erase_unit(partition_t *part, u_int16_t srcunit, /* 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) { @@ -686,8 +654,8 @@ static int reclaim_block(partition_t *part) 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) @@ -785,7 +753,7 @@ static u_int32_t find_free(partition_t *part) /* 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)); @@ -814,64 +782,6 @@ static u_int32_t find_free(partition_t *part) } /* 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) -{ - partition_t *partition = inode->i_bdev->bd_disk->private_data; - if (!partition) - return -ENODEV; - - if (partition->state != FTL_FORMATTED) - return -ENXIO; - - if (get_capacity(partition->disk) == 0) - return -ENXIO; - - if (!get_mtd_device(partition->mtd, -1)) - return /* -E'SBUGGEREDOFF */ -ENXIO; - - if ((file->f_mode & 2) && !(partition->mtd->flags & MTD_CLEAR_BITS) ) { - put_mtd_device(partition->mtd); - return -EROFS; - } - - DEBUG(0, "ftl_cs: ftl_open(%s)\n", inode->i_bdev->bd_disk->disk_name); - - atomic_inc(&partition->open); - - return 0; -} - -/*====================================================================*/ - -static release_t ftl_close(struct inode *inode, struct file *file) -{ - partition_t *part = inode->i_bdev->bd_disk->private_data; - int i; - - DEBUG(0, "ftl_cs: ftl_close(%s)\n", inode->i_bdev->bd_disk->disk_name); - - /* 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); - release_return(0); -} /* ftl_close */ - /*====================================================================== @@ -906,7 +816,7 @@ static int ftl_read(partition_t *part, caddr_t buffer, 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) { @@ -945,7 +855,7 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr, 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); @@ -982,7 +892,7 @@ static int set_bam_entry(partition_t *part, u_int32_t log_addr, #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) { @@ -1042,7 +952,7 @@ static int ftl_write(partition_t *part, caddr_t buffer, 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) { @@ -1080,94 +990,43 @@ static int ftl_write(partition_t *part, caddr_t buffer, ======================================================================*/ -static int ftl_ioctl(struct inode *inode, struct file *file, - u_int cmd, u_long arg) +static int ftl_ioctl(struct mtd_blktrans_dev *dev, struct inode *inode, + struct file *file, u_int cmd, u_long arg) { - partition_t *part = inode->i_bdev->bd_disk->private_data; - struct hd_geometry *geo = (struct hd_geometry *)arg; - int ret = 0; - u_long sect; - - if (!part) - return -ENODEV; /* How? */ + struct hd_geometry *geo = (struct hd_geometry *)arg; + partition_t *part = (void *)dev; + u_long sect; - if (cmd != HDIO_GETGEO) - return -EINVAL; - ret = verify_area(VERIFY_WRITE, (long *)arg, sizeof(*geo)); - if (ret) - return ret; + switch (cmd) { + case HDIO_GETGEO: /* Sort of arbitrary: round size down to 4K 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(get_start_sect(inode->i_bdev), (u_long *)&geo->start); - return 0; + if (put_user(1, (char *)&geo->heads) || + put_user(8, (char *)&geo->sectors) || + put_user((sect>>3), (short *)&geo->cylinders) || + put_user(0, (u_long *)&geo->start)) + return -EFAULT; + + case BLKFLSBUF: + return 0; + } + return -ENOTTY; } /* ftl_ioctl */ -/*====================================================================== - Handler for block device requests +/*======================================================================*/ -======================================================================*/ - -static int ftl_revalidate(struct gendisk *disk) +static int ftl_readsect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) { - partition_t *part = disk->private_data; - scan_header(part); - set_capacity(disk, le32_to_cpu(part->header.FormattedSize)/SECTOR_SIZE); - return 0; + return ftl_read((void *)dev, buf, block, 1); } -/*====================================================================== - - Handler for block device requests - -======================================================================*/ - -static struct request_queue ftl_queue; - -static void do_ftl_request(struct request_queue *q) +static int ftl_writesect(struct mtd_blktrans_dev *dev, + unsigned long block, char *buf) { - struct request *req; - partition_t *part; - int ret; - - do { - // sti(); - req = elv_next_request(q); - if (!req) - return; - part = req->rq_disk->private_data; - if (part) { - ret = 0; - switch (rq_data_dir(req)) { - case READ: - ret = ftl_read(part, req->buffer, req->sector, - req->current_nr_sectors); - if (ret) - printk("ftl_read returned %d\n", ret); - break; - case WRITE: - ret = ftl_write(part, req->buffer, req->sector, - req->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) - req->sector += req->current_nr_sectors; - - end_request(req, (ret == 0) ? 1 : 0); - } while (1); -} /* do_ftl_request */ + return ftl_write((void *)dev, buf, block, 1); +} /*====================================================================*/ @@ -1196,109 +1055,75 @@ void ftl_freepart(partition_t *part) } /* 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; - struct gendisk *disk; - 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); - disk = alloc_disk(1 << PART_BITS); - if (!partition||!disk) { + if (!partition) { printk(KERN_WARNING "No memory to scan for FTL on %s\n", - mtd->name); - kfree(partition); - put_disk(disk); + mtd->name); return; } memset(partition, 0, sizeof(partition_t)); - sprintf(disk->disk_name, "ftl%c", 'a' + device); - disk->major = FTL_MAJOR; - disk->first_minor = device << 4; - disk->fops = &ftl_blk_fops; - partition->mtd = mtd; - partition->disk = disk; - - if ((scan_header(partition) == 0) && (build_maps(partition) == 0)) { + + 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; - set_capacity(disk, le32_to_cpu(partition->header.FormattedSize)/SECTOR_SIZE); - disk->private_data = partition; - disk->queue = &ftl_queue; - add_disk(disk); #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 - } else { + 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); - put_disk(disk); - } } -static void ftl_notify_remove(struct mtd_info *mtd) +static void ftl_remove_dev(struct mtd_blktrans_dev *dev) { - int i; - - /* 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; - del_gendisk(myparts[i]->disk); - put_disk(myparts[i]->disk); - 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, + .ioctl = ftl_ioctl, + .add_mtd = ftl_add_mtd, + .remove_dev = ftl_remove_dev, + .owner = THIS_MODULE, +}; + int init_ftl(void) { - static spinlock_t lock = SPIN_LOCK_UNLOCKED; - DEBUG(0, "$Id: ftl.c,v 1.39 2001/10/02 15:05:11 dwmw2 Exp $\n"); - - if (register_blkdev(FTL_MAJOR, "ftl")) - return -EAGAIN; + DEBUG(0, "$Id: ftl.c,v 1.50 2003/05/21 10:49:47 dwmw2 Exp $\n"); - blk_init_queue(&ftl_queue, &do_ftl_request, &lock); - 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(&ftl_queue); + deregister_mtd_blktrans(&ftl_tr); } module_init(init_ftl); module_exit(cleanup_ftl); + MODULE_LICENSE("Dual MPL/GPL"); MODULE_AUTHOR("David Hinds <dahinds@users.sourceforge.net>"); -MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices and M-Systems DiskOnChip 1000"); +MODULE_DESCRIPTION("Support code for Flash Translation Layer, used on PCMCIA devices"); diff --git a/drivers/mtd/inftlcore.c b/drivers/mtd/inftlcore.c new file mode 100644 index 000000000000..f9e4eca258d8 --- /dev/null +++ b/drivers/mtd/inftlcore.c @@ -0,0 +1,894 @@ +/* + * inftlcore.c -- Linux driver for Inverse Flash Translation Layer (INFTL) + * + * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) + * + * Based heavily on the nftlcore.c code which is: + * (c) 1999 Machine Vision Holdings, Inc. + * Author: David Woodhouse <dwmw2@infradead.org> + * + * $Id: inftlcore.c,v 1.9 2003/05/23 11:41:47 dwmw2 Exp $ + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#include <linux/config.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/kmod.h> +#include <linux/hdreg.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nftl.h> +#include <linux/mtd/inftl.h> +#include <asm/uaccess.h> +#include <asm/errno.h> +#include <asm/io.h> + +/* + * Maximum number of loops while examining next block, to have a + * chance to detect consistency problems (they should never happen + * because of the checks done in the mounting. + */ +#define MAX_LOOPS 10000 + +extern void INFTL_dumptables(struct INFTLrecord *inftl); +extern void INFTL_dumpVUchains(struct INFTLrecord *inftl); + +static void inftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) +{ + struct INFTLrecord *inftl; + unsigned long temp; + + if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) + return; + + DEBUG(MTD_DEBUG_LEVEL3, "INFTL: add_mtd for %s\n", mtd->name); + + inftl = kmalloc(sizeof(*inftl), GFP_KERNEL); + + if (!inftl) { + printk(KERN_WARNING "INFTL: Out of memory for data structures\n"); + return; + } + memset(inftl, 0, sizeof(*inftl)); + + inftl->mbd.mtd = mtd; + inftl->mbd.devnum = -1; + inftl->mbd.blksize = 512; + inftl->mbd.tr = tr; + + if (INFTL_mount(inftl) < 0) { + printk(KERN_WARNING "INFTL: could not mount device\n"); + kfree(inftl); + return; + } + + /* OK, it's a new one. Set up all the data structures. */ + + /* Calculate geometry */ + inftl->cylinders = 1024; + inftl->heads = 16; + + temp = inftl->cylinders * inftl->heads; + inftl->sectors = inftl->mbd.size / temp; + if (inftl->mbd.size % temp) { + inftl->sectors++; + temp = inftl->cylinders * inftl->sectors; + inftl->heads = inftl->mbd.size / temp; + + if (inftl->mbd.size % temp) { + inftl->heads++; + temp = inftl->heads * inftl->sectors; + inftl->cylinders = inftl->mbd.size / temp; + } + } + + if (inftl->mbd.size != inftl->heads * inftl->cylinders * inftl->sectors) { + /* + Oh no we don't have + mbd.size == heads * cylinders * sectors + */ + printk(KERN_WARNING "INFTL: cannot calculate a geometry to " + "match size of 0x%lx.\n", inftl->mbd.size); + printk(KERN_WARNING "INFTL: using C:%d H:%d S:%d " + "(== 0x%lx sects)\n", + inftl->cylinders, inftl->heads , inftl->sectors, + (long)inftl->cylinders * (long)inftl->heads * + (long)inftl->sectors ); + } + + if (add_mtd_blktrans_dev) { + 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, prevEUN, 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 (;;) { + /* Find oldest unit in chain. */ + thisEUN = inftl->VUtable[thisVUC]; + prevEUN = BLOCK_NIL; + while (inftl->PUtable[thisEUN] != BLOCK_NIL) { + prevEUN = thisEUN; + thisEUN = inftl->PUtable[thisEUN]; + } + + 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++; + } + } + + 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_ioctl(struct mtd_blktrans_dev *dev, + struct inode *inode, struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct NFTLrecord *nftl = (void *)dev; + + switch (cmd) { + case HDIO_GETGEO: { + struct hd_geometry g; + + g.heads = nftl->heads; + g.sectors = nftl->sectors; + g.cylinders = nftl->cylinders; + g.start = 0; + return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; + } + + default: + return -ENOTTY; + } +} + + +struct mtd_blktrans_ops inftl_tr = { + .name = "inftl", + .major = INFTL_MAJOR, + .part_bits = INFTL_PARTN_BITS, + .ioctl = inftl_ioctl, + .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.9 $, " + "inftlmount.c %s\n", inftlmountrev); + + return register_mtd_blktrans(&inftl_tr); +} + +static void __exit cleanup_inftl(void) +{ + deregister_mtd_blktrans(&inftl_tr); +} + +module_init(init_inftl); +module_exit(cleanup_inftl); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>, David Woodhouse <dwmw2@infradead.org>, Fabrice Bellard <fabrice.bellard@netgem.com> et al."); +MODULE_DESCRIPTION("Support code for Inverse Flash Translation Layer, used on M-Systems DiskOnChip 2000, Millennium and Millennium Plus"); diff --git a/drivers/mtd/inftlmount.c b/drivers/mtd/inftlmount.c new file mode 100644 index 000000000000..abe3957c2f9d --- /dev/null +++ b/drivers/mtd/inftlmount.c @@ -0,0 +1,815 @@ +/* + * 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.9 2003/05/23 11:35:07 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 + */ + +#define __NO_VERSION__ +#include <linux/kernel.h> +#include <linux/module.h> +#include <asm/errno.h> +#include <asm/io.h> +#include <asm/uaccess.h> +#include <linux/miscdevice.h> +#include <linux/pci.h> +#include <linux/delay.h> +#include <linux/slab.h> +#include <linux/sched.h> +#include <linux/init.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nftl.h> +#include <linux/mtd/inftl.h> +#include <linux/mtd/compatmac.h> + +char inftlmountrev[]="$Revision: 1.9 $"; + +/* + * 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 " + "Parition %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 " + "Parition %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 Parition " + "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)); + + /* 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 paritial 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 --git a/drivers/mtd/maps/Kconfig b/drivers/mtd/maps/Kconfig index 3ab0183ebc86..29343b23fe13 100644 --- a/drivers/mtd/maps/Kconfig +++ b/drivers/mtd/maps/Kconfig @@ -1,9 +1,16 @@ -# drivers/mtd/maps/Config.in -# $Id: Config.in,v 1.16 2001/09/19 18:28:37 dwmw2 Exp $ +# drivers/mtd/maps/Kconfig +# $Id: Kconfig,v 1.10 2003/05/28 11:34:44 dwmw2 Exp $ menu "Mapping drivers for chip access" depends on MTD!=n +config MTD_COMPLEX_MAPPINGS + bool "Support non-linear mappings of flash chips" + depends on MTD + help + This causes the chip drivers to allow for complicated + paged mappings of flash chips. + config MTD_PHYSMAP tristate "CFI Flash device in physical memory map" depends on MTD_CFI @@ -80,7 +87,7 @@ config MTD_NETSC520 config MTD_SBC_GXX tristate "CFI Flash device mapped on Arcom SBC-GXx boards" - depends on X86 && MTD_CFI_INTELEXT && MTD_PARTITIONS + depends on X86 && MTD_CFI_INTELEXT && MTD_PARTITIONS && MTD_COMPLEX_MAPPINGS help This provides a driver for the on-board flash of Arcom Control Systems' SBC-GXn family of boards, formerly known as SBC-MediaGX. @@ -91,7 +98,7 @@ config MTD_SBC_GXX config MTD_ELAN_104NC tristate "CFI Flash device mapped on Arcom ELAN-104NC" - depends on X86 && MTD_CFI_INTELEXT && MTD_PARTITIONS + depends on X86 && MTD_CFI_INTELEXT && MTD_PARTITIONS && MTD_COMPLEX_MAPPINGS help This provides a driver for the on-board flash of the Arcom Control System's ELAN-104NC development board. By default the flash @@ -99,20 +106,16 @@ config MTD_ELAN_104NC devices. This board utilizes Intel StrataFlash. More info at <http://www.arcomcontrols.com/products/icp/pc104/processors/>. -config MTD_MIXMEM - tristate "JEDEC Flash device mapped on Mixcom piggyback card" - depends on X86 && MTD_JEDEC +config MTD_LUBBOCK + tristate "CFI Flash device mapped on Intel Lubbock XScale eval board" + depends on ARCH_LUBBOCK && MTD_CFI_INTELEXT && MTD_PARTITIONS help - This supports the paging arrangement for access to flash chips - on the MixCOM piggyback card, allowing the flash chip drivers - to get on with their job of driving the flash chips without - having to know about the paging. If you have one of these boards, - you probably want to enable this mapping driver. More info is at - <http://www.itc.hu/>. + This provides a driver for the on-board flash of the Intel + 'Lubbock' XScale evaluation board. config MTD_OCTAGON tristate "JEDEC Flash device mapped on Octagon 5066 SBC" - depends on X86 && MTD_JEDEC + depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS help This provides a 'mapping' driver which supports the way in which the flash chips are connected in the Octagon-5066 Single Board @@ -121,7 +124,7 @@ config MTD_OCTAGON config MTD_VMAX tristate "JEDEC Flash device mapped on Tempustech VMAX SBC301" - depends on X86 && MTD_JEDEC + depends on X86 && MTD_JEDEC && MTD_COMPLEX_MAPPINGS help This provides a 'mapping' driver which supports the way in which the flash chips are connected in the Tempustech VMAX SBC301 Single @@ -139,9 +142,93 @@ config MTD_SCx200_DOCFLASH If compiled as a module, it will be called scx200_docflash. +config MTD_AMD76XROM + tristate "BIOS flash chip on AMD76x southbridge" + depends on X86 && MTD_JEDECPROBE + help + Support for treating the BIOS flash chip on AMD76x motherboards + as an MTD device - with this you can reprogram your BIOS. + + BE VERY CAREFUL. + +config MTD_ICH2ROM + tristate "BIOS flash chip on Intel Hub Controller 2" + depends on X86 && MTD_JEDECPROBE && MTD_COMPLEX_MAPPINGS + help + Support for treating the BIOS flash chip on ICH2 motherboards + as an MTD device - with this you can reprogram your BIOS. + + BE VERY CAREFUL. + +config MTD_SCB2_FLASH + tristate "BIOS flash chip on Intel SCB2 boards" + depends on X86 && MTD_JEDECPROBE + help + Support for treating the BIOS flash chip on Intel SCB2 boards + as an MTD device - with this you can reprogram your BIOS. + + BE VERY CAREFUL. + +config MTD_TSUNAMI + tristate "Flash chips on Tsunami TIG bus" + depends on ALPHA_TSUNAMI && MTD_COMPLEX_MAPPINGS + help + Support for the flash chip on Tsunami TIG bus. + +config MTD_LASAT + tristate "Flash chips on LASAT board" + depends on LASAT && MTD_CFI + help + Support for the flash chips on the Lasat 100 and 200 boards. + +config MTD_NETtel + tristate "CFI flash device on SnapGear/SecureEdge" + depends on X86 && MTD_PARTITIONS && MTD_JEDECPROBE + help + Support for flash chips on NETtel/SecureEdge/SnapGear boards. + +config MTD_PB1XXX + tristate "Flash devices on Alchemy PB1xxx boards" + depends on MIPS && ( MIPS_PB1000 || MIPS_PB1100 || MIPS_PB1500 ) + help + Flash memory access on Alchemy Pb1000/Pb1100/Pb1500 boards + +config MTD_PB1XXX_BOOT + bool "PB1x00 boot flash device" + depends on MTD_PB1XXX && ( MIPS_PB1100 || MIPS_PB1500 ) + help + Use the first of the two 32MiB flash banks on Pb1100/Pb1500 board. + You can say 'Y' to both this and 'MTD_PB1XXX_USER' below, to use + both banks. + +config MTD_PB1XXX_USER + bool "PB1x00 user flash device" + depends on MTD_PB1XXX && ( MIPS_PB1100 || MIPS_PB1500 ) + default y if MTD_PB1XX_BOOT = n + help + Use the second of the two 32MiB flash banks on Pb1100/Pb1500 board. + You can say 'Y' to both this and 'MTD_PB1XXX_BOOT' above, to use + both banks. + +config MTD_DILNETPC + tristate "CFI Flash device mapped on DIL/Net PC" + depends on X86 && MTD_CONCAT && MTD_PARTITIONS && MTD_CFI_INTELEXT + help + MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP". + For details, see http://www.ssv-embedded.de/ssv/pc104/p169.htm + and http://www.ssv-embedded.de/ssv/pc104/p170.htm + +config MTD_DILNETPC_BOOTSIZE + hex "Size of DIL/Net PC flash boot partition" + depends on MTD_DILNETPC + default "0x80000" + help + The amount of space taken up by the kernel or Etherboot + on the DIL/Net PC flash chips. + config MTD_L440GX tristate "BIOS flash chip on Intel L440GX boards" - depends on X86 && MTD_JEDEC + depends on X86 && MTD_JEDECPROBE help Support for treating the BIOS flash chip on Intel L440GX motherboards as an MTD device - with this you can reprogram your BIOS. @@ -150,7 +237,7 @@ config MTD_L440GX config MTD_TQM8XXL tristate "CFI Flash device mapped on TQM8XXL" - depends on MTD_CFI && TQM8xxL && PPC + depends on MTD_CFI && PPC32 && 8xx && TQM8xxL help The TQM8xxL PowerPC board has up to two banks of CFI-compliant chips, currently uses AMD one. This 'mapping' driver supports @@ -160,7 +247,7 @@ config MTD_TQM8XXL config MTD_RPXLITE tristate "CFI Flash device mapped on RPX Lite or CLLF" - depends on MTD_CFI && PPC + depends on MTD_CFI && PPC32 && 8xx && (RPXCLASSIC || RPXLITE) help The RPXLite PowerPC board has CFI-compliant chips mapped in a strange sparse mapping. This 'mapping' driver supports that @@ -168,9 +255,17 @@ config MTD_RPXLITE to communicate with the chips on the RPXLite board. More at <http://www.embeddedplanet.com/rpx_lite_specification_sheet.htm>. +config MTD_MBX860 + tristate "System flash on MBX860 board" + depends on MTD_CFI && PPC32 && 8xx && MBX + help + This enables access routines for the flash chips on the Motorola + MBX860 board. If you have one of these boards and would like + to use the flash chips on it, say 'Y'. + config MTD_DBOX2 tristate "CFI Flash device mapped on D-Box2" - depends on PPC && MTD_CFI_INTELSTD && MTD_CFI_INTELEXT && MTD_CFI_AMDSTD + depends on PPC32 && 8xx && MTD_CFI_INTELSTD && MTD_CFI_INTELEXT && MTD_CFI_AMDSTD help This enables access routines for the flash chips on the Nokia/Sagem D-Box 2 board. If you have one of these boards and would like to use @@ -178,15 +273,47 @@ config MTD_DBOX2 config MTD_CFI_FLAGADM tristate "CFI Flash device mapping on FlagaDM" - depends on PPC && MTD_CFI + depends on PPC32 && 8xx && MTD_CFI help Mapping for the Flaga digital module. If you don´t have one, ignore this setting. +config MTD_BEECH + tristate "CFI Flash device mapped on IBM 405LP Beech" + depends on MTD_CFI && PPC32 && 40x && BEECH + help + This enables access routines for the flash chips on the IBM + 405LP Beech board. If you have one of these boards and would like + to use the flash chips on it, say 'Y'. + +config MTD_ARCTIC + tristate "CFI Flash device mapped on IBM 405LP Arctic" + depends on MTD_CFI && PPC32 && 40x && ARCTIC2 + help + This enables access routines for the flash chips on the IBM 405LP + Arctic board. If you have one of these boards and would like to + use the flash chips on it, say 'Y'. + +config MTD_EBONY + tristate "CFI Flash device mapped on IBM 440GP Ebony" + depends on MTD_CFI && PPC32 && 440 && EBONY + help + This enables access routines for the flash chips on the IBM 440GP + Ebony board. If you have one of these boards and would like to + use the flash chips on it, say 'Y'. + +config MTD_REDWOOD + tristate "CFI Flash devices mapped on IBM Redwood" + depends on MTD_CFI && PPC32 && 4xx && 40x && ( REDWOOD_4 || REDWOOD_5 || REDWOOD_6 ) + help + This enables access routines for the flash chips on the IBM + Redwood board. If you have one of these boards and would like to + use the flash chips on it, say 'Y'. + config MTD_CSTM_MIPS_IXX tristate "Flash chip mapping on ITE QED-4N-S01B, Globespan IVR or custom board" - depends on MIPS && MTD_CFI && MTD_JEDEC && MTD_PARTITIONS - ---help--- + depends on MIPS && MTD_CFI && MTD_JEDECPROBE && MTD_PARTITIONS + help This provides a mapping driver for the Integrated Tecnology Express, Inc (ITE) QED-4N-S01B eval board and the Globespan IVR Reference Board. It provides the necessary addressing, length, @@ -242,12 +369,6 @@ config MTD_SOLUTIONENGINE This enables access to the flash chips on the Hitachi SolutionEngine and similar boards. Say 'Y' if you are building a kernel for such a board. -config MTD_NORA - tristate "CFI Flash device mapped on Nora" - depends on ARM && MTD_CFI - help - If you had to ask, you don't have one. Say 'N'. - config MTD_ARM_INTEGRATOR tristate "CFI Flash device mapped on ARM Integrator/P720T" depends on ARM && MTD_CFI @@ -269,7 +390,7 @@ config MTD_SA1100 config MTD_DC21285 tristate "CFI Flash device mapped on DC21285 Footbridge" - depends on ARM && MTD_CFI && ARCH_FOOTBRIDGE + depends on ARM && MTD_CFI && ARCH_FOOTBRIDGE && MTD_COMPLEX_MAPPINGS help This provides a driver for the flash accessed using Intel's 21285 bridge used with Intel's StrongARM processors. More info at @@ -283,14 +404,6 @@ config MTD_IQ80310 IQ80310 evaluation board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. -config MTD_IQ80321 - tristate "CFI Flash device mapped on the XScale IQ80321 board" - depends on ARM && MTD_CFI && ARCH_IQ80321 - help - This enables access routines for the flash chips on the Intel XScale - IQ80321 evaluation board. If you have one of these boards and would - like to use the flash chips on it, say 'Y'. - config MTD_EPXA10DB tristate "CFI Flash device mapped on Epxa10db" depends on ARM && MTD_CFI && MTD_PARTITIONS && ARCH_CAMELOT @@ -335,6 +448,13 @@ config MTD_CEIVA PhotoMax Digital Picture Frame. If you have such a device, say 'Y'. +config MTD_H720X + tristate "Hynix evaluation board mappings" + depends on ARM && MTD_CFI && ( ARCH_H7201 || ARCH_H7202 ) + help + This enables access to the flash chips on the Hynix evaluation boards. + If you have such a board, say 'Y'. + # This needs CFI or JEDEC, depending on the cards found. config MTD_PCI tristate "PCI MTD driver" diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index a6dec0139c63..6c82870b32e6 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -1,7 +1,11 @@ # # linux/drivers/maps/Makefile # -# $Id: Makefile,v 1.13 2001/08/16 15:16:58 rmk Exp $ +# $Id: Makefile.common,v 1.2 2003/05/28 10:48:41 dwmw2 Exp $ + +ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y) +obj-$(CONFIG_MTD) += map_funcs.o +endif # Chip mappings obj-$(CONFIG_MTD_CDB89712) += cdb89712.o @@ -9,12 +13,16 @@ obj-$(CONFIG_MTD_ARM_INTEGRATOR)+= integrator-flash.o obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o obj-$(CONFIG_MTD_DC21285) += dc21285.o +obj-$(CONFIG_MTD_DILNETPC) += dilnetpc.o obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.o obj-$(CONFIG_MTD_EPXA10DB) += epxa10db-flash.o obj-$(CONFIG_MTD_IQ80310) += iq80310.o -obj-$(CONFIG_MTD_IQ80321) += iq80321.o obj-$(CONFIG_MTD_L440GX) += l440gx.o -obj-$(CONFIG_MTD_NORA) += nora.o +obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o +obj-$(CONFIG_MTD_ICH2ROM) += ich2rom.o +obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o +obj-$(CONFIG_MTD_LUBBOCK) += lubbock-flash.o +obj-$(CONFIG_MTD_MBX860) += mbx860.o obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o @@ -31,10 +39,19 @@ obj-$(CONFIG_MTD_VMAX) += vmax301.o obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o obj-$(CONFIG_MTD_OCELOT) += ocelot.o -obj-$(CONFIG_MTD_UCLINUX) += uclinux.o obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o obj-$(CONFIG_MTD_PCI) += pci.o +obj-$(CONFIG_MTD_PB1XXX) += 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 obj-$(CONFIG_MTD_FORTUNET) += fortunet.o +obj-$(CONFIG_MTD_REDWOOD) += redwood.o +obj-$(CONFIG_MTD_UCLINUX) += uclinux.o +obj-$(CONFIG_MTD_NETtel) += nettel.o +obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_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 diff --git a/drivers/mtd/maps/amd76xrom.c b/drivers/mtd/maps/amd76xrom.c new file mode 100644 index 000000000000..1b9354876e18 --- /dev/null +++ b/drivers/mtd/maps/amd76xrom.c @@ -0,0 +1,215 @@ +/* + * amd76xrom.c + * + * Normal mappings of chips in physical memory + * $Id: amd76xrom.c,v 1.7 2003/05/21 12:45:17 dwmw2 Exp $ + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/config.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> + + +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; +}; + + +static struct amd76xrom_map_info amd76xrom_map = { + .map = { + .name = "AMD76X rom", + .size = 0, + .buswidth = 1, + }, + .mtd = 0, + .window_addr = 0, +}; + +static int __devinit amd76xrom_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + struct rom_window { + u32 start; + u32 size; + u8 segen_bits; + }; + static struct rom_window rom_window[] = { + { 0xffb00000, 5*1024*1024, (1<<7) | (1<<6), }, + { 0xffc00000, 4*1024*1024, (1<<7), }, + { 0xffff0000, 64*1024, 0 }, + { 0 , 0, 0 }, + }; + static const u32 rom_probe_sizes[] = { + 5*1024*1024, 4*1024*1024, 2*1024*1024, 1024*1024, 512*1024, + 256*1024, 128*1024, 64*1024, 0}; + static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", 0 }; + u8 byte; + struct amd76xrom_map_info *info = &amd76xrom_map; + struct rom_window *window; + int i; + u32 rom_size; + + window = &rom_window[0]; + + /* disabled because it fights with BIOS reserved regions */ +#define REQUEST_MEM_REGION 0 +#if REQUEST_MEM_REGION + 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; + } +#endif /* REQUEST_MEM_REGION */ + + /* Enable the selected rom window */ + pci_read_config_byte(pdev, 0x43, &byte); + pci_write_config_byte(pdev, 0x43, byte | window->segen_bits); + + /* Enable writes through the rom window */ + pci_read_config_byte(pdev, 0x40, &byte); + pci_write_config_byte(pdev, 0x40, byte | 1); + + /* FIXME handle registers 0x80 - 0x8C the bios region locks */ + + printk(KERN_NOTICE "amd76xrom 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); + + if (!info->window_addr) { + printk(KERN_ERR "Failed to ioremap\n"); + goto err_out_free_mmio_region; + } + info->mtd = 0; + for(i = 0; (rom_size = rom_probe_sizes[i]); i++) { + char **chip_type; + if (rom_size > window->size) { + continue; + } + 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 err_out_iounmap; + } + printk(KERN_NOTICE "amd76xrom chip at offset: 0x%x\n", + window->size - rom_size); + + info->mtd->owner = THIS_MODULE; + add_mtd_device(info->mtd); + info->window_start = window->start; + info->window_size = window->size; + return 0; + +err_out_iounmap: + iounmap((void *)(info->window_addr)); +err_out_free_mmio_region: +#if REQUEST_MEM_REGION + release_mem_region(window->start, window->size); +err_out_none: +#endif /* REQUEST_MEM_REGION */ + return -ENODEV; +} + + +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.virt = 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); + +#if REQUEST_MEM_REGION + release_mem_region(info->window_start, info->window_size); +#endif /* REQUEST_MEM_REGION */ +} + +static struct pci_device_id amd76xrom_pci_tbl[] __devinitdata = { + { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410, + 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, } +}; + +MODULE_DEVICE_TABLE(pci, amd76xrom_pci_tbl); + +#if 0 +static struct pci_driver amd76xrom_driver = { + .name = "amd76xrom", + .id_table = amd76xrom_pci_tbl, + .probe = amd76xrom_init_one, + .remove = amd76xrom_remove_one, +}; +#endif + +int __init init_amd76xrom(void) +{ + struct pci_dev *pdev; + struct pci_device_id *id; + pdev = 0; + for(id = amd76xrom_pci_tbl; id->vendor; id++) { + pdev = pci_find_device(id->vendor, id->device, 0); + if (pdev) { + break; + } + } + if (pdev) { + amd76xrom_map.pdev = pdev; + return amd76xrom_init_one(pdev, &amd76xrom_pci_tbl[0]); + } + return -ENXIO; +#if 0 + return pci_module_init(&amd76xrom_driver); +#endif +} + +static void __exit cleanup_amd76xrom(void) +{ + amd76xrom_remove_one(amd76xrom_map.pdev); +} + +module_init(init_amd76xrom); +module_exit(cleanup_amd76xrom); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>"); +MODULE_DESCRIPTION("MTD map driver for BIOS chips on the AMD76X southbridge"); + diff --git a/drivers/mtd/maps/arctic-mtd.c b/drivers/mtd/maps/arctic-mtd.c new file mode 100644 index 000000000000..974e7ceb94f8 --- /dev/null +++ b/drivers/mtd/maps/arctic-mtd.c @@ -0,0 +1,128 @@ +/* + * $Id: arctic-mtd.c,v 1.8 2003/05/21 12:45:17 dwmw2 Exp $ + * + * drivers/mtd/maps/arctic-mtd.c MTD mappings and partition tables for + * IBM 405LP Arctic boards. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March 2002 + * + * modified for Arctic by, + * David Gibson + * IBM OzLabs, Canberra, Australia + * <arctic@gibson.dropbear.id.au> + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> +#include <asm/ibm4xx.h> + +/* + * fe000000 -- ff9fffff Arctic FFS (26MB) + * ffa00000 -- fff5ffff kernel (5.504MB) + * fff60000 -- ffffffff firmware (640KB) + */ + +#define ARCTIC_FFS_SIZE 0x01a00000 /* 26 M */ +#define ARCTIC_FIRMWARE_SIZE 0x000a0000 /* 640K */ + +#define NAME "Arctic Linux Flash" +#define PADDR SUBZERO_BOOTFLASH_PADDR +#define SIZE SUBZERO_BOOTFLASH_SIZE +#define BUSWIDTH 2 + +/* 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[3] = { + { .name = "Arctic FFS", + .size = ARCTIC_FFS_SIZE, + .offset = 0,}, + { .name = "Kernel", + .size = SUBZERO_BOOTFLASH_SIZE - ARCTIC_FFS_SIZE - + ARCTIC_FIRMWARE_SIZE, + .offset = ARCTIC_FFS_SIZE,}, + { .name = "Firmware", + .size = ARCTIC_FIRMWARE_SIZE, + .offset = SUBZERO_BOOTFLASH_SIZE - ARCTIC_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, 3); +} + +static void __exit +cleanup_arctic_mtd(void) +{ + if (arctic_mtd) { + del_mtd_partitions(arctic_mtd); + map_destroy(arctic_mtd); + iounmap((void *) arctic_mtd_map.virt); + } +} + +module_init(init_arctic_mtd); +module_exit(cleanup_arctic_mtd); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("David Gibson <arctic@gibson.dropbear.id.au>"); +MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Arctic boards"); diff --git a/drivers/mtd/maps/autcpu12-nvram.c b/drivers/mtd/maps/autcpu12-nvram.c index 9b3cd3c21354..f4c27c41569a 100644 --- a/drivers/mtd/maps/autcpu12-nvram.c +++ b/drivers/mtd/maps/autcpu12-nvram.c @@ -2,7 +2,7 @@ * NV-RAM memory access on autcpu12 * (C) 2002 Thomas Gleixner (gleixner@autronix.de) * - * $Id: autcpu12-nvram.c,v 1.1 2002/02/22 09:30:24 gleixner Exp $ + * $Id: autcpu12-nvram.c,v 1.5 2003/05/21 12:45:18 dwmw2 Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -24,6 +24,7 @@ #include <linux/types.h> #include <linux/kernel.h> #include <linux/ioport.h> +#include <linux/init.h> #include <asm/io.h> #include <asm/sizes.h> #include <asm/hardware.h> @@ -32,81 +33,28 @@ #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> -__u8 autcpu12_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 autcpu12_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 autcpu12_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void autcpu12_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void autcpu12_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void autcpu12_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void autcpu12_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void autcpu12_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - while(len) { - __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); - from++; - to++; - len--; - } -} static struct mtd_info *sram_mtd; struct map_info autcpu12_sram_map = { - .name = "SRAM", - .size = 32768, - .buswidth = 8, - .read8 = autcpu12_read8, - .read16 = autcpu12_read16, - .read32 = autcpu12_read32, - .copy_from = autcpu12_copy_from, - .write8 = autcpu12_write8, - .write16 = autcpu12_write16, - .write32 = autcpu12_write32, - .copy_to = autcpu12_copy_to + .name = "SRAM", + .size = 32768, + .buswidth = 4, + .phys = 0x12000000, }; static int __init init_autcpu12_sram (void) { int err, save0, save1; - autcpu12_sram_map.map_priv_1 = (unsigned long)ioremap(0x12000000, SZ_128K); - if (!autcpu12_sram_map.map_priv_1) { + autcpu12_sram_map.virt = (unsigned long)ioremap(0x12000000, SZ_128K); + if (!autcpu12_sram_map.virt) { printk("Failed to ioremap autcpu12 NV-RAM space\n"); err = -EIO; goto out; } - + simple_map_init(&autcpu_sram_map); + /* * Check for 32K/128K * read ofs 0 @@ -115,20 +63,20 @@ static int __init init_autcpu12_sram (void) * 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 @@ map: 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 @@ map: 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 @@ out_probe: 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 @@ static void __exit cleanup_autcpu12_maps(void) 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 --git a/drivers/mtd/maps/beech-mtd.c b/drivers/mtd/maps/beech-mtd.c new file mode 100644 index 000000000000..61e21970d445 --- /dev/null +++ b/drivers/mtd/maps/beech-mtd.c @@ -0,0 +1,112 @@ +/* + * $Id: beech-mtd.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ + * + * drivers/mtd/maps/beech-mtd.c MTD mappings and partition tables for + * IBM 405LP Beech boards. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * + * Copyright (C) 2002, International Business Machines Corporation + * All Rights Reserved. + * + * Bishop Brock + * IBM Research, Austin Center for Low-Power Computing + * bcbrock@us.ibm.com + * March 2002 + * + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/init.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> +#include <asm/ibm4xx.h> + +#define NAME "Beech Linux Flash" +#define PADDR BEECH_BIGFLASH_PADDR +#define SIZE BEECH_BIGFLASH_SIZE +#define BUSWIDTH 1 + +/* Flash memories on these boards are memory resources, accessed big-endian. */ + + +static struct map_info beech_mtd_map = { + .name = NAME, + .size = SIZE, + .buswidth = BUSWIDTH, + .phys = PADDR +}; + +static struct mtd_info *beech_mtd; + +static struct mtd_partition beech_partitions[2] = { + { + .name = "Linux Kernel", + .size = BEECH_KERNEL_SIZE, + .offset = BEECH_KERNEL_OFFSET + }, { + .name = "Free Area", + .size = BEECH_FREE_AREA_SIZE, + .offset = BEECH_FREE_AREA_OFFSET + } +}; + +static int __init +init_beech_mtd(void) +{ + printk("%s: 0x%08x at 0x%08x\n", NAME, SIZE, PADDR); + + beech_mtd_map.virt = (unsigned long) ioremap(PADDR, SIZE); + + if (!beech_mtd_map.virt) { + printk("%s: failed to ioremap 0x%x\n", NAME, PADDR); + return -EIO; + } + + simple_map_init(&beech_mtd_map); + + printk("%s: probing %d-bit flash bus\n", NAME, BUSWIDTH * 8); + beech_mtd = do_map_probe("cfi_probe", &beech_mtd_map); + + if (!beech_mtd) + return -ENXIO; + + beech_mtd->owner = THIS_MODULE; + + return add_mtd_partitions(beech_mtd, beech_partitions, 2); +} + +static void __exit +cleanup_beech_mtd(void) +{ + if (beech_mtd) { + del_mtd_partitions(beech_mtd); + map_destroy(beech_mtd); + iounmap((void *) beech_mtd_map.virt); + } +} + +module_init(init_beech_mtd); +module_exit(cleanup_beech_mtd); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bishop Brock <bcbrock@us.ibm.com>"); +MODULE_DESCRIPTION("MTD map and partitions for IBM 405LP Beech boards"); diff --git a/drivers/mtd/maps/cdb89712.c b/drivers/mtd/maps/cdb89712.c index 12097a7f1c3e..119968944e31 100644 --- a/drivers/mtd/maps/cdb89712.c +++ b/drivers/mtd/maps/cdb89712.c @@ -1,89 +1,37 @@ /* * Flash on Cirrus CDB89712 * - * $Id: cdb89712.c,v 1.3 2001/10/02 15:14:43 rmk Exp $ + * $Id: cdb89712.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ */ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/ioport.h> +#include <linux/init.h> #include <asm/io.h> #include <asm/arch/hardware.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> -__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) @@ -96,13 +44,13 @@ static int __init init_cdb89712_flash (void) 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); @@ -115,7 +63,7 @@ static int __init init_cdb89712_flash (void) 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"); @@ -129,34 +77,31 @@ out_probe: 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: return err; } + + + + 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) @@ -169,13 +114,13 @@ static int __init init_cdb89712_sram (void) 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"); @@ -183,7 +128,7 @@ static int __init init_cdb89712_sram (void) goto out_ioremap; } - sram_mtd->module = THIS_MODULE; + sram_mtd->owner = THIS_MODULE; sram_mtd->erasesize = 16; if (add_mtd_device(sram_mtd)) { @@ -198,30 +143,33 @@ out_probe: 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: return err; } + + + + + + 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) @@ -234,13 +182,13 @@ static int __init init_cdb89712_bootrom (void) 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"); @@ -248,7 +196,7 @@ static int __init init_cdb89712_bootrom (void) goto out_ioremap; } - bootrom_mtd->module = THIS_MODULE; + bootrom_mtd->owner = THIS_MODULE; bootrom_mtd->erasesize = 0x10000; if (add_mtd_device(bootrom_mtd)) { @@ -263,13 +211,17 @@ out_probe: 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: return err; } + + + + static int __init init_cdb89712_maps(void) { @@ -289,21 +241,21 @@ static void __exit cleanup_cdb89712_maps(void) 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 --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c index f99d2ccf1aec..9234480a4b90 100644 --- a/drivers/mtd/maps/ceiva.c +++ b/drivers/mtd/maps/ceiva.c @@ -11,7 +11,7 @@ * * (C) 2000 Nicolas Pitre <nico@cam.org> * - * $Id: ceiva.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ + * $Id: ceiva.c,v 1.8 2003/05/21 12:45:18 dwmw2 Exp $ */ #include <linux/config.h> @@ -19,6 +19,7 @@ #include <linux/types.h> #include <linux/ioport.h> #include <linux/kernel.h> +#include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -31,62 +32,10 @@ #include <asm/sizes.h> /* - * This isnt complete yet, so... + * This isn't complete yet, so... */ #define CONFIG_MTD_CEIVA_STATICMAP -static __u8 clps_read8(struct map_info *map, unsigned long ofs) -{ - return readb(map->map_priv_1 + ofs); -} - -static __u16 clps_read16(struct map_info *map, unsigned long ofs) -{ - return readw(map->map_priv_1 + ofs); -} - -static __u32 clps_read32(struct map_info *map, unsigned long ofs) -{ - return readl(map->map_priv_1 + ofs); -} - -static void clps_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy(to, (void *)(map->map_priv_1 + from), len); -} - -static void clps_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - writeb(d, map->map_priv_1 + adr); -} - -static void clps_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - writew(d, map->map_priv_1 + adr); -} - -static void clps_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - writel(d, map->map_priv_1 + adr); -} - -static void clps_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy((void *)(map->map_priv_1 + to), from, len); -} - -static struct map_info clps_map __initdata = { - .name = "clps flash", - .read8 = clps_read8, - .read16 = clps_read16, - .read32 = clps_read32, - .copy_from = clps_copy_from, - .write8 = clps_write8, - .write16 = clps_write16, - .write32 = clps_write32, - .copy_to = clps_copy_to, -}; - #ifdef CONFIG_MTD_CEIVA_STATICMAP /* * See include/linux/mtd/partitions.h for definition of the mtd_partition @@ -94,7 +43,7 @@ static struct map_info clps_map __initdata = { * * Please note: * 1. The flash size given should be the largest flash size that can - * be accommodated. + * be accomodated. * * 2. The bus width must defined in clps_setup_flash. * @@ -115,25 +64,23 @@ static struct map_info clps_map __initdata = { static struct mtd_partition ceiva_partitions[] = { { - .name = "Ceiva BOOT partition", - .size = BOOT_PARTITION_SIZE_KiB*1024, - - }, - { - .name = "Ceiva parameters partition", - .size = PARAMS_PARTITION_SIZE_KiB*1024, - .offset = (16 + 8) * 1024, - }, - { - .name = "Ceiva kernel partition", - .size = (KERNEL_PARTITION_SIZE_KiB)*1024, - .offset = 0x20000, - - }, - { - .name = "Ceiva root filesystem partition", - .offset = MTDPART_OFS_APPEND, - .size = (ROOT_PARTITION_SIZE_KiB)*1024, + name: "Ceiva BOOT partition", + size: BOOT_PARTITION_SIZE_KiB*1024, + offset: 0, + + },{ + name: "Ceiva parameters partition", + size: PARAMS_PARTITION_SIZE_KiB*1024, + offset: (16 + 8) * 1024, + },{ + name: "Ceiva kernel partition", + size: (KERNEL_PARTITION_SIZE_KiB)*1024, + offset: 0x20000, + + },{ + name: "Ceiva root filesystem partition", + offset: MTDPART_OFS_APPEND, + size: (ROOT_PARTITION_SIZE_KiB)*1024, } }; #endif @@ -178,7 +125,7 @@ static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info 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. */ @@ -193,7 +140,9 @@ static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info } 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) { @@ -201,16 +150,18 @@ static int __init clps_setup_mtd(struct clps_info *clps, int nr, struct mtd_info 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, " @@ -320,10 +271,8 @@ static int __init clps_setup_flash(void) 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) { @@ -333,20 +282,11 @@ static void __init clps_locate_partitions(struct mtd_info *mtd) /* * 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 --git a/drivers/mtd/maps/cfi_flagadm.c b/drivers/mtd/maps/cfi_flagadm.c index 7e8a98b7c5f8..ff08d851fe25 100644 --- a/drivers/mtd/maps/cfi_flagadm.c +++ b/drivers/mtd/maps/cfi_flagadm.c @@ -1,7 +1,7 @@ /* * Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is> * - * $Id: cfi_flagadm.c,v 1.7 2001/10/02 15:05:13 dwmw2 Exp $ + * $Id: cfi_flagadm.c,v 1.11 2003/05/21 12:45:18 dwmw2 Exp $ * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -55,83 +56,33 @@ #define FLASH_PARTITION3_ADDR 0x00240000 #define FLASH_PARTITION3_SIZE 0x001C0000 -__u8 flagadm_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 flagadm_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 flagadm_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void flagadm_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void flagadm_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void flagadm_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void flagadm_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void flagadm_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} struct map_info flagadm_map = { - .name = "FlagaDM flash device", - .size = FLASH_SIZE, - .buswidth = 2, - .read8 = flagadm_read8, - .read16 = flagadm_read16, - .read32 = flagadm_read32, - .copy_from = flagadm_copy_from, - .write8 = flagadm_write8, - .write16 = flagadm_write16, - .write32 = flagadm_write32, - .copy_to = flagadm_copy_to + .name = "FlagaDM flash device", + .size = FLASH_SIZE, + .buswidth = 2, }; struct mtd_partition flagadm_parts[] = { { - .name = "Bootloader", - .offset = FLASH_PARTITION0_ADDR, - .size = FLASH_PARTITION0_SIZE + .name = "Bootloader", + .offset = FLASH_PARTITION0_ADDR, + .size = FLASH_PARTITION0_SIZE }, { - .name = "Kernel image", - .offset = FLASH_PARTITION1_ADDR, - .size = FLASH_PARTITION1_SIZE + .name = "Kernel image", + .offset = FLASH_PARTITION1_ADDR, + .size = FLASH_PARTITION1_SIZE }, { - .name = "Initial ramdisk image", - .offset = FLASH_PARTITION2_ADDR, - .size = FLASH_PARTITION2_SIZE + .name = "Initial ramdisk image", + .offset = FLASH_PARTITION2_ADDR, + .size = FLASH_PARTITION2_SIZE }, { - .name = "Persistant storage", - .offset = FLASH_PARTITION3_ADDR, - .size = FLASH_PARTITION3_SIZE + .name = "Persistant storage", + .offset = FLASH_PARTITION3_ADDR, + .size = FLASH_PARTITION3_SIZE } }; @@ -144,22 +95,26 @@ int __init init_flagadm(void) 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 @@ static void __exit cleanup_flagadm(void) 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 --git a/drivers/mtd/maps/cstm_mips_ixx.c b/drivers/mtd/maps/cstm_mips_ixx.c index 74e8b2c73323..38f7e1183b26 100644 --- a/drivers/mtd/maps/cstm_mips_ixx.c +++ b/drivers/mtd/maps/cstm_mips_ixx.c @@ -1,11 +1,11 @@ /* - * $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. * * Basically physmap.c with the addition of partitions and - * an array of mapping info to accommodate more than one flash type per board. + * an array of mapping info to accomodate more than one flash type per board. * * Copyright 2000 MontaVista Software Inc. * @@ -33,55 +33,13 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> #include <linux/config.h> - -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) #include <linux/delay.h> -#endif - -__u8 cstm_mips_ixx_read8(struct map_info *map, unsigned long ofs) -{ - return *(__u8 *)(map->map_priv_1 + ofs); -} - -__u16 cstm_mips_ixx_read16(struct map_info *map, unsigned long ofs) -{ - return *(__u16 *)(map->map_priv_1 + ofs); -} - -__u32 cstm_mips_ixx_read32(struct map_info *map, unsigned long ofs) -{ - return *(__u32 *)(map->map_priv_1 + ofs); -} - -void cstm_mips_ixx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void cstm_mips_ixx_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *(__u8 *)(map->map_priv_1 + adr) = d; -} - -void cstm_mips_ixx_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *(__u16 *)(map->map_priv_1 + adr) = d; -} - -void cstm_mips_ixx_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *(__u32 *)(map->map_priv_1 + adr) = d; -} - -void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) #define CC_GCR 0xB4013818 @@ -97,51 +55,47 @@ void cstm_mips_ixx_copy_to(struct map_info *map, unsigned long to, const void *f #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) { - if (vpp) { -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) - __u16 data; - __u8 data1; - static u8 first = 1; - - // Set GPIO port B pin3 to high - data = *(__u16 *)(CC_GPBCR); - data = (data & 0xff0f) | 0x0040; - *(__u16 *)CC_GPBCR = data; - *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) | 0x08; - if (first) { - first = 0; - /* need to have this delay for first - enabling vpp after powerup */ - udelay(40); + 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 (!vpp_count++) { + __u16 data; + __u8 data1; + static u8 first = 1; + + // Set GPIO port B pin3 to high + data = *(__u16 *)(CC_GPBCR); + data = (data & 0xff0f) | 0x0040; + *(__u16 *)CC_GPBCR = data; + *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) | 0x08; + if (first) { + first = 0; + /* need to have this delay for first + enabling vpp after powerup */ + udelay(40); + } + } + } else { + if (!--vpp_count) { + __u16 data; + + // Set GPIO port B pin3 to high + data = *(__u16 *)(CC_GPBCR); + data = (data & 0xff3f) | 0x0040; + *(__u16 *)CC_GPBCR = data; + *(__u8 *)CC_GPBDR = (*(__u8*)CC_GPBDR) & 0xf7; + } } -#endif /* CONFIG_MIPS_ITE8172 */ - } - else { -#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) - __u16 data; - - // Set GPIO port B pin3 to high - data = *(__u16 *)(CC_GPBCR); - 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 = { - .read8 = cstm_mips_ixx_read8, - .read16 = cstm_mips_ixx_read16, - .read32 = cstm_mips_ixx_read32, - .copy_from = cstm_mips_ixx_copy_from, - .write8 = cstm_mips_ixx_write8, - .write16 = cstm_mips_ixx_write16, - .write32 = cstm_mips_ixx_write32, - .copy_to = cstm_mips_ixx_copy_to, - .set_vpp = cstm_mips_ixx_set_vpp, -}; +#endif /* board and partition description */ @@ -170,8 +124,9 @@ const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = 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 + .name = "main partition ", + .size = 0x02000000, // 128 x 2 x 128k byte sectors + .offset = 0, }, }, }; @@ -191,8 +146,9 @@ const struct cstm_mips_ixx_info cstm_mips_ixx_board_desc[PHYSMAP_NUMBER] = static struct mtd_partition cstm_mips_ixx_partitions[PHYSMAP_NUMBER][MAX_PHYSMAP_PARTITIONS] = { { { - .name = "main partition", - .size = CONFIG_MTD_CSTM_MIPS_IXX_LEN, + .name = "main partition", + .size = CONFIG_MTD_CSTM_MIPS_IXX_LEN, + .offset = 0, }, }, }; @@ -209,17 +165,24 @@ int __init init_cstm_mips_ixx(void) /* Initialize mapping */ for (i=0;i<PHYSMAP_NUMBER;i++) { - printk(KERN_NOTICE "cstm_mips_ixx flash device: %lx at %lx\n", cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr); - memcpy((char *)&cstm_mips_ixx_map[i],(char *)&basic_cstm_mips_ixx_map,sizeof(struct map_info)); - cstm_mips_ixx_map[i].map_priv_1 = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size); - if (!cstm_mips_ixx_map[i].map_priv_1) { + printk(KERN_NOTICE "cstm_mips_ixx flash device: 0x%lx at 0x%lx\n", + cstm_mips_ixx_board_desc[i].window_size, cstm_mips_ixx_board_desc[i].window_addr); + + + cstm_mips_ixx_map[i].phys = cstm_mips_ixx_board_desc[i].window_addr; + cstm_mips_ixx_map[i].virt = (unsigned long)ioremap(cstm_mips_ixx_board_desc[i].window_addr, cstm_mips_ixx_board_desc[i].window_size); + if (!cstm_mips_ixx_map[i].virt) { printk(KERN_WARNING "Failed to ioremap\n"); return -EIO; } cstm_mips_ixx_map[i].name = cstm_mips_ixx_board_desc[i].name; cstm_mips_ixx_map[i].size = cstm_mips_ixx_board_desc[i].window_size; cstm_mips_ixx_map[i].buswidth = cstm_mips_ixx_board_desc[i].buswidth; - //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].map_priv_1)); +#if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) + cstm_mips_ixx_map[i].set_vpp = cstm_mips_ixx_set_vpp; +#endif + simple_map_init(&cstm_mips_ixx_map[i]); + //printk(KERN_NOTICE "cstm_mips_ixx: ioremap is %x\n",(unsigned int)(cstm_mips_ixx_map[i].virt)); } #if defined(CONFIG_MIPS_ITE8172) || defined(CONFIG_MIPS_IVR) @@ -237,7 +200,7 @@ int __init init_cstm_mips_ixx(void) printk(KERN_NOTICE "cstm_mips_ixx %d jedec: mymtd is %x\n",i,(unsigned int)mymtd); } if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; cstm_mips_ixx_map[i].map_priv_2 = (unsigned long)mymtd; add_mtd_partitions(mymtd, parts, cstm_mips_ixx_board_desc[i].num_partitions); @@ -259,9 +222,9 @@ static void __exit cleanup_cstm_mips_ixx(void) 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 --git a/drivers/mtd/maps/dbox2-flash.c b/drivers/mtd/maps/dbox2-flash.c index 59fea4335193..6565afb17d2f 100644 --- a/drivers/mtd/maps/dbox2-flash.c +++ b/drivers/mtd/maps/dbox2-flash.c @@ -1,12 +1,13 @@ /* - * $Id: dbox2-flash.c,v 1.4 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: dbox2-flash.c,v 1.9 2003/05/21 12:45:18 dwmw2 Exp $ * - * Nokia / Sagem D-Box 2 flash driver + * D-Box 2 flash driver */ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -18,24 +19,40 @@ * device. */ static struct mtd_partition partition_info[]= { { - .name = "BR bootloader", /* raw */ - .size = 128 * 1024, - .mask_flags = MTD_WRITEABLE + .name = "BR bootloader", + .size = 128 * 1024, + .offset = 0, + .mask_flags = MTD_WRITEABLE }, { - .name = "PPC bootloader", /* flfs */ - .size = 128 * 1024, - .offset = MTDPART_OFS_APPEND, + .name = "flfs (ppcboot)", + .size = 128 * 1024, + .offset = MTDPART_OFS_APPEND, + .mask_flags = 0 }, { - .name = "Kernel", /* idxfs */ - .size = 768 * 1024, - .offset = MTDPART_OFS_APPEND, + .name = "root (cramfs)", + .size = 7040 * 1024, + .offset = MTDPART_OFS_APPEND, + .mask_flags = 0 }, { - .name = "System", /* jffs */ - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, + .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 } }; @@ -46,72 +63,24 @@ static struct mtd_partition partition_info[]= { 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 + .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); @@ -123,7 +92,7 @@ int __init init_dbox2_flash(void) } if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; /* Create MTD devices for each partition. */ add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); @@ -131,7 +100,7 @@ int __init init_dbox2_flash(void) return 0; } - iounmap((void *)dbox2_flash_map.map_priv_1); + iounmap((void *)dbox2_flash_map.virt); return -ENXIO; } @@ -141,9 +110,9 @@ static void __exit cleanup_dbox2_flash(void) 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; } } @@ -152,5 +121,5 @@ module_exit(cleanup_dbox2_flash); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>"); -MODULE_DESCRIPTION("MTD map driver for Nokia/Sagem D-Box 2 board"); +MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>, Bastian Blank <waldi@tuxbox.org>, Alexander Wild <wild@te-elektronik.com>"); +MODULE_DESCRIPTION("MTD map driver for D-Box 2 board"); diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c index aa5978d92a9e..8230cc5cd780 100644 --- a/drivers/mtd/maps/dc21285.c +++ b/drivers/mtd/maps/dc21285.c @@ -5,12 +5,13 @@ * * This code is GPL * - * $Id: dc21285.c,v 1.9 2002/10/14 12:22:10 rmk Exp $ + * $Id: dc21285.c,v 1.15 2003/05/21 12:45:18 dwmw2 Exp $ */ #include <linux/config.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -92,28 +93,42 @@ void dc21285_copy_to(struct map_info *map, unsigned long to, const void *from, s } 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 **); -extern int parse_cmdline_partitions(struct mtd_info *master, - struct mtd_partition **pparts, - const char *mtd_id); - +#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: @@ -142,50 +157,19 @@ int __init init_dc21285(void) mymtd = do_map_probe("cfi_probe", &dc21285_map); if (mymtd) { int nrparts = 0; - const char *part_type = NULL; - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; /* partition fixup */ - do { -#ifdef CONFIG_MTD_CMDLINE_PARTS - nrparts = parse_cmdline_partitions(mymtd, &dc21285_parts, "dc21285"); - if (nrparts > 0) { - part_type = "command line"; - break; - } -#endif -#ifdef CONFIG_MTD_REDBOOT_PARTS - nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); - if (nrparts > 0) { - part_type = "RedBoot"; - break; - } -#endif - } while (0); +#ifdef CONFIG_MTD_PARTITIONS + nrparts = parse_mtd_partitions(mymtd, probes, &dc21285_parts, (void *)0); if (nrparts > 0) { add_mtd_partitions(mymtd, dc21285_parts, nrparts); - printk(KERN_NOTICE "DC21285 using %s partition " - "definition\n", part_type); - } else if (nrparts == 0) { - printk(KERN_NOTICE "DC21285 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; } @@ -195,17 +179,16 @@ int __init init_dc21285(void) static void __exit cleanup_dc21285(void) { - if (mymtd) { - 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) +#ifdef CONFIG_MTD_PARTITIONS + if (dc21285_parts) { + del_mtd_partitions(mymtd); kfree(dc21285_parts); + } else +#endif + del_mtd_device(mymtd); + + map_destroy(mymtd); + iounmap((void *)dc21285_map.map_priv_1); } module_init(init_dc21285); diff --git a/drivers/mtd/maps/dilnetpc.c b/drivers/mtd/maps/dilnetpc.c new file mode 100644 index 000000000000..d7521b39c946 --- /dev/null +++ b/drivers/mtd/maps/dilnetpc.c @@ -0,0 +1,495 @@ +/* dilnetpc.c -- MTD map driver for SSV DIL/Net PC Boards "DNP" and "ADNP" + * + * 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: 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 + * board: DNP/1486 and ADNP/1486. The DNP version has 2 megs of flash + * ROM (Intel 28F016S3) and 8 megs of DRAM, the ADNP version has 4 megs + * flash and 16 megs of RAM. + * For details, see http://www.ssv-embedded.de/ssv/pc104/p169.htm + * and http://www.ssv-embedded.de/ssv/pc104/p170.htm + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/concat.h> + +/* +** 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). +** +** Therefore, the BIOS blocks must never be erased or written to +** except by people who know exactly what they are doing (e.g. +** to install a BIOS update). These partitions are marked read-only +** by default, but can be made read/write by undefining +** DNPC_BIOS_BLOCKS_WRITEPROTECTED: +*/ +#define DNPC_BIOS_BLOCKS_WRITEPROTECTED + +/* +** The ID string (in ROM) is checked to determine whether we +** are running on a DNP/1486 or ADNP/1486 +*/ +#define BIOSID_BASE 0x000fe100 + +#define ID_DNPC "DNP1486" +#define ID_ADNP "ADNP1486" + +/* +** Address where the flash should appear in CPU space +*/ +#define FLASH_BASE 0x2000000 + +/* +** Chip Setup and Control (CSC) indexed register space +*/ +#define CSC_INDEX 0x22 +#define CSC_DATA 0x23 + +#define CSC_MMSWAR 0x30 /* MMS window C-F attributes register */ +#define CSC_MMSWDSR 0x31 /* MMS window C-F device select register */ + +#define CSC_RBWR 0xa7 /* GPIO Read-Back/Write Register B */ + +#define CSC_CR 0xd0 /* internal I/O device disable/Echo */ + /* Z-bus/configuration register */ + +#define CSC_PCCMDCR 0xf1 /* PC card mode and DMA control register */ + + +/* +** PC Card indexed register space: +*/ + +#define PCC_INDEX 0x3e0 +#define PCC_DATA 0x3e1 + +#define PCC_AWER_B 0x46 /* Socket B Address Window enable register */ +#define PCC_MWSAR_1_Lo 0x58 /* memory window 1 start address low register */ +#define PCC_MWSAR_1_Hi 0x59 /* memory window 1 start address high register */ +#define PCC_MWEAR_1_Lo 0x5A /* memory window 1 stop address low register */ +#define PCC_MWEAR_1_Hi 0x5B /* memory window 1 stop address high register */ +#define PCC_MWAOR_1_Lo 0x5C /* memory window 1 address offset low register */ +#define PCC_MWAOR_1_Hi 0x5D /* memory window 1 address offset high register */ + + +/* +** Access to SC4x0's Chip Setup and Control (CSC) +** and PC Card (PCC) indexed registers: +*/ +static inline void setcsc(int reg, unsigned char data) +{ + outb(reg, CSC_INDEX); + outb(data, CSC_DATA); +} + +static inline unsigned char getcsc(int reg) +{ + outb(reg, CSC_INDEX); + return(inb(CSC_DATA)); +} + +static inline void setpcc(int reg, unsigned char data) +{ + outb(reg, PCC_INDEX); + outb(data, PCC_DATA); +} + +static inline unsigned char getpcc(int reg) +{ + outb(reg, PCC_INDEX); + return(inb(PCC_DATA)); +} + + +/* +************************************************************ +** Enable access to DIL/NetPC's flash by mapping it into +** the SC4x0's MMS Window C. +************************************************************ +*/ +static void dnpc_map_flash(unsigned long flash_base, unsigned long flash_size) +{ + unsigned long flash_end = flash_base + flash_size - 1; + + /* + ** enable setup of MMS windows C-F: + */ + /* - enable PC Card indexed register space */ + setcsc(CSC_CR, getcsc(CSC_CR) | 0x2); + /* - set PC Card controller to operate in standard mode */ + setcsc(CSC_PCCMDCR, getcsc(CSC_PCCMDCR) & ~1); + + /* + ** Program base address and end address of window + ** where the flash ROM should appear in CPU address space + */ + setpcc(PCC_MWSAR_1_Lo, (flash_base >> 12) & 0xff); + setpcc(PCC_MWSAR_1_Hi, (flash_base >> 20) & 0x3f); + setpcc(PCC_MWEAR_1_Lo, (flash_end >> 12) & 0xff); + setpcc(PCC_MWEAR_1_Hi, (flash_end >> 20) & 0x3f); + + /* program offset of first flash location to appear in this window (0) */ + setpcc(PCC_MWAOR_1_Lo, ((0 - flash_base) >> 12) & 0xff); + setpcc(PCC_MWAOR_1_Hi, ((0 - flash_base)>> 20) & 0x3f); + + /* set attributes for MMS window C: non-cacheable, write-enabled */ + setcsc(CSC_MMSWAR, getcsc(CSC_MMSWAR) & ~0x11); + + /* select physical device ROMCS0 (i.e. flash) for MMS Window C */ + setcsc(CSC_MMSWDSR, getcsc(CSC_MMSWDSR) & ~0x03); + + /* enable memory window 1 */ + setpcc(PCC_AWER_B, getpcc(PCC_AWER_B) | 0x02); + + /* now disable PC Card indexed register space again */ + setcsc(CSC_CR, getcsc(CSC_CR) & ~0x2); +} + + +/* +************************************************************ +** Disable access to DIL/NetPC's flash by mapping it into +** the SC4x0's MMS Window C. +************************************************************ +*/ +static void dnpc_unmap_flash(void) +{ + /* - enable PC Card indexed register space */ + setcsc(CSC_CR, getcsc(CSC_CR) | 0x2); + + /* disable memory window 1 */ + setpcc(PCC_AWER_B, getpcc(PCC_AWER_B) & ~0x02); + + /* now disable PC Card indexed register space again */ + setcsc(CSC_CR, getcsc(CSC_CR) & ~0x2); +} + + + +/* +************************************************************ +** Enable/Disable VPP to write to flash +************************************************************ +*/ + +static spinlock_t dnpc_spin = SPIN_LOCK_UNLOCKED; +static int vpp_counter = 0; +/* +** This is what has to be done for the DNP board .. +*/ +static void dnp_set_vpp(struct map_info *not_used, int on) +{ + spin_lock_irq(&dnpc_spin); + + if (on) + { + if(++vpp_counter == 1) + setcsc(CSC_RBWR, getcsc(CSC_RBWR) & ~0x4); + } + else + { + if(--vpp_counter == 0) + setcsc(CSC_RBWR, getcsc(CSC_RBWR) | 0x4); + else if(vpp_counter < 0) + BUG(); + } + spin_unlock_irq(&dnpc_spin); +} + +/* +** .. and this the ADNP version: +*/ +static void adnp_set_vpp(struct map_info *not_used, int on) +{ + spin_lock_irq(&dnpc_spin); + + if (on) + { + if(++vpp_counter == 1) + setcsc(CSC_RBWR, getcsc(CSC_RBWR) & ~0x8); + } + else + { + if(--vpp_counter == 0) + setcsc(CSC_RBWR, getcsc(CSC_RBWR) | 0x8); + else if(vpp_counter < 0) + BUG(); + } + spin_unlock_irq(&dnpc_spin); +} + + + +#define DNP_WINDOW_SIZE 0x00200000 /* DNP flash size is 2MiB */ +#define ADNP_WINDOW_SIZE 0x00400000 /* ADNP flash size is 4MiB */ +#define WINDOW_ADDR FLASH_BASE + +static struct map_info dnpc_map = { + .name = "ADNP Flash Bank", + .size = ADNP_WINDOW_SIZE, + .buswidth = 1, + .set_vpp = adnp_set_vpp, + .phys = WINDOW_ADDR +}; + +/* +** The layout of the flash is somewhat "strange": +** +** 1. 960 KiB (15 blocks) : Space for ROM Bootloader and user data +** 2. 64 KiB (1 block) : System BIOS +** 3. 960 KiB (15 blocks) : User Data (DNP model) or +** 3. 3008 KiB (47 blocks) : User Data (ADNP model) +** 4. 64 KiB (1 block) : System BIOS Entry +*/ + +static struct mtd_partition partition_info[]= +{ + { + .name = "ADNP boot", + .offset = 0, + .size = 0xf0000, + }, + { + .name = "ADNP system BIOS", + .offset = MTDPART_OFS_NXTBLK, + .size = 0x10000, +#ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED + .mask_flags = MTD_WRITEABLE, +#endif + }, + { + .name = "ADNP file system", + .offset = MTDPART_OFS_NXTBLK, + .size = 0x2f0000, + }, + { + .name = "ADNP system BIOS entry", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, +#ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED + .mask_flags = MTD_WRITEABLE, +#endif + }, +}; + +#define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) + +static struct mtd_info *mymtd; +static struct mtd_info *lowlvl_parts[NUM_PARTITIONS]; +static struct mtd_info *merged_mtd; + +/* +** "Highlevel" partition info: +** +** Using the MTD concat layer, we can re-arrange partitions to our +** liking: we construct a virtual MTD device by concatenating the +** partitions, specifying the sequence such that the boot block +** is immediately followed by the filesystem block (i.e. the stupid +** system BIOS block is mapped to a different place). When re-partitioning +** this concatenated MTD device, we can set the boot block size to +** an arbitrary (though erase block aligned) value i.e. not one that +** is dictated by the flash's physical layout. We can thus set the +** boot block to be e.g. 64 KB (which is fully sufficient if we want +** to boot an etherboot image) or to -say- 1.5 MB if we want to boot +** a large kernel image. In all cases, the remainder of the flash +** is available as file system space. +*/ + +static struct mtd_partition higlvl_partition_info[]= +{ + { + .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 system BIOS + BIOS Entry", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, +#ifdef DNPC_BIOS_BLOCKS_WRITEPROTECTED + .mask_flags = MTD_WRITEABLE, +#endif + }, +}; + +#define NUM_HIGHLVL_PARTITIONS (sizeof(higlvl_partition_info)/sizeof(partition_info[0])) + + +static int dnp_adnp_probe(void) +{ + char *biosid, rc = -1; + + biosid = (char*)ioremap(BIOSID_BASE, 16); + if(biosid) + { + if(!strcmp(biosid, ID_DNPC)) + rc = 1; /* this is a DNPC */ + else if(!strcmp(biosid, ID_ADNP)) + rc = 0; /* this is a ADNPC */ + } + iounmap((void *)biosid); + return(rc); +} + + +static int __init init_dnpc(void) +{ + int is_dnp; + + /* + ** determine hardware (DNP/ADNP/invalid) + */ + if((is_dnp = dnp_adnp_probe()) < 0) + return -ENXIO; + + /* + ** Things are set up for ADNP by default + ** -> modify all that needs to be different for DNP + */ + if(is_dnp) + { /* + ** Adjust window size, select correct set_vpp function. + ** The partitioning scheme is identical on both DNP + ** and ADNP except for the size of the third partition. + */ + int i; + dnpc_map.size = DNP_WINDOW_SIZE; + dnpc_map.set_vpp = dnp_set_vpp; + partition_info[2].size = 0xf0000; + + /* + ** increment all string pointers so the leading 'A' gets skipped, + ** thus turning all occurrences of "ADNP ..." into "DNP ..." + */ + ++dnpc_map.name; + for(i = 0; i < NUM_PARTITIONS; i++) + ++partition_info[i].name; + higlvl_partition_info[1].size = DNP_WINDOW_SIZE - + CONFIG_MTD_DILNETPC_BOOTSIZE - 0x20000; + for(i = 0; i < NUM_HIGHLVL_PARTITIONS; i++) + ++higlvl_partition_info[i].name; + } + + printk(KERN_NOTICE "DIL/Net %s flash: 0x%lx at 0x%lx\n", + is_dnp ? "DNPC" : "ADNP", dnpc_map.size, dnpc_map.phys); + + dnpc_map.virt = (unsigned long)ioremap_nocache(dnpc_map.phys, dnpc_map.size); + + dnpc_map_flash(dnpc_map.phys, dnpc_map.size); + + 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.virt); + + mymtd = do_map_probe("jedec_probe", &dnpc_map); + + if (!mymtd) + mymtd = do_map_probe("cfi_probe", &dnpc_map); + + /* + ** If flash probes fail, try to make flashes accessible + ** at least as ROM. Ajust erasesize in this case since + ** the default one (128M) will break our partitioning + */ + if (!mymtd) + if((mymtd = do_map_probe("map_rom", &dnpc_map))) + mymtd->erasesize = 0x10000; + + if (!mymtd) { + iounmap((void *)dnpc_map.virt); + return -ENXIO; + } + + mymtd->owner = THIS_MODULE; + + /* + ** Supply pointers to lowlvl_parts[] array to add_mtd_partitions() + ** -> add_mtd_partitions() will _not_ register MTD devices for + ** the partitions, but will instead store pointers to the MTD + ** objects it creates into our lowlvl_parts[] array. + ** NOTE: we arrange the pointers such that the sequence of the + ** partitions gets re-arranged: partition #2 follows + ** partition #0. + */ + partition_info[0].mtdp = &lowlvl_parts[0]; + partition_info[1].mtdp = &lowlvl_parts[2]; + partition_info[2].mtdp = &lowlvl_parts[1]; + partition_info[3].mtdp = &lowlvl_parts[3]; + + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + + /* + ** now create a virtual MTD device by concatenating the for partitions + ** (in the sequence given by the lowlvl_parts[] array. + */ + merged_mtd = mtd_concat_create(lowlvl_parts, NUM_PARTITIONS, "(A)DNP Flash Concatenated"); + if(merged_mtd) + { /* + ** now partition the new device the way we want it. This time, + ** we do not supply mtd pointers in higlvl_partition_info, so + ** add_mtd_partitions() will register the devices. + */ + add_mtd_partitions(merged_mtd, higlvl_partition_info, NUM_HIGHLVL_PARTITIONS); + } + + return 0; +} + +static void __exit cleanup_dnpc(void) +{ + if(merged_mtd) { + del_mtd_partitions(merged_mtd); + mtd_concat_destroy(merged_mtd); + } + + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (dnpc_map.virt) { + iounmap((void *)dnpc_map.virt); + dnpc_unmap_flash(); + dnpc_map.virt = 0; + } +} + +module_init(init_dnpc); +module_exit(cleanup_dnpc); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Sysgo Real-Time Solutions GmbH"); +MODULE_DESCRIPTION("MTD map driver for SSV DIL/NetPC DNP & ADNP"); diff --git a/drivers/mtd/maps/ebony.c b/drivers/mtd/maps/ebony.c new file mode 100644 index 000000000000..cb28518159b0 --- /dev/null +++ b/drivers/mtd/maps/ebony.c @@ -0,0 +1,166 @@ +/* + * $Id: ebony.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ + * + * Mapping for Ebony user flash + * + * Matt Porter <mporter@mvista.com> + * + * Copyright 2002 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License as published by the + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/config.h> +#include <asm/io.h> +#include <asm/ibm440.h> +#include <platforms/ebony.h> + +static struct mtd_info *flash; + +static struct map_info ebony_small_map = { + .name = "Ebony small flash", + .size = EBONY_SMALL_FLASH_SIZE, + .buswidth = 1, +}; + +static struct map_info ebony_large_map = { + .name = "Ebony large flash", + .size = EBONY_LARGE_FLASH_SIZE, + .buswidth = 1, +}; + +static struct mtd_partition ebony_small_partitions[] = { + { + .name = "OpenBIOS", + .offset = 0x0, + .size = 0x80000, + } +}; + +static struct mtd_partition ebony_large_partitions[] = { + { + .name = "fs", + .offset = 0, + .size = 0x380000, + }, + { + .name = "firmware", + .offset = 0x380000, + .size = 0x80000, + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +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, + NB_OF(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, + NB_OF(ebony_large_partitions)); + } else { + printk("map probe failed for flash\n"); + return -ENXIO; + } + + return 0; +} + +static void __exit cleanup_ebony(void) +{ + if (flash) { + del_mtd_partitions(flash); + map_destroy(flash); + } + + if (ebony_small_map.virt) { + iounmap((void *)ebony_small_map.virt); + ebony_small_map.virt = 0; + } + + if (ebony_large_map.virt) { + iounmap((void *)ebony_large_map.virt); + ebony_large_map.virt = 0; + } +} + +module_init(init_ebony); +module_exit(cleanup_ebony); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Matt Porter <mporter@mvista.com>"); +MODULE_DESCRIPTION("MTD map and partitions for IBM 440GP Ebony boards"); diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c index 929a89f2ec8a..346ae2e4dd95 100644 --- a/drivers/mtd/maps/edb7312.c +++ b/drivers/mtd/maps/edb7312.c @@ -1,5 +1,5 @@ /* - * $Id: edb7312.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ + * $Id: edb7312.c,v 1.8 2003/05/21 12:45:18 dwmw2 Exp $ * * Handle mapping of the NOR flash on Cogent EDB7312 boards * @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -35,61 +36,11 @@ static struct mtd_info *mymtd; -__u8 edb7312nor_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 edb7312nor_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 edb7312nor_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void edb7312nor_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void edb7312nor_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void edb7312nor_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void edb7312nor_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void edb7312nor_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} - struct map_info edb7312nor_map = { - .name = "NOR flash on EDB7312", - .size = WINDOW_SIZE, - .buswidth = BUSWIDTH, - .read8 = edb7312nor_read8, - .read16 = edb7312nor_read16, - .read32 = edb7312nor_read32, - .copy_from = edb7312nor_copy_from, - .write8 = edb7312nor_write8, - .write16 = edb7312nor_write16, - .write32 = edb7312nor_write32, - .copy_to = edb7312nor_copy_to + .name = "NOR flash on EDB7312", + .size = WINDOW_SIZE, + .buswidth = BUSWIDTH, + .phys = WINDOW_ADDR, }; #ifdef CONFIG_MTD_PARTITIONS @@ -97,7 +48,8 @@ struct map_info edb7312nor_map = { /* * MTD partitioning stuff */ -static struct mtd_partition static_partitions[3] = { +static struct mtd_partition static_partitions[3] = +{ { .name = "ARMboot", .size = 0x40000, @@ -116,12 +68,7 @@ static struct mtd_partition static_partitions[3] = { }; #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 @@ -136,13 +83,15 @@ int __init init_edb7312nor(void) 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; @@ -150,14 +99,13 @@ int __init init_edb7312nor(void) 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; @@ -177,7 +125,7 @@ int __init init_edb7312nor(void) return 0; } - iounmap((void *)edb7312nor_map.map_priv_1); + iounmap((void *)edb7312nor_map.virt); return -ENXIO; } @@ -187,9 +135,9 @@ static void __exit cleanup_edb7312nor(void) 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 --git a/drivers/mtd/maps/elan-104nc.c b/drivers/mtd/maps/elan-104nc.c index 0c0308cc3337..6caf4f8f877a 100644 --- a/drivers/mtd/maps/elan-104nc.c +++ b/drivers/mtd/maps/elan-104nc.c @@ -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.12 2001/10/02 15:05:14 dwmw2 Exp $ + $Id: elan-104nc.c,v 1.17 2003/05/21 15:15:07 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. @@ -27,7 +27,7 @@ The flash is accessed as follows: 16 bit I/O port (0x22) for some sort of paging. -The single flash device is divided into 3 partition which appear as separate +The single flash device is divided into 3 partition which appear as seperate MTD devices. Linux thinks that the I/O port is used by the PIC and hence check_region() will @@ -40,6 +40,7 @@ always fail. So we don't do it. I just hope it doesn't break anything. #include <asm/io.h> #include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> #define WINDOW_START 0xb0000 @@ -58,20 +59,15 @@ static spinlock_t elan_104nc_spin = SPIN_LOCK_UNLOCKED; /* 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 = "ELAN-104NC flash boot partition", - .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, - } +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 } }; #define NUM_PARTITIONS (sizeof(partition_info)/sizeof(partition_info[0])) @@ -200,20 +196,20 @@ static void elan_104nc_copy_to(struct map_info *map, unsigned long to, const voi } static struct map_info elan_104nc_map = { - .name = "ELAN-104NC flash", - .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 + .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 }; /* MTD device for all of the flash. */ @@ -266,7 +262,7 @@ int __init init_elan_104nc(void) 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 --git a/drivers/mtd/maps/epxa10db-flash.c b/drivers/mtd/maps/epxa10db-flash.c index c3f9580c5b2f..5b83d5fd828e 100644 --- a/drivers/mtd/maps/epxa10db-flash.c +++ b/drivers/mtd/maps/epxa10db-flash.c @@ -5,7 +5,7 @@ * Copyright (C) 2001 Altera Corporation * Copyright (C) 2001 Red Hat, Inc. * - * $Id: epxa10db-flash.c,v 1.4 2002/08/22 10:46:19 cdavies Exp $ + * $Id: epxa10db-flash.c,v 1.10 2003/05/21 12:45:18 dwmw2 Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -43,87 +44,38 @@ static struct mtd_partition *parts; 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 @@ static int __init epxa_mtd_init(void) } } -#ifdef CONFIG_MTD_REDBOOT_PARTS - nr_parts = parse_redboot_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 -#ifdef CONFIG_MTD_AFS_PARTS - nr_parts = parse_afs_partitions(mymtd, &parts); - - 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 @@ static void __exit epxa_mtd_cleanup(void) 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; } } @@ -199,12 +142,12 @@ static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_pa printk("Using default partitions for %s\n",BOARD_NAME); npartitions=1; - parts = kmalloc(npartitions*sizeof(*parts)+strlen(name)+1, GFP_KERNEL); + parts = kmalloc(npartitions*sizeof(*parts)+strlen(name), GFP_KERNEL); + memzero(parts,npartitions*sizeof(*parts)+strlen(name)); if (!parts) { ret = -ENOMEM; goto out; } - memzero(parts,npartitions*sizeof(*parts)+strlen(name)); i=0; names = (char *)&parts[npartitions]; parts[i].name = names; @@ -218,11 +161,10 @@ static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_pa parts[i].size = FLASH_SIZE-0x00180000; parts[i].offset = 0x00180000; #endif - ret = npartitions; out: *pparts = parts; - return ret; + return npartitions; } diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c index 9bddde021ce7..0cc00cdd2951 100644 --- a/drivers/mtd/maps/fortunet.c +++ b/drivers/mtd/maps/fortunet.c @@ -1,11 +1,12 @@ /* fortunet.c memory map * - * $Id: fortunet.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ + * $Id: fortunet.c,v 1.6 2003/05/21 12:45:18 dwmw2 Exp $ */ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -23,7 +24,7 @@ struct map_region { - int window_addr_phyical; + int window_addr_physical; int altbuswidth; struct map_info map_info; struct mtd_info *mymtd; @@ -37,57 +38,10 @@ static int map_regions_set[MAX_NUM_REGIONS] = {0,0,0,0}; 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 @@ static int __init MTD_New_Region(char *line) 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 @@ static int __init MTD_New_Region(char *line) 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 @@ static int __init MTD_New_Region(char *line) 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 @@ static int __init MTD_New_Partion(char *line) } 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 @@ static int __init MTD_New_Partion(char *line) } __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 @@ int __init init_fortunet(void) { 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 @@ int __init init_fortunet(void) 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 @@ int __init init_fortunet(void) 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 @@ static void __exit cleanup_fortunet(void) 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 --git a/drivers/mtd/maps/h720x-flash.c b/drivers/mtd/maps/h720x-flash.c new file mode 100644 index 000000000000..3a0c58db2981 --- /dev/null +++ b/drivers/mtd/maps/h720x-flash.c @@ -0,0 +1,142 @@ +/* + * Flash memory access on Hynix GMS30C7201/HMS30C7202 based + * evaluation boards + * + * (C) 2002 Jungjun Kim <jungjun.kim@hynix.com> + * 2003 Thomas Gleixner <tglx@linutronix.de> +*/ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/errno.h> +#include <linux/slab.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <asm/hardware.h> +#include <asm/io.h> + +static struct mtd_info *mymtd; + +static struct map_info h720x_map = { + .name = "H720X", + .buswidth = 4, + .size = FLASH_SIZE, + .phys = FLASH_PHYS, +}; + +static struct mtd_partition h720x_partitions[] = { + { + .name = "ArMon", + .size = 0x00080000, + .offset = 0, + .mask_flags = MTD_WRITEABLE + },{ + .name = "Env", + .size = 0x00040000, + .offset = 0x00080000, + .mask_flags = MTD_WRITEABLE + },{ + .name = "Kernel", + .size = 0x00180000, + .offset = 0x000c0000, + .mask_flags = MTD_WRITEABLE + },{ + .name = "Ramdisk", + .size = 0x00400000, + .offset = 0x00240000, + .mask_flags = MTD_WRITEABLE + },{ + .name = "jffs2", + .size = MTDPART_SIZ_FULL, + .offset = MTDPART_OFS_APPEND + } +}; + +#define NUM_PARTITIONS (sizeof(h720x_partitions)/sizeof(h720x_partitions[0])) + +static int nr_mtd_parts; +static struct mtd_partition *mtd_parts; +static const char *probes[] = { "cmdlinepart", NULL }; + +/* + * Initialize FLASH support + */ +int __init h720x_mtd_init(void) +{ + + char *part_type = NULL; + + h720x_map.virt = (unsigned long)ioremap(FLASH_PHYS, FLASH_SIZE); + + if (!h720x_map.virt) { + printk(KERN_ERR "H720x-MTD: ioremap failed\n"); + return -EIO; + } + + simple_map_init(&h720x_map); + + // Probe for flash buswidth 4 + printk (KERN_INFO "H720x-MTD probing 32bit FLASH\n"); + mymtd = do_map_probe("cfi_probe", &h720x_map); + if (!mymtd) { + printk (KERN_INFO "H720x-MTD probing 16bit FLASH\n"); + // Probe for buswidth 2 + h720x_map.buswidth = 2; + mymtd = do_map_probe("cfi_probe", &h720x_map); + } + + if (mymtd) { + mymtd->owner = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS + nr_mtd_parts = parse_mtd_partitions(mymtd, probes, &mtd_parts, 0); + if (nr_mtd_parts > 0) + part_type = "command line"; +#endif + if (nr_mtd_parts <= 0) { + mtd_parts = h720x_partitions; + nr_mtd_parts = NUM_PARTITIONS; + part_type = "builtin"; + } + printk(KERN_INFO "Using %s partition table\n", part_type); + add_mtd_partitions(mymtd, mtd_parts, nr_mtd_parts); + return 0; + } + + iounmap((void *)h720x_map.virt); + return -ENXIO; +} + +/* + * Cleanup + */ +static void __exit h720x_mtd_cleanup(void) +{ + + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + + /* Free partition info, if commandline partition was used */ + if (mtd_parts && (mtd_parts != h720x_partitions)) + kfree (mtd_parts); + + if (h720x_map.virt) { + iounmap((void *)h720x_map.virt); + h720x_map.virt = 0; + } +} + + +module_init(h720x_mtd_init); +module_exit(h720x_mtd_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); +MODULE_DESCRIPTION("MTD map driver for Hynix evaluation boards"); diff --git a/drivers/mtd/maps/ich2rom.c b/drivers/mtd/maps/ich2rom.c new file mode 100644 index 000000000000..e94d646527b9 --- /dev/null +++ b/drivers/mtd/maps/ich2rom.c @@ -0,0 +1,316 @@ +/* + * ich2rom.c + * + * Normal mappings of chips in physical memory + * $Id: ich2rom.c,v 1.7 2003/05/21 12:45:18 dwmw2 Exp $ + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/config.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> + +#define RESERVE_MEM_REGION 0 + +#define ICH2_FWH_REGION_START 0xFF000000UL +#define ICH2_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 ich2rom_map_info { + struct map_info map; + struct mtd_info *mtd; + unsigned long window_addr; +}; + +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 + ICH2_FWH_REGION_START; +} + +static __u8 ich2rom_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(addr(map, ofs)); +} + +static __u16 ich2rom_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(addr(map, ofs)); +} + +static __u32 ich2rom_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(addr(map, ofs)); +} + +static void ich2rom_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, addr(map, from), len); +} + +static void ich2rom_write8(struct map_info *map, __u8 d, unsigned long ofs) +{ + __raw_writeb(d, addr(map,ofs)); + mb(); +} + +static void ich2rom_write16(struct map_info *map, __u16 d, unsigned long ofs) +{ + __raw_writew(d, addr(map, ofs)); + mb(); +} + +static void ich2rom_write32(struct map_info *map, __u32 d, unsigned long ofs) +{ + __raw_writel(d, addr(map, ofs)); + mb(); +} + +static void ich2rom_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(addr(map, to), from, len); +} + +static struct ich2rom_map_info ich2rom_map = { + .map = { + .name = "ICH2 rom", + .phys = NO_XIP, + .size = 0, + .buswidth = 1, + .read8 = ich2rom_read8, + .read16 = ich2rom_read16, + .read32 = ich2rom_read32, + .copy_from = ich2rom_copy_from, + .write8 = ich2rom_write8, + .write16 = ich2rom_write16, + .write32 = ich2rom_write32, + .copy_to = ich2rom_copy_to, + /* Firmware hubs only use vpp when being programmed + * in a factory setting. So in place programming + * needs to use a different method. + */ + }, + .mtd = 0, + .window_addr = 0, +}; + +enum fwh_lock_state { + FWH_DENY_WRITE = 1, + FWH_IMMUTABLE = 2, + FWH_DENY_READ = 4, +}; + +static int ich2rom_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 ich2rom_lock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + return ich2rom_set_lock_state(mtd, ofs, len, FWH_DENY_WRITE); +} + +static int ich2rom_unlock(struct mtd_info *mtd, loff_t ofs, size_t len) +{ + return ich2rom_set_lock_state(mtd, ofs, len, 0); +} + +static int __devinit ich2rom_init_one (struct pci_dev *pdev, + const struct pci_device_id *ent) +{ + u16 word; + struct ich2rom_map_info *info = &ich2rom_map; + unsigned long map_size; + + /* For now I just handle the ich2 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 ICH2 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. + */ + +#if RESERVE_MEM_REGION + /* Some boards have this reserved and I haven't found a good work + * around to say I know what I'm doing! + */ + if (!request_mem_region(ICH2_FWH_REGION_START, ICH2_FWH_REGION_SIZE, "ich2rom")) { + printk(KERN_ERR "ich2rom: cannot reserve rom window\n"); + goto err_out_none; + } +#endif /* RESERVE_MEM_REGION */ + + /* 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 "ich2rom: firmware access control, I can't enable writes\n"); + goto err_out_none; + } + pci_write_config_word(pdev, BIOS_CNTL, word | 1); + + + /* Map the firmware hub into my address space. */ + /* Does this use to much virtual address space? */ + info->window_addr = (unsigned long)ioremap( + ICH2_FWH_REGION_START, ICH2_FWH_REGION_SIZE); + if (!info->window_addr) { + printk(KERN_ERR "Failed to ioremap\n"); + goto err_out_free_mmio_region; + } + + /* 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 = ICH2_FWH_REGION_SIZE; + while(!info->mtd && (map_size > 0)) { + info->map.size = map_size; + info->mtd = do_map_probe("jedec_probe", &ich2rom_map.map); + map_size -= 512*1024; + } + if (!info->mtd) { + goto err_out_iounmap; + } + /* I know I can only be a firmware hub here so put + * in the special lock and unlock routines. + */ + info->mtd->lock = ich2rom_lock; + info->mtd->unlock = ich2rom_unlock; + + info->mtd->owner = THIS_MODULE; + add_mtd_device(info->mtd); + return 0; + +err_out_iounmap: + iounmap((void *)(info->window_addr)); +err_out_free_mmio_region: +#if RESERVE_MEM_REGION + release_mem_region(ICH2_FWH_REGION_START, ICH2_FWH_REGION_SIZE); +#endif +err_out_none: + return -ENODEV; +} + + +static void __devexit ich2rom_remove_one (struct pci_dev *pdev) +{ + struct ich2rom_map_info *info = &ich2rom_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(ICH2_FWH_REGION_START, ICH2_FWH_REGION_SIZE); +#endif +} + +static struct pci_device_id ich2rom_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, }, + { 0, }, +}; + +MODULE_DEVICE_TABLE(pci, ich2rom_pci_tbl); + +#if 0 +static struct pci_driver ich2rom_driver = { + .name = "ich2rom", + .id_table = ich2rom_pci_tbl, + .probe = ich2rom_init_one, + .remove = ich2rom_remove_one, +}; +#endif + +static struct pci_dev *mydev; +int __init init_ich2rom(void) +{ + struct pci_dev *pdev; + struct pci_device_id *id; + pdev = 0; + for(id = ich2rom_pci_tbl; id->vendor; id++) { + pdev = pci_find_device(id->vendor, id->device, 0); + if (pdev) { + break; + } + } + if (pdev) { + mydev = pdev; + return ich2rom_init_one(pdev, &ich2rom_pci_tbl[0]); + } + return -ENXIO; +#if 0 + return pci_module_init(&ich2rom_driver); +#endif +} + +static void __exit cleanup_ich2rom(void) +{ + ich2rom_remove_one(mydev); +} + +module_init(init_ich2rom); +module_exit(cleanup_ich2rom); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>"); +MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICH2 southbridge"); diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c index 4395677b4337..9c3785aa67c1 100644 --- a/drivers/mtd/maps/impa7.c +++ b/drivers/mtd/maps/impa7.c @@ -1,5 +1,5 @@ /* - * $Id: impa7.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ + * $Id: impa7.c,v 1.8 2003/05/21 12:45:18 dwmw2 Exp $ * * Handle mapping of the NOR flash on implementa A7 boards * @@ -13,6 +13,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -37,75 +38,17 @@ static struct mtd_info *impa7_mtd[NUM_FLASHBANKS] = { 0 }; -__u8 impa7_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 impa7_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 impa7_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void impa7_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void impa7_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void impa7_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void impa7_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void impa7_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} static struct map_info impa7_map[NUM_FLASHBANKS] = { { - .name = "impA7 NOR Flash Bank #0", - .size = WINDOW_SIZE0, - .buswidth = BUSWIDTH, - .read8 = impa7_read8, - .read16 = impa7_read16, - .read32 = impa7_read32, - .copy_from = impa7_copy_from, - .write8 = impa7_write8, - .write16 = impa7_write16, - .write32 = impa7_write32, - .copy_to = impa7_copy_to + .name = "impA7 NOR Flash Bank #0", + .size = WINDOW_SIZE0, + .buswidth = BUSWIDTH, }, { - .name = "impA7 NOR Flash Bank #1", - .size = WINDOW_SIZE1, - .buswidth = BUSWIDTH, - .read8 = impa7_read8, - .read16 = impa7_read16, - .read32 = impa7_read32, - .copy_from = impa7_copy_from, - .write8 = impa7_write8, - .write16 = impa7_write16, - .write32 = impa7_write32, - .copy_to = impa7_copy_to + .name = "impA7 NOR Flash Bank #1", + .size = WINDOW_SIZE1, + .buswidth = BUSWIDTH, }, }; @@ -114,26 +57,22 @@ static struct map_info impa7_map[NUM_FLASHBANKS] = { /* * MTD partitioning stuff */ -static struct mtd_partition static_partitions[] = { +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])) -#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) { @@ -142,8 +81,8 @@ int __init init_impa7(void) const char *part_type = 0; int i; static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = { - { .addr = WINDOW_ADDR0, .size = WINDOW_SIZE0 }, - { .addr = WINDOW_ADDR1, .size = WINDOW_SIZE1 }, + { WINDOW_ADDR0, WINDOW_SIZE0 }, + { WINDOW_ADDR1, WINDOW_SIZE1 }, }; char mtdid[10]; int devicesfound = 0; @@ -152,13 +91,15 @@ int __init init_impa7(void) { printk(KERN_NOTICE MSG_PREFIX "probing 0x%08lx at 0x%08lx\n", pt[i].size, pt[i].addr); - impa7_map[i].map_priv_1 = (unsigned long) - ioremap(pt[i].addr, pt[i].size); - if (!impa7_map[i].map_priv_1) { + impa7_map[i].phys = pt[i].addr; + impa7_map[i].virt = (unsigned long) + ioremap(pt[i].addr, pt[i].size); + if (!impa7_map[i].virt) { printk(MSG_PREFIX "failed to ioremap\n"); return -EIO; } + simple_map_init(&impa7_map[i]); impa7_mtd[i] = 0; type = rom_probe_types; @@ -168,15 +109,14 @@ int __init init_impa7(void) if (impa7_mtd[i]) { - impa7_mtd[i]->module = THIS_MODULE; + impa7_mtd[i]->owner = THIS_MODULE; add_mtd_device(impa7_mtd[i]); 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); + mtd_parts_nb = parse_mtd_partitions(impa7_mtd[i], + probes, + &mtd_parts, + 0); if (mtd_parts_nb > 0) part_type = "command line"; #endif @@ -202,7 +142,7 @@ int __init init_impa7(void) #endif } else - iounmap((void *)impa7_map[i].map_priv_1); + iounmap((void *)impa7_map[i].virt); } return devicesfound == 0 ? -ENXIO : 0; } @@ -217,10 +157,10 @@ static void __exit cleanup_impa7(void) del_mtd_device(impa7_mtd[i]); map_destroy(impa7_mtd[i]); } - if (impa7_map[i].map_priv_1) + if (impa7_map[i].virt) { - iounmap((void *)impa7_map[i].map_priv_1); - impa7_map[i].map_priv_1 = 0; + iounmap((void *)impa7_map[i].virt); + impa7_map[i].virt = 0; } } } diff --git a/drivers/mtd/maps/integrator-flash.c b/drivers/mtd/maps/integrator-flash.c index 6afe145986d5..7a85a89c86fc 100644 --- a/drivers/mtd/maps/integrator-flash.c +++ b/drivers/mtd/maps/integrator-flash.c @@ -21,7 +21,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.12 2003/05/20 20:59:30 dwmw2 Exp $ ======================================================================*/ @@ -41,8 +41,6 @@ #include <asm/io.h> #include <asm/system.h> -extern int parse_afs_partitions(struct mtd_info *, struct mtd_partition **); - // board specific stuff - sorry, it should be in arch/arm/mach-*. #ifdef CONFIG_ARCH_INTEGRATOR @@ -153,62 +151,17 @@ 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); -} - -static __u32 armflash_read32(struct map_info *map, unsigned long ofs) -{ - return readl(ofs + map->map_priv_2); -} - -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); -} - -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 void armflash_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - writel(d, adr + map->map_priv_2); -} - -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); -} 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, + .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) { @@ -222,7 +175,9 @@ static int __init armflash_cfi_init(void *base, u_int size) */ armflash_map.size = size; armflash_map.buswidth = 4; - armflash_map.map_priv_2 = (unsigned long) base; + armflash_map.virt = (unsigned long) base; + + simple_map_init(&armflash_map); /* * Also, the CFI layer automatically works out what size @@ -233,9 +188,9 @@ static int __init armflash_cfi_init(void *base, u_int size) if (!mtd) return -ENXIO; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; - ret = parse_afs_partitions(mtd, &parts); + ret = parse_mtd_partitions(mtd, probes, &parts, (void *)0); if (ret > 0) { ret = add_mtd_partitions(mtd, parts, ret); if (ret) @@ -290,7 +245,7 @@ out: static void __exit armflash_exit(void) { armflash_cfi_exit(); - iounmap((void *)armflash_map.map_priv_2); + iounmap((void *)armflash_map.virt); release_mem_region(FLASH_BASE, FLASH_SIZE); armflash_flash_exit(); } diff --git a/drivers/mtd/maps/iq80310.c b/drivers/mtd/maps/iq80310.c index 81fba4828be6..d4eddc54f10b 100644 --- a/drivers/mtd/maps/iq80310.c +++ b/drivers/mtd/maps/iq80310.c @@ -1,5 +1,5 @@ /* - * $Id: iq80310.c,v 1.9 2002/01/01 22:45:02 rmk Exp $ + * $Id: iq80310.c,v 1.16 2003/05/21 15:15:07 dwmw2 Exp $ * * Mapping for the Intel XScale IQ80310 evaluation board * @@ -14,6 +14,8 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> +#include <linux/slab.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -26,81 +28,32 @@ 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, - .mask_flags = MTD_WRITEABLE /* force read-only */ - }, - { - .name = "Kernel", - .size = 0x000a0000, - .offset = 0x00080000, - }, - { - .name = "Filesystem", - .size = 0x00600000, - .offset = 0x00120000 - }, - { - .name = "RedBoot", - .size = 0x000e0000, - .offset = 0x00720000, - .mask_flags = MTD_WRITEABLE + .name = "Firmware", + .size = 0x00080000, + .offset = 0, + .mask_flags = MTD_WRITEABLE /* force read-only */ + },{ + .name = "Kernel", + .size = 0x000a0000, + .offset = 0x00080000, + },{ + .name = "Filesystem", + .size = 0x00600000, + .offset = 0x00120000 + },{ + .name = "RedBoot", + .size = 0x000e0000, + .offset = 0x00720000, + .mask_flags = MTD_WRITEABLE } }; @@ -108,38 +61,33 @@ static struct mtd_partition iq80310_partitions[4] = { 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"; - parsed_nr_parts = ret; - } - } -#endif + if (ret > 0) + parsed_nr_parts = ret; if (parsed_nr_parts > 0) { parts = parsed_parts; @@ -148,7 +96,6 @@ static int __init init_iq80310(void) parts = iq80310_partitions; nb_parts = NB_OF(iq80310_partitions); } - printk(KERN_NOTICE "Using %s partition definition\n", part_type); add_mtd_partitions(mymtd, parts, nb_parts); return 0; } @@ -161,8 +108,8 @@ static void __exit cleanup_iq80310(void) 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 --git a/drivers/mtd/maps/iq80321.c b/drivers/mtd/maps/iq80321.c deleted file mode 100644 index 06f1c9d8e9cd..000000000000 --- a/drivers/mtd/maps/iq80321.c +++ /dev/null @@ -1,169 +0,0 @@ -/* - * $Id: iq80321.c,v 1.1.2.1 2003/03/04 16:14:31 ejc Exp $ - * - * Mapping for the Intel XScale IQ80321 evaluation board - * - * Author: Rory Bolt <rorybolt@pacbell.net> - * Copyright: (C) 2002 Rory Bolt - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> -#include <asm/io.h> -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> -#include <linux/mtd/partitions.h> - - -#define WINDOW_ADDR 0xf0000000 -#define WINDOW_SIZE 8*1024*1024 -#define BUSWIDTH 1 - -static struct mtd_info *mymtd; - -static __u8 iq80321_read8(struct map_info *map, unsigned long ofs) -{ - return *(__u8 *)(map->map_priv_1 + ofs); -} - -static __u16 iq80321_read16(struct map_info *map, unsigned long ofs) -{ - return *(__u16 *)(map->map_priv_1 + ofs); -} - -static __u32 iq80321_read32(struct map_info *map, unsigned long ofs) -{ - return *(__u32 *)(map->map_priv_1 + ofs); -} - -static void iq80321_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 iq80321_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *(__u8 *)(map->map_priv_1 + adr) = d; -} - -static void iq80321_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *(__u16 *)(map->map_priv_1 + adr) = d; -} - -static void iq80321_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *(__u32 *)(map->map_priv_1 + adr) = d; -} - -static void iq80321_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 iq80321_map = { - .name = "IQ80321 flash", - .size = WINDOW_SIZE, - .buswidth = BUSWIDTH, - .read8 = iq80321_read8, - .read16 = iq80321_read16, - .read32 = iq80321_read32, - .copy_from = iq80321_copy_from, - .write8 = iq80321_write8, - .write16 = iq80321_write16, - .write32 = iq80321_write32, - .copy_to = iq80321_copy_to -}; - -static struct mtd_partition iq80321_partitions[4] = { - { - .name = "Firmware", - .size = 0x00080000, - .offset = 0, - .mask_flags = MTD_WRITEABLE /* force read-only */ - },{ - .name = "Kernel", - .size = 0x000a0000, - .offset = 0x00080000, - },{ - .name = "Filesystem", - .size = 0x00600000, - .offset = 0x00120000 - },{ - .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 int __init init_iq80321(void) -{ - struct mtd_partition *parts; - int nb_parts = 0; - int parsed_nr_parts = 0; - char *part_type = "Static"; - - iq80321_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); - if (!iq80321_map.map_priv_1) { - printk("Failed to ioremap\n"); - return -EIO; - } - mymtd = do_map_probe("cfi_probe", &iq80321_map); - if (!mymtd) { - iounmap((void *)iq80321_map.map_priv_1); - return -ENXIO; - } - mymtd->module = THIS_MODULE; - -#ifdef CONFIG_MTD_REDBOOT_PARTS - if (parsed_nr_parts == 0) { - int ret = parse_redboot_partitions(mymtd, &parsed_parts); - - if (ret > 0) { - part_type = "RedBoot"; - parsed_nr_parts = ret; - } - } -#endif - - if (parsed_nr_parts > 0) { - parts = parsed_parts; - nb_parts = parsed_nr_parts; - } else { - parts = iq80321_partitions; - nb_parts = NB_OF(iq80321_partitions); - } - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(mymtd, parts, nb_parts); - return 0; -} - -static void __exit cleanup_iq80321(void) -{ - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - if (parsed_parts) - kfree(parsed_parts); - } - if (iq80321_map.map_priv_1) - iounmap((void *)iq80321_map.map_priv_1); -} - -module_init(init_iq80321); -module_exit(cleanup_iq80321); -MODULE_LICENSE("GPL"); -MODULE_DESCRIPTION("MTD map driver for Intel XScale IQ80321 evaluation board"); diff --git a/drivers/mtd/maps/l440gx.c b/drivers/mtd/maps/l440gx.c index 064eec463d4c..2e410ef554e6 100644 --- a/drivers/mtd/maps/l440gx.c +++ b/drivers/mtd/maps/l440gx.c @@ -1,142 +1,143 @@ /* - * $Id: l440gx.c,v 1.7 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: l440gx.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $ * * BIOS Flash chip on Intel 440GX board. + * + * Bugs this currently does not work under linuxBIOS. */ #include <linux/module.h> #include <linux/pci.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/config.h> +#define PIIXE_IOBASE_RESOURCE 11 #define WINDOW_ADDR 0xfff00000 #define WINDOW_SIZE 0x00100000 #define BUSWIDTH 1 -#define IOBASE 0xc00 +static u32 iobase; +#define IOBASE iobase #define TRIBUF_PORT (IOBASE+0x37) #define VPP_PORT (IOBASE+0x28) 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) { unsigned long l; l = inl(VPP_PORT); - l = vpp?(l | 1):(l & ~1); + if (vpp) { + l |= 1; + } else { + l &= ~1; + } outl(l, VPP_PORT); } 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, - .set_vpp = l440gx_set_vpp + .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 +#endif }; static int __init init_l440gx(void) { - struct pci_dev *dev; - unsigned char b; - __u16 w; + struct pci_dev *dev, *pm_dev; + struct resource *pm_iobase; + __u16 word; - dev = pci_find_device(PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371AB_0, - NULL); + dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_0, NULL); - if (!dev) { + pm_dev = pci_find_device(PCI_VENDOR_ID_INTEL, + PCI_DEVICE_ID_INTEL_82371AB_3, NULL); + + if (!dev || !pm_dev) { printk(KERN_NOTICE "L440GX flash mapping: failed to find PIIX4 ISA bridge, cannot continue\n"); return -ENODEV; } + l440gx_map.virt = (unsigned long)ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE); - l440gx_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); - - if (!l440gx_map.map_priv_1) { - printk("Failed to ioremap L440GX flash region\n"); + if (!l440gx_map.virt) { + printk(KERN_WARNING "Failed to ioremap L440GX flash region\n"); return -ENOMEM; } + 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 + * driver but for the moment I'm content with getting the + * allocation correct. + */ + pm_iobase = &pm_dev->resource[PIIXE_IOBASE_RESOURCE]; + if (!(pm_iobase->flags & IORESOURCE_IO)) { + pm_iobase->name = "pm iobase"; + pm_iobase->start = 0; + pm_iobase->end = 63; + pm_iobase->flags = IORESOURCE_IO; + + /* Put the current value in the resource */ + pci_read_config_dword(pm_dev, 0x40, &iobase); + iobase &= ~1; + pm_iobase->start += iobase & ~1; + pm_iobase->end += iobase & ~1; + + /* 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.virt); + return -ENXIO; + } + } + /* Set the iobase */ + iobase = pm_iobase->start; + pci_write_config_dword(pm_dev, 0x40, iobase | 1); + /* Set XBCS# */ - pci_read_config_word(dev, 0x4e, &w); - w |= 0x4; - pci_write_config_word(dev, 0x4e, w); + pci_read_config_word(dev, 0x4e, &word); + word |= 0x4; + pci_write_config_word(dev, 0x4e, word); + + /* Supply write voltage to the chip */ + l440gx_set_vpp(&l440gx_map, 1); /* Enable the gate on the WE line */ - b = inb(TRIBUF_PORT); - b |= 1; - outb(b, TRIBUF_PORT); + outb(inb(TRIBUF_PORT) & ~1, TRIBUF_PORT); printk(KERN_NOTICE "Enabled WE line to L440GX BIOS flash chip.\n"); - mymtd = do_map_probe("jedec", &l440gx_map); + mymtd = do_map_probe("jedec_probe", &l440gx_map); if (!mymtd) { printk(KERN_NOTICE "JEDEC probe on BIOS chip failed. Using ROM\n"); 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; } @@ -145,7 +146,7 @@ static void __exit cleanup_l440gx(void) del_mtd_device(mymtd); map_destroy(mymtd); - iounmap((void *)l440gx_map.map_priv_1); + iounmap((void *)l440gx_map.virt); } module_init(init_l440gx); diff --git a/drivers/mtd/maps/lasat.c b/drivers/mtd/maps/lasat.c new file mode 100644 index 000000000000..36a14d6a441a --- /dev/null +++ b/drivers/mtd/maps/lasat.c @@ -0,0 +1,94 @@ +/* + * Flash device on lasat 100 and 200 boards + * + * Presumably (C) 2002 Brian Murphy <brian@murphy.dk> or whoever he + * works for. + * + * 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.5 2003/05/21 12:45:19 dwmw2 Exp $ + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/config.h> +#include <asm/lasat/lasat.h> +#include <asm/lasat/lasat_mtd.h> + +static struct mtd_info *mymtd; + +static struct map_info sp_map = { + .name = "SP flash", + .buswidth = 4, +}; + +static struct mtd_partition partition_info[LASAT_MTD_LAST]; +static char *lasat_mtd_partnames[] = {"Bootloader", "Service", "Normal", "Filesystem", "Config"}; + +static int __init init_sp(void) +{ + int i; + /* this does not play well with the old flash code which + * protects and uprotects the flash when necessary */ + /* FIXME: Implement set_vpp() */ + printk(KERN_NOTICE "Unprotecting flash\n"); + *lasat_misc->flash_wp_reg |= 1 << lasat_misc->flash_wp_bit; + + sp_map.virt = lasat_flash_partition_start(LASAT_MTD_BOOTLOADER); + sp_map.phys = virt_to_phys(sp_map.virt); + sp_map.size = lasat_board_info.li_flash_size; + + simple_map_init(&sp_map); + + printk(KERN_NOTICE "sp flash device: %lx at %lx\n", + sp_map.size, sp_map.phys); + + 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) { + u32 size, offset = 0; + + mymtd->owner = THIS_MODULE; + + for (i=0; i < LASAT_MTD_LAST; i++) { + size = lasat_flash_partition_size(i); + partition_info[i].size = size; + partition_info[i].offset = offset; + offset += size; + } + + add_mtd_partitions( mymtd, partition_info, LASAT_MTD_LAST ); + return 0; + } + + return -ENXIO; +} + +static void __exit cleanup_sp(void) +{ + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + } + if (sp_map.virt) { + sp_map.virt = 0; + } +} + +module_init(init_sp); +module_exit(cleanup_sp); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Brian Murphy <brian@murphy.dk>"); +MODULE_DESCRIPTION("Lasat Safepipe/Masquerade MTD map driver"); diff --git a/drivers/mtd/maps/lubbock-flash.c b/drivers/mtd/maps/lubbock-flash.c new file mode 100644 index 000000000000..facc7e0252f6 --- /dev/null +++ b/drivers/mtd/maps/lubbock-flash.c @@ -0,0 +1,153 @@ +/* + * $Id: lubbock-flash.c,v 1.8 2003/05/21 12:45:19 dwmw2 Exp $ + * + * Map driver for the Lubbock developer platform. + * + * Author: Nicolas Pitre + * Copyright: (C) 2001 MontaVista Software Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <asm/hardware.h> + + +#define ROM_ADDR 0x00000000 +#define FLASH_ADDR 0x04000000 + +#define WINDOW_SIZE 64*1024*1024 + +static struct map_info lubbock_maps[2] = { { + .size = WINDOW_SIZE, + .phys = 0x00000000, +}, { + .size = WINDOW_SIZE, + .phys = 0x04000000, +} }; + +static struct mtd_partition lubbock_partitions[] = { + { + .name = "Bootloader", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE /* force read-only */ + },{ + .name = "Kernel", + .size = 0x00100000, + .offset = 0x00040000, + },{ + .name = "Filesystem", + .size = MTDPART_SIZ_FULL, + .offset = 0x00140000 + } +}; + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +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, NB_OF(lubbock_partitions)); + } else { + printk("Registering %s as whole device\n", lubbock_maps[i].name); + add_mtd_device(mymtds[i]); + } + } + return 0; +} + +static void __exit cleanup_lubbock(void) +{ + int i; + for (i = 0; i < 2; i++) { + if (!mymtds[i]) + continue; + + if (nr_parsed_parts[i] || !i) + del_mtd_partitions(mymtds[i]); + else + del_mtd_device(mymtds[i]); + + map_destroy(mymtds[i]); + iounmap((void *)lubbock_maps[i].virt); + + if (parsed_parts[i]) + kfree(parsed_parts[i]); + } +} + +module_init(init_lubbock); +module_exit(cleanup_lubbock); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); +MODULE_DESCRIPTION("MTD map driver for Intel Lubbock"); diff --git a/drivers/mtd/maps/map_funcs.c b/drivers/mtd/maps/map_funcs.c new file mode 100644 index 000000000000..61a04d79876d --- /dev/null +++ b/drivers/mtd/maps/map_funcs.c @@ -0,0 +1,94 @@ +/* + * $Id: map_funcs.c,v 1.2 2003/05/21 15:15:07 dwmw2 Exp $ + * + * Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS + * is enabled. + */ + +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/config.h> +#include <linux/types.h> +#include <linux/string.h> +#include <asm/io.h> + +#include <linux/mtd/map.h> + +static u8 simple_map_read8(struct map_info *map, unsigned long ofs) +{ + return __raw_readb(map->virt + ofs); +} + +static u16 simple_map_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->virt + ofs); +} + +static u32 simple_map_read32(struct map_info *map, unsigned long ofs) +{ + return __raw_readl(map->virt + ofs); +} + +static u64 simple_map_read64(struct map_info *map, unsigned long ofs) +{ +#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ + BUG(); + return 0; +#else + return __raw_readll(map->virt + ofs); +#endif +} + +static void simple_map_write8(struct map_info *map, u8 datum, unsigned long ofs) +{ + __raw_writeb(datum, map->virt + ofs); + mb(); +} + +static void simple_map_write16(struct map_info *map, u16 datum, unsigned long ofs) +{ + __raw_writew(datum, map->virt + ofs); + mb(); +} + +static void simple_map_write32(struct map_info *map, u32 datum, unsigned long ofs) +{ + __raw_writel(datum, map->virt + ofs); + mb(); +} + +static void simple_map_write64(struct map_info *map, u64 datum, unsigned long ofs) +{ +#ifndef CONFIG_MTD_CFI_B8 /* 64-bit mappings */ + BUG(); +#else + __raw_writell(datum, map->virt + ofs); + mb(); +#endif /* CFI_B8 */ +} + +static void simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + memcpy_fromio(to, map->virt + from, len); +} + +static void simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + memcpy_toio(map->virt + to, from, len); +} + +void simple_map_init(struct map_info *map) +{ + map->read8 = simple_map_read8; + map->read16 = simple_map_read16; + map->read32 = simple_map_read32; + map->read64 = simple_map_read64; + map->write8 = simple_map_write8; + map->write16 = simple_map_write16; + map->write32 = simple_map_write32; + map->write64 = simple_map_write64; + map->copy_from = simple_map_copy_from; + map->copy_to = simple_map_copy_to; +} + +EXPORT_SYMBOL(simple_map_init); diff --git a/drivers/mtd/maps/mbx860.c b/drivers/mtd/maps/mbx860.c new file mode 100644 index 000000000000..5adbec8807c3 --- /dev/null +++ b/drivers/mtd/maps/mbx860.c @@ -0,0 +1,100 @@ +/* + * $Id: mbx860.c,v 1.5 2003/05/21 12:45:19 dwmw2 Exp $ + * + * Handle mapping of the flash on MBX860 boards + * + * Author: Anton Todorov + * Copyright: (C) 2001 Emness Technology + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + + +#define WINDOW_ADDR 0xfe000000 +#define WINDOW_SIZE 0x00200000 + +/* Flash / Partition sizing */ +#define MAX_SIZE_KiB 8192 +#define BOOT_PARTITION_SIZE_KiB 512 +#define KERNEL_PARTITION_SIZE_KiB 5632 +#define APP_PARTITION_SIZE_KiB 2048 + +#define NUM_PARTITIONS 3 + +/* 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 = "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; + +struct map_info mbx_map = { + .name = "MBX flash", + .size = WINDOW_SIZE, + .phys = WINDOW_ADDR, + .buswidth = 4, +}; + +int __init init_mbx(void) +{ + 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.virt) { + printk("Failed to ioremap\n"); + return -EIO; + } + simple_map_init(&mbx_map); + + mymtd = do_map_probe("jedec_probe", &mbx_map); + if (mymtd) { + mymtd->owner = THIS_MODULE; + add_mtd_device(mymtd); + add_mtd_partitions(mymtd, partition_info, NUM_PARTITIONS); + return 0; + } + + iounmap((void *)mbx_map.virt); + return -ENXIO; +} + +static void __exit cleanup_mbx(void) +{ + if (mymtd) { + del_mtd_device(mymtd); + map_destroy(mymtd); + } + if (mbx_map.virt) { + iounmap((void *)mbx_map.virt); + mbx_map.virt = 0; + } +} + +module_init(init_mbx); +module_exit(cleanup_mbx); + +MODULE_AUTHOR("Anton Todorov <a.todorov@emness.com>"); +MODULE_DESCRIPTION("MTD map driver for Motorola MBX860 board"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/netsc520.c b/drivers/mtd/maps/netsc520.c index a2df8ac12055..caaac71cc77a 100644 --- a/drivers/mtd/maps/netsc520.c +++ b/drivers/mtd/maps/netsc520.c @@ -3,7 +3,7 @@ * Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com) * based on sc520cdp.c by Sysgo Real-Time Solutions GmbH * - * $Id: netsc520.c,v 1.5 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: netsc520.c,v 1.9 2003/05/21 12:45:19 dwmw2 Exp $ * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -27,6 +27,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -50,94 +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", - .size = 0xc0000 - }, - { - .name = "NetSc520 Low BIOS", - .offset = 0xc0000, - .size = 0x40000 - }, - { - .name = "NetSc520 file system", - .offset = 0x100000, - .size = 0xe80000 - }, - { - .name = "NetSc520 High BIOS", - .offset = 0xf80000, - .size = 0x80000 - }, +static struct mtd_partition partition_info[]={ + { + .name = "NetSc520 boot kernel", + .offset = 0, + .size = 0xc0000 + }, + { + .name = "NetSc520 Low BIOS", + .offset = 0xc0000, + .size = 0x40000 + }, + { + .name = "NetSc520 file system", + .offset = 0x100000, + .size = 0xe80000 + }, + { + .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)) @@ -146,13 +94,16 @@ static struct mtd_info *mymtd; 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); @@ -160,11 +111,11 @@ static int __init init_netsc520(void) 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; } @@ -175,9 +126,9 @@ static void __exit cleanup_netsc520(void) 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 --git a/drivers/mtd/maps/nettel.c b/drivers/mtd/maps/nettel.c new file mode 100644 index 000000000000..53a9934fb736 --- /dev/null +++ b/drivers/mtd/maps/nettel.c @@ -0,0 +1,499 @@ +/****************************************************************************/ + +/* + * nettel.c -- mappings for NETtel/SecureEdge/SnapGear (x86) boards. + * + * (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com) + * (C) Copyright 2001-2002, SnapGear (www.snapgear.com) + * + * $Id: nettel.c,v 1.4 2003/05/20 20:59:30 dwmw2 Exp $ + */ + +/****************************************************************************/ + +#include <linux/module.h> +#include <linux/init.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> +#include <linux/mtd/cfi.h> +#include <linux/reboot.h> +#include <asm/io.h> + +/****************************************************************************/ + +#define INTEL_BUSWIDTH 1 +#define AMD_WINDOW_MAXSIZE 0x00200000 +#define AMD_BUSWIDTH 1 + +/* + * PAR masks and shifts, assuming 64K pages. + */ +#define SC520_PAR_ADDR_MASK 0x00003fff +#define SC520_PAR_ADDR_SHIFT 16 +#define SC520_PAR_TO_ADDR(par) \ + (((par)&SC520_PAR_ADDR_MASK) << SC520_PAR_ADDR_SHIFT) + +#define SC520_PAR_SIZE_MASK 0x01ffc000 +#define SC520_PAR_SIZE_SHIFT 2 +#define SC520_PAR_TO_SIZE(par) \ + ((((par)&SC520_PAR_SIZE_MASK) << SC520_PAR_SIZE_SHIFT) + (64*1024)) + +#define SC520_PAR(cs, addr, size) \ + ((cs) | \ + ((((size)-(64*1024)) >> SC520_PAR_SIZE_SHIFT) & SC520_PAR_SIZE_MASK) | \ + (((addr) >> SC520_PAR_ADDR_SHIFT) & SC520_PAR_ADDR_MASK)) + +#define SC520_PAR_BOOTCS 0x8a000000 +#define SC520_PAR_ROMCS1 0xaa000000 +#define SC520_PAR_ROMCS2 0xca000000 /* Cache disabled, 64K page */ + +static void *nettel_mmcrp = NULL; + +#ifdef CONFIG_MTD_CFI_INTELEXT +static struct mtd_info *intel_mtd; +#endif +static struct mtd_info *amd_mtd; + +/****************************************************************************/ + +/****************************************************************************/ + +#ifdef CONFIG_MTD_CFI_INTELEXT +static struct map_info nettel_intel_map = { + .name = "SnapGear Intel", + .size = 0, + .buswidth = INTEL_BUSWIDTH, +}; + +static struct mtd_partition nettel_intel_partitions[] = { + { + .name = "SnapGear kernel", + .offset = 0, + .size = 0x000e0000 + }, + { + .name = "SnapGear filesystem", + .offset = 0x00100000, + }, + { + .name = "SnapGear config", + .offset = 0x000e0000, + .size = 0x00020000 + }, + { + .name = "SnapGear Intel", + .offset = 0 + }, + { + .name = "SnapGear BIOS Config", + .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, +}; + +static struct mtd_partition nettel_amd_partitions[] = { + { + .name = "SnapGear BIOS config", + .offset = 0x000e0000, + .size = 0x00010000 + }, + { + .name = "SnapGear BIOS", + .offset = 0x000f0000, + .size = 0x00010000 + }, + { + .name = "SnapGear AMD", + .offset = 0 + }, + { + .name = "SnapGear high BIOS", + .offset = 0x001f0000, + .size = 0x00010000 + } +}; + +#define NUM_AMD_PARTITIONS \ + (sizeof(nettel_amd_partitions)/sizeof(nettel_amd_partitions[0])) + +/****************************************************************************/ + +#ifdef CONFIG_MTD_CFI_INTELEXT + +/* + * Set the Intel flash back to read mode since some old boot + * loaders don't. + */ +static int nettel_reboot_notifier(struct notifier_block *nb, unsigned long val, void *v) +{ + struct cfi_private *cfi = nettel_intel_map.fldrv_priv; + unsigned long b; + + /* Make sure all FLASH chips are put back into read mode */ + for (b = 0; (b < nettel_intel_partitions[3].size); b += 0x100000) { + cfi_send_gen_cmd(0xff, 0x55, b, &nettel_intel_map, cfi, + cfi->device_type, NULL); + } + return(NOTIFY_OK); +} + +static struct notifier_block nettel_notifier_block = { + nettel_reboot_notifier, NULL, 0 +}; + +/* + * Erase the configuration file system. + * Used to support the software reset button. + */ +static void nettel_erasecallback(struct erase_info *done) +{ + wait_queue_head_t *wait_q = (wait_queue_head_t *)done->priv; + wake_up(wait_q); +} + +static struct erase_info nettel_erase; + +int nettel_eraseconfig(void) +{ + struct mtd_info *mtd; + DECLARE_WAITQUEUE(wait, current); + wait_queue_head_t wait_q; + int ret; + + init_waitqueue_head(&wait_q); + mtd = get_mtd_device(NULL, 2); + if (mtd) { + nettel_erase.mtd = mtd; + nettel_erase.callback = nettel_erasecallback; + nettel_erase.callback = 0; + nettel_erase.addr = 0; + nettel_erase.len = mtd->size; + nettel_erase.priv = (u_long) &wait_q; + nettel_erase.priv = 0; + + set_current_state(TASK_INTERRUPTIBLE); + add_wait_queue(&wait_q, &wait); + + ret = MTD_ERASE(mtd, &nettel_erase); + if (ret) { + set_current_state(TASK_RUNNING); + remove_wait_queue(&wait_q, &wait); + put_mtd_device(mtd); + return(ret); + } + + schedule(); /* Wait for erase to finish. */ + remove_wait_queue(&wait_q, &wait); + + put_mtd_device(mtd); + } + + return(0); +} + +#else + +int nettel_eraseconfig(void) +{ + return(0); +} + +#endif + +/****************************************************************************/ + +int __init nettel_init(void) +{ + volatile unsigned long *amdpar; + unsigned long amdaddr, maxsize; + int num_amd_partitions=0; +#ifdef CONFIG_MTD_CFI_INTELEXT + volatile unsigned long *intel0par, *intel1par; + unsigned long orig_bootcspar, orig_romcs1par; + unsigned long intel0addr, intel0size; + unsigned long intel1addr, intel1size; + int intelboot, intel0cs, intel1cs; + int num_intel_partitions; +#endif + int rc = 0; + + nettel_mmcrp = (void *) ioremap_nocache(0xfffef000, 4096); + if (nettel_mmcrp == NULL) { + printk("SNAPGEAR: failed to disable MMCR cache??\n"); + return(-EIO); + } + + /* Set CPU clock to be 33.000MHz */ + *((unsigned char *) (nettel_mmcrp + 0xc64)) = 0x01; + + amdpar = (volatile unsigned long *) (nettel_mmcrp + 0xc4); + +#ifdef CONFIG_MTD_CFI_INTELEXT + intelboot = 0; + intel0cs = SC520_PAR_ROMCS1; + intel0par = (volatile unsigned long *) (nettel_mmcrp + 0xc0); + intel1cs = SC520_PAR_ROMCS2; + intel1par = (volatile unsigned long *) (nettel_mmcrp + 0xbc); + + /* + * Save the CS settings then ensure ROMCS1 and ROMCS2 are off, + * otherwise they might clash with where we try to map BOOTCS. + */ + orig_bootcspar = *amdpar; + orig_romcs1par = *intel0par; + *intel0par = 0; + *intel1par = 0; +#endif + + /* + * The first thing to do is determine if we have a separate + * boot FLASH device. Typically this is a small (1 to 2MB) + * AMD FLASH part. It seems that device size is about the + * only way to tell if this is the case... + */ + amdaddr = 0x20000000; + maxsize = AMD_WINDOW_MAXSIZE; + + *amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize); + __asm__ ("wbinvd"); + + nettel_amd_map.phys = amdaddr; + nettel_amd_map.virt = (unsigned long) + ioremap_nocache(amdaddr, maxsize); + 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->owner = THIS_MODULE; + + /* The high BIOS partition is only present for 2MB units */ + num_amd_partitions = NUM_AMD_PARTITIONS; + if (amd_mtd->size < AMD_WINDOW_MAXSIZE) + num_amd_partitions--; + /* Don't add the partition until after the primary INTEL's */ + +#ifdef CONFIG_MTD_CFI_INTELEXT + /* + * Map the Intel flash into memory after the AMD + * It has to start on a multiple of maxsize. + */ + maxsize = SC520_PAR_TO_SIZE(orig_romcs1par); + if (maxsize < (32 * 1024 * 1024)) + maxsize = (32 * 1024 * 1024); + intel0addr = amdaddr + maxsize; +#endif + } else { +#ifdef CONFIG_MTD_CFI_INTELEXT + /* INTEL boot FLASH */ + intelboot++; + + if (!orig_romcs1par) { + intel0cs = SC520_PAR_BOOTCS; + intel0par = (volatile unsigned long *) + (nettel_mmcrp + 0xc4); + intel1cs = SC520_PAR_ROMCS1; + intel1par = (volatile unsigned long *) + (nettel_mmcrp + 0xc0); + + intel0addr = SC520_PAR_TO_ADDR(orig_bootcspar); + maxsize = SC520_PAR_TO_SIZE(orig_bootcspar); + } else { + /* Kernel base is on ROMCS1, not BOOTCS */ + intel0cs = SC520_PAR_ROMCS1; + intel0par = (volatile unsigned long *) + (nettel_mmcrp + 0xc0); + intel1cs = SC520_PAR_BOOTCS; + intel1par = (volatile unsigned long *) + (nettel_mmcrp + 0xc4); + + intel0addr = SC520_PAR_TO_ADDR(orig_romcs1par); + maxsize = SC520_PAR_TO_SIZE(orig_romcs1par); + } + + /* Destroy useless AMD MTD mapping */ + amd_mtd = NULL; + iounmap((void *) nettel_amd_map.virt); + nettel_amd_map.virt = (unsigned long) NULL; +#else + /* Only AMD flash supported */ + return(-ENXIO); +#endif + } + +#ifdef CONFIG_MTD_CFI_INTELEXT + /* + * We have determined the INTEL FLASH configuration, so lets + * go ahead and probe for them now. + */ + + /* Set PAR to the maximum size */ + if (maxsize < (32 * 1024 * 1024)) + maxsize = (32 * 1024 * 1024); + *intel0par = SC520_PAR(intel0cs, intel0addr, maxsize); + + /* Turn other PAR off so the first probe doesn't find it */ + *intel1par = 0; + + /* Probe for the the size of the first Intel flash */ + nettel_intel_map.size = maxsize; + nettel_intel_map.phys = intel0addr; + nettel_intel_map.virt = (unsigned long) + ioremap_nocache(intel0addr, maxsize); + 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.virt); + return(-ENXIO); + } + + /* Set PAR to the detected size */ + intel0size = intel_mtd->size; + *intel0par = SC520_PAR(intel0cs, intel0addr, intel0size); + + /* + * Map second Intel FLASH right after first. Set its size to the + * same maxsize used for the first Intel FLASH. + */ + intel1addr = intel0addr + intel0size; + *intel1par = SC520_PAR(intel1cs, intel1addr, maxsize); + __asm__ ("wbinvd"); + + maxsize += intel0size; + + /* Delete the old map and probe again to do both chips */ + map_destroy(intel_mtd); + intel_mtd = NULL; + iounmap((void *) nettel_intel_map.virt); + + nettel_intel_map.size = maxsize; + nettel_intel_map.virt = (unsigned long) + ioremap_nocache(intel0addr, maxsize); + 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.virt); + return(-ENXIO); + } + + intel1size = intel_mtd->size - intel0size; + if (intel1size > 0) { + *intel1par = SC520_PAR(intel1cs, intel1addr, intel1size); + __asm__ ("wbinvd"); + } else { + *intel1par = 0; + } + + printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %dK\n", + (intel_mtd->size >> 10)); + + intel_mtd->owner = THIS_MODULE; + +#ifndef CONFIG_BLK_DEV_INITRD + ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 1); +#endif + + num_intel_partitions = sizeof(nettel_intel_partitions) / + sizeof(nettel_intel_partitions[0]); + + if (intelboot) { + /* + * Adjust offset and size of last boot partition. + * Must allow for BIOS region at end of FLASH. + */ + nettel_intel_partitions[1].size = (intel0size + intel1size) - + (1024*1024 + intel_mtd->erasesize); + nettel_intel_partitions[3].size = intel0size + intel1size; + nettel_intel_partitions[4].offset = + (intel0size + intel1size) - intel_mtd->erasesize; + nettel_intel_partitions[4].size = intel_mtd->erasesize; + nettel_intel_partitions[5].offset = + nettel_intel_partitions[4].offset; + nettel_intel_partitions[5].size = + nettel_intel_partitions[4].size; + } else { + /* No BIOS regions when AMD boot */ + num_intel_partitions -= 2; + } + rc = add_mtd_partitions(intel_mtd, nettel_intel_partitions, + num_intel_partitions); +#endif + + if (amd_mtd) { + rc = add_mtd_partitions(amd_mtd, nettel_amd_partitions, + num_amd_partitions); + } + +#ifdef CONFIG_MTD_CFI_INTELEXT + register_reboot_notifier(&nettel_notifier_block); +#endif + + return(rc); +} + +/****************************************************************************/ + +void __exit nettel_cleanup(void) +{ +#ifdef CONFIG_MTD_CFI_INTELEXT + unregister_reboot_notifier(&nettel_notifier_block); +#endif + if (amd_mtd) { + del_mtd_partitions(amd_mtd); + map_destroy(amd_mtd); + } + 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.virt) { + iounmap((void *)nettel_intel_map.virt); + nettel_intel_map.virt = 0; + } +#endif +} + +/****************************************************************************/ + +module_init(nettel_init); +module_exit(nettel_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>"); +MODULE_DESCRIPTION("SnapGear/SecureEdge FLASH support"); + +/****************************************************************************/ diff --git a/drivers/mtd/maps/nora.c b/drivers/mtd/maps/nora.c deleted file mode 100644 index 04c12214fa8b..000000000000 --- a/drivers/mtd/maps/nora.c +++ /dev/null @@ -1,203 +0,0 @@ -/* - * $Id: nora.c,v 1.21 2001/10/02 15:05:14 dwmw2 Exp $ - * - * This is so simple I love it. - */ - -#include <linux/module.h> -#include <linux/types.h> -#include <linux/kernel.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/map.h> - - -#define WINDOW_ADDR 0xd0000000 -#define WINDOW_SIZE 0x04000000 - -static struct mtd_info *mymtd; - -__u8 nora_read8(struct map_info *map, unsigned long ofs) -{ - return *(__u8 *)(WINDOW_ADDR + ofs); -} - -__u16 nora_read16(struct map_info *map, unsigned long ofs) -{ - return *(__u16 *)(WINDOW_ADDR + ofs); -} - -__u32 nora_read32(struct map_info *map, unsigned long ofs) -{ - return *(__u32 *)(WINDOW_ADDR + ofs); -} - -void nora_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy(to, (void *)(WINDOW_ADDR + from), len); -} - -void nora_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *(__u8 *)(WINDOW_ADDR + adr) = d; -} - -void nora_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *(__u16 *)(WINDOW_ADDR + adr) = d; -} - -void nora_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *(__u32 *)(WINDOW_ADDR + adr) = d; -} - -void nora_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 nora_map = { - .name = "NORA", - .size = WINDOW_SIZE, - .buswidth = 2, - .read8 = nora_read8, - .read16 = nora_read16, - .read32 = nora_read32, - .copy_from = nora_copy_from, - .write8 = nora_write8, - .write16 = nora_write16, - .write32 = nora_write32, - .copy_to = nora_copy_to -}; - - -static int nora_mtd_read (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf) -{ - return mymtd->read(mymtd, from + (unsigned long)mtd->priv, len, retlen, buf); -} - -static int nora_mtd_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) -{ - return mymtd->write(mymtd, to + (unsigned long)mtd->priv, len, retlen, buf); -} - -static int nora_mtd_erase (struct mtd_info *mtd, struct erase_info *instr) -{ - instr->addr += (unsigned long)mtd->priv; - return mymtd->erase(mymtd, instr); -} - -static void nora_mtd_sync (struct mtd_info *mtd) -{ - mymtd->sync(mymtd); -} - -static int nora_mtd_suspend (struct mtd_info *mtd) -{ - return mymtd->suspend(mymtd); -} - -static void nora_mtd_resume (struct mtd_info *mtd) -{ - mymtd->resume(mymtd); -} - - -static struct mtd_info nora_mtds[4] = { /* boot, kernel, ramdisk, fs */ - { - .type = MTD_NORFLASH, - .flags = MTD_CAP_NORFLASH, - .size = 0x60000, - .erasesize = 0x20000, - .name = "NORA boot firmware", - .module = THIS_MODULE, - .erase = nora_mtd_erase, - .read = nora_mtd_read, - .write = nora_mtd_write, - .suspend = nora_mtd_suspend, - .resume = nora_mtd_resume, - .sync = nora_mtd_sync, - }, - { - .type = MTD_NORFLASH, - .flags = MTD_CAP_NORFLASH, - .size = 0x0a0000, - .erasesize = 0x20000, - .name = "NORA kernel", - .module = THIS_MODULE, - .erase = nora_mtd_erase, - .read = nora_mtd_read, - .write = nora_mtd_write, - .suspend = nora_mtd_suspend, - .resume = nora_mtd_resume, - .sync = nora_mtd_sync, - .priv = (void *)0x60000 - }, - { - .type = MTD_NORFLASH, - .flags = MTD_CAP_NORFLASH, - .size = 0x900000, - .erasesize = 0x20000, - .name = "NORA root filesystem", - .module = THIS_MODULE, - .erase = nora_mtd_erase, - .read = nora_mtd_read, - .write = nora_mtd_write, - .suspend = nora_mtd_suspend, - .resume = nora_mtd_resume, - .sync = nora_mtd_sync, - .priv = (void *)0x100000 - }, - { - .type = MTD_NORFLASH, - .flags = MTD_CAP_NORFLASH, - .size = 0x1600000, - .erasesize = 0x20000, - .name = "NORA second filesystem", - .module = THIS_MODULE, - .erase = nora_mtd_erase, - .read = nora_mtd_read, - .write = nora_mtd_write, - .suspend = nora_mtd_suspend, - .resume = nora_mtd_resume, - .sync = nora_mtd_sync, - .priv = (void *)0xa00000 - } -}; - -int __init init_nora(void) -{ - printk(KERN_NOTICE "nora flash device: %x at %x\n", WINDOW_SIZE, WINDOW_ADDR); - - mymtd = do_map_probe("cfi_probe", &nora_map); - if (mymtd) { - mymtd->module = THIS_MODULE; - - add_mtd_device(&nora_mtds[2]); - add_mtd_device(&nora_mtds[0]); - add_mtd_device(&nora_mtds[1]); - add_mtd_device(&nora_mtds[3]); - return 0; - } - - return -ENXIO; -} - -static void __exit cleanup_nora(void) -{ - if (mymtd) { - del_mtd_device(&nora_mtds[3]); - del_mtd_device(&nora_mtds[1]); - del_mtd_device(&nora_mtds[0]); - del_mtd_device(&nora_mtds[2]); - map_destroy(mymtd); - } -} - -module_init(init_nora); -module_exit(cleanup_nora); - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>"); -MODULE_DESCRIPTION("MTD map driver for Nora board"); diff --git a/drivers/mtd/maps/ocelot.c b/drivers/mtd/maps/ocelot.c index ad92c425d18c..201f67a408f2 100644 --- a/drivers/mtd/maps/ocelot.c +++ b/drivers/mtd/maps/ocelot.c @@ -1,5 +1,5 @@ /* - * $Id: ocelot.c,v 1.6 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: ocelot.c,v 1.12 2003/05/21 12:45:19 dwmw2 Exp $ * * Flash on Momenco Ocelot */ @@ -7,6 +7,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -20,47 +21,23 @@ #define NVRAM_WINDOW_SIZE 0x00007FF0 #define NVRAM_BUSWIDTH 1 -extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); - static unsigned int cacheflush = 0; static struct mtd_info *flash_mtd; static struct mtd_info *nvram_mtd; -__u8 ocelot_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -void ocelot_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - cacheflush = 1; - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void ocelot_copy_from_cache(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - if (cacheflush) { - dma_cache_inv(map->map_priv_2, map->size); - cacheflush = 0; - } - memcpy_fromio(to, map->map_priv_1 + from, len); -} - -void ocelot_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +static void ocelot_ram_write(struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf) { - memcpy_fromio(to, map->map_priv_1 + from, len); -} + struct map_info *map = (struct map_info *)mtd->priv; + size_t done = 0; -void ocelot_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ /* If we use memcpy, it does word-wide writes. Even though we told the GT64120A that it's an 8-bit wide region, word-wide writes don't work. We end up just writing the first byte of the four to all four bytes. So we have this loop instead */ + *retlen = len; while(len) { - __raw_writeb(*(unsigned char *) from, map->map_priv_1 + to); + __raw_writeb(*(unsigned char *) from, map->virt + to); from++; to++; len--; @@ -70,24 +47,21 @@ void ocelot_copy_to(struct map_info *map, unsigned long to, const void *from, ss 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 @@ static int __init init_ocelot_maps(void) 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 @@ static int __init init_ocelot_maps(void) 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 @@ static int __init init_ocelot_maps(void) 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 @@ static int __init init_ocelot_maps(void) 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 @@ static void __exit cleanup_ocelot_maps(void) { 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 --git a/drivers/mtd/maps/octagon-5066.c b/drivers/mtd/maps/octagon-5066.c index 89575be6f13b..176890b9b8e1 100644 --- a/drivers/mtd/maps/octagon-5066.c +++ b/drivers/mtd/maps/octagon-5066.c @@ -1,4 +1,4 @@ -// $Id: octagon-5066.c,v 1.19 2001/10/02 15:05:14 dwmw2 Exp $ +// $Id: octagon-5066.c,v 1.24 2003/05/21 15:15:07 dwmw2 Exp $ /* ###################################################################### Octagon 5066 MTD Driver. @@ -31,6 +31,7 @@ #include <asm/io.h> #include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> #define WINDOW_START 0xe8000 #define WINDOW_LENGTH 0x8000 @@ -151,32 +152,34 @@ static void oct5066_copy_to(struct map_info *map, unsigned long to, const void * 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 } }; @@ -223,33 +226,32 @@ void cleanup_oct5066(void) } } iounmap((void *)iomapadr); - release_region(PAGE_IO,1); + release_region(PAGE_IO, 1); } int __init init_oct5066(void) { int i; - + int ret = 0; + // Do an autoprobe sequence - if (check_region(PAGE_IO,1) != 0) - { - printk("5066: Page Register in Use\n"); - return -EAGAIN; - } + if (!request_region(PAGE_IO,1,"Octagon SSD")) { + printk(KERN_NOTICE "5066: Page Register in Use\n"); + return -EAGAIN; + } iomapadr = (unsigned long)ioremap(WINDOW_START, WINDOW_LENGTH); if (!iomapadr) { - printk("Failed to ioremap memory region\n"); - return -EIO; + printk(KERN_NOTICE "Failed to ioremap memory region\n"); + ret = -EIO; + goto out_rel; } - if (OctProbe() != 0) - { - printk("5066: Octagon Probe Failed, is this an Octagon 5066 SBC?\n"); - iounmap((void *)iomapadr); - return -EAGAIN; - } - - request_region(PAGE_IO,1,"Octagon SSD"); - + 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; + } + // Print out our little header.. printk("Octagon 5066 SSD IO:0x%x MEM:0x%x-0x%x\n",PAGE_IO,WINDOW_START, WINDOW_START+WINDOW_LENGTH); @@ -263,7 +265,7 @@ int __init init_oct5066(void) 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]); } } @@ -272,8 +274,14 @@ int __init init_oct5066(void) cleanup_oct5066(); return -ENXIO; } - + return 0; + + out_unmap: + iounmap((void *)iomapadr); + out_rel: + release_region(PAGE_IO, 1); + return ret; } module_init(init_oct5066); diff --git a/drivers/mtd/maps/pb1xxx-flash.c b/drivers/mtd/maps/pb1xxx-flash.c new file mode 100644 index 000000000000..b53795a120dd --- /dev/null +++ b/drivers/mtd/maps/pb1xxx-flash.c @@ -0,0 +1,196 @@ +/* + * Flash memory access on Alchemy Pb1xxx boards + * + * (C) 2001 Pete Popov <ppopov@mvista.com> + * + * $Id: pb1xxx-flash.c,v 1.8 2003/05/21 12:45:19 dwmw2 Exp $ + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> +#include <asm/au1000.h> + +#ifdef DEBUG_RW +#define DBG(x...) printk(x) +#else +#define DBG(x...) +#endif + +#ifdef CONFIG_MIPS_PB1000 +#define WINDOW_ADDR 0x1F800000 +#define WINDOW_SIZE 0x800000 +#endif + + +static struct map_info pb1xxx_map = { + .name = "Pb1xxx flash", +}; + + +#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 + } +}; + +#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 + * 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 + } +}; +#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 + } +}; +#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, + } +}; +#else +#error MTD_PB1500 define combo error /* should never happen */ +#endif +#else +#error Unsupported board +#endif + + +#define NB_OF(x) (sizeof(x)/sizeof(x[0])) + +static struct mtd_partition *parsed_parts; +static struct mtd_info *mymtd; + +int __init pb1xxx_mtd_init(void) +{ + struct mtd_partition *parts; + 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; + + /* + * 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.phys = WINDOW_ADDR; + pb1xxx_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + + simple_map_init(&pb1xxx_map); + + mymtd = do_map_probe("cfi_probe", &pb1xxx_map); + if (!mymtd) { + iounmap(pb1xxx_map.virt); + return -ENXIO; + } + mymtd->owner = THIS_MODULE; + + add_mtd_partitions(mymtd, 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_map.virt) + iounmap(pb1xxx_map.virt); +} + +module_init(pb1xxx_mtd_init); +module_exit(pb1xxx_mtd_cleanup); + +MODULE_AUTHOR("Pete Popov"); +MODULE_DESCRIPTION("Pb1xxx CFI map driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c index 0ba1c33537ae..452d45357972 100644 --- a/drivers/mtd/maps/pci.c +++ b/drivers/mtd/maps/pci.c @@ -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.1 2001/09/27 20:28:45 rmk 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 @@ intel_iq80310_translate(struct map_pci_info *map, unsigned long ofs) } 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 @@ intel_dc21285_translate(struct map_pci_info *map, unsigned long ofs) } 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,22 @@ static struct mtd_pci_info intel_dc21285_info = { 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 */ - .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, }, - { .vendor = 0, } + { 0, } }; /* @@ -273,14 +273,15 @@ static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void * } 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 @@ -320,7 +321,7 @@ mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) if (!mtd) goto release; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; add_mtd_device(mtd); pci_set_drvdata(dev, mtd); @@ -357,10 +358,10 @@ mtd_pci_remove(struct pci_dev *dev) } static struct pci_driver mtd_pci_driver = { - .name = "MTD PCI", - .probe = mtd_pci_probe, - .remove = 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 --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c index e13395177dc9..7f081796d160 100644 --- a/drivers/mtd/maps/pcmciamtd.c +++ b/drivers/mtd/maps/pcmciamtd.c @@ -1,5 +1,5 @@ /* - * $Id: pcmciamtd.c,v 1.36 2002/10/14 18:49:12 rmk Exp $ + * $Id: pcmciamtd.c,v 1.47 2003/05/28 13:36:14 dwmw2 Exp $ * * pcmciamtd.c - MTD driver for PCMCIA flash memory cards * @@ -14,6 +14,7 @@ #include <linux/module.h> #include <linux/slab.h> #include <linux/timer.h> +#include <linux/init.h> #include <asm/io.h> #include <asm/system.h> @@ -24,6 +25,7 @@ #include <pcmcia/ds.h> #include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> #ifdef CONFIG_MTD_DEBUG static int debug = CONFIG_MTD_DEBUG_VERBOSE; @@ -47,7 +49,7 @@ static const int debug = 0; #define DRIVER_DESC "PCMCIA Flash memory card driver" -#define DRIVER_VERSION "$Revision: 1.36 $" +#define DRIVER_VERSION "$Revision: 1.47 $" /* Size of the PCMCIA address space: 26 bits = 64 MB */ #define MAX_PCMCIA_ADDR 0x4000000 @@ -519,6 +521,7 @@ static void pcmciamtd_config(dev_link_t *link) 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; @@ -529,7 +532,7 @@ static void pcmciamtd_config(dev_link_t *link) 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; @@ -542,7 +545,7 @@ static void pcmciamtd_config(dev_link_t *link) 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); @@ -550,7 +553,7 @@ static void pcmciamtd_config(dev_link_t *link) 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; } @@ -563,7 +566,7 @@ static void pcmciamtd_config(dev_link_t *link) 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); @@ -636,7 +639,7 @@ static void pcmciamtd_config(dev_link_t *link) } dev->mtd_info = mtd; - mtd->module = THIS_MODULE; + mtd->owner = THIS_MODULE; dev->cardsize = mtd->size; if(new_name) { @@ -644,14 +647,14 @@ static void pcmciamtd_config(dev_link_t *link) 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'; } - sprintf(mtd->name, "%d%cB %s", size, unit, "PCMCIA Memory card"); + sprintf(mtd->name, "%d%ciB %s", size, unit, "PCMCIA Memory card"); } /* If the memory found is fits completely into the mapped PCMCIA window, diff --git a/drivers/mtd/maps/physmap.c b/drivers/mtd/maps/physmap.c index 5243513f93e4..acfd683eda07 100644 --- a/drivers/mtd/maps/physmap.c +++ b/drivers/mtd/maps/physmap.c @@ -1,5 +1,5 @@ /* - * $Id: physmap.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: physmap.c,v 1.27 2003/05/21 12:45:19 dwmw2 Exp $ * * Normal mappings of chips in physical memory */ @@ -7,11 +7,12 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/config.h> - +#include <linux/mtd/partitions.h> #define WINDOW_ADDR CONFIG_MTD_PHYSMAP_START #define WINDOW_SIZE CONFIG_MTD_PHYSMAP_LEN @@ -19,94 +20,117 @@ 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); -} -void physmap_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void physmap_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} +struct map_info physmap_map = { + .name = "Physically mapped flash", + .size = WINDOW_SIZE, + .buswidth = BUSWIDTH, + .phys = WINDOW_ADDR, +}; -void physmap_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} +#ifdef CONFIG_MTD_PARTITIONS +static struct mtd_partition *mtd_parts; +static int mtd_parts_nb; + +static struct mtd_partition physmap_partitions[] = { +#if 0 +/* Put your own partition definitions here */ + { + .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 +}; -void physmap_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio(map->map_priv_1 + to, from, len); -} +#define NUM_PARTITIONS (sizeof(physmap_partitions)/sizeof(struct mtd_partition)) -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 -}; +#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); + physmap_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); - if (!physmap_map.map_priv_1) { + if (!physmap_map.virt) { printk("Failed to ioremap\n"); return -EIO; } - mymtd = do_map_probe("cfi_probe", &physmap_map); - if (mymtd) { - mymtd->module = THIS_MODULE; + 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->owner = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS + mtd_parts_nb = parse_mtd_partitions(mymtd, probes, + &mtd_parts, 0); + + if (mtd_parts_nb > 0) + { + add_mtd_partitions (mymtd, mtd_parts, mtd_parts_nb); + return 0; + } + + if (NUM_PARTITIONS != 0) + { + printk(KERN_NOTICE + "Using physmap partition definition\n"); + add_mtd_partitions (mymtd, physmap_partitions, NUM_PARTITIONS); + return 0; + } + +#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_PARTITIONS) { + del_mtd_partions(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 --git a/drivers/mtd/maps/pnc2000.c b/drivers/mtd/maps/pnc2000.c index ec3ba8c37e07..dcf68f2aab4e 100644 --- a/drivers/mtd/maps/pnc2000.c +++ b/drivers/mtd/maps/pnc2000.c @@ -5,12 +5,13 @@ * * This code is GPL * - * $Id: pnc2000.c,v 1.10 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: pnc2000.c,v 1.14 2003/05/21 12:45:19 dwmw2 Exp $ */ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -24,58 +25,13 @@ * MAP DRIVER STUFF */ -__u8 pnc_read8(struct map_info *map, unsigned long ofs) -{ - return *(__u8 *)(WINDOW_ADDR + ofs); -} - -__u16 pnc_read16(struct map_info *map, unsigned long ofs) -{ - return *(__u16 *)(WINDOW_ADDR + ofs); -} - -__u32 pnc_read32(struct map_info *map, unsigned long ofs) -{ - return *(volatile unsigned int *)(WINDOW_ADDR + ofs); -} - -void pnc_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy(to, (void *)(WINDOW_ADDR + from), len); -} - -void pnc_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - *(__u8 *)(WINDOW_ADDR + adr) = d; -} - -void pnc_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - *(__u16 *)(WINDOW_ADDR + adr) = d; -} - -void pnc_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - *(__u32 *)(WINDOW_ADDR + adr) = d; -} - -void pnc_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy((void *)(WINDOW_ADDR + to), from, len); -} struct map_info pnc_map = { - .name = "PNC-2000", - .size = WINDOW_SIZE, - .buswidth = 4, - .read8 = pnc_read8, - .read16 = pnc_read16, - .read32 = pnc_read32, - .copy_from = pnc_copy_from, - .write8 = pnc_write8, - .write16 = pnc_write16, - .write32 = pnc_write32, - .copy_to = pnc_copy_to + .name = "PNC-2000", + .size = WINDOW_SIZE, + .buswidth = 4, + .phys = 0xFFFFFFFF, + .virt = WINDOW_ADDR, }; @@ -84,18 +40,19 @@ struct map_info pnc_map = { */ static struct mtd_partition pnc_partitions[3] = { { - .name = "PNC-2000 boot firmware", - .size = 0x20000, + .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 } }; @@ -109,9 +66,11 @@ int __init init_pnc2000(void) { 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 --git a/drivers/mtd/maps/redwood.c b/drivers/mtd/maps/redwood.c new file mode 100644 index 000000000000..4ece1c883b72 --- /dev/null +++ b/drivers/mtd/maps/redwood.c @@ -0,0 +1,171 @@ +/* + * $Id: redwood.c,v 1.6 2003/05/21 12:45:19 dwmw2 Exp $ + * + * drivers/mtd/maps/redwood.c + * + * FLASH map for the IBM Redwood 4/5/6 boards. + * + * + * Author: Armin Kuster <akuster@mvista.com> + * + * 2001-2002 (c) MontaVista, Software, Inc. This file is licensed under + * the terms of the GNU General Public License version 2. This program + * is licensed "as is" without any warranty of any kind, whether express + * or implied. + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/io.h> + +#if !defined (CONFIG_REDWOOD_6) + +#define WINDOW_ADDR 0xffc00000 +#define WINDOW_SIZE 0x00400000 + +#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 + +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 kernel", + .offset = RW_PART0_OF, + .size = RW_PART0_SZ + }, + { + .name = "Redwood OpenBIOS Vital Product Data", + .offset = RW_PART1_OF, + .size = RW_PART1_SZ, + .mask_flags = MTD_WRITEABLE /* force read-only */ + }, + { + .name = "Redwood filesystem", + .offset = RW_PART2_OF, + .size = RW_PART2_SZ + }, + { + .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])) + +static struct mtd_info *redwood_mtd; + +int __init init_redwood_flash(void) +{ + printk(KERN_NOTICE "redwood: flash mapping: %x at %x\n", + WINDOW_SIZE, WINDOW_ADDR); + + redwood_flash_map.virt = + (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); + + 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->owner = THIS_MODULE; + return add_mtd_partitions(redwood_mtd, + redwood_flash_partitions, + NUM_REDWOOD_FLASH_PARTITIONS); + } + + return -ENXIO; +} + +static void __exit cleanup_redwood_flash(void) +{ + if (redwood_mtd) { + del_mtd_partitions(redwood_mtd); + /* 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("Armin Kuster <akuster@mvista.com>"); +MODULE_DESCRIPTION("MTD map driver for the IBM Redwood reference boards"); diff --git a/drivers/mtd/maps/rpxlite.c b/drivers/mtd/maps/rpxlite.c index 3cd1a1e0283c..9c3e7da443d7 100644 --- a/drivers/mtd/maps/rpxlite.c +++ b/drivers/mtd/maps/rpxlite.c @@ -1,5 +1,5 @@ /* - * $Id: rpxlite.c,v 1.15 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: rpxlite.c,v 1.19 2003/05/21 12:45:19 dwmw2 Exp $ * * Handle mapping of the flash on the RPX Lite and CLLF boards */ @@ -7,6 +7,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -17,80 +18,31 @@ static struct mtd_info *mymtd; -__u8 rpxlite_read8(struct map_info *map, unsigned long ofs) -{ - return __raw_readb(map->map_priv_1 + ofs); -} - -__u16 rpxlite_read16(struct map_info *map, unsigned long ofs) -{ - return __raw_readw(map->map_priv_1 + ofs); -} - -__u32 rpxlite_read32(struct map_info *map, unsigned long ofs) -{ - return __raw_readl(map->map_priv_1 + ofs); -} - -void rpxlite_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) -{ - memcpy_fromio(to, (void *)(map->map_priv_1 + from), len); -} - -void rpxlite_write8(struct map_info *map, __u8 d, unsigned long adr) -{ - __raw_writeb(d, map->map_priv_1 + adr); - mb(); -} - -void rpxlite_write16(struct map_info *map, __u16 d, unsigned long adr) -{ - __raw_writew(d, map->map_priv_1 + adr); - mb(); -} - -void rpxlite_write32(struct map_info *map, __u32 d, unsigned long adr) -{ - __raw_writel(d, map->map_priv_1 + adr); - mb(); -} - -void rpxlite_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) -{ - memcpy_toio((void *)(map->map_priv_1 + to), from, len); -} - -struct map_info rpxlite_map = { - .name = "RPX", - .size = WINDOW_SIZE, - .buswidth = 4, - .read8 = rpxlite_read8, - .read16 = rpxlite_read16, - .read32 = rpxlite_read32, - .copy_from = rpxlite_copy_from, - .write8 = rpxlite_write8, - .write16 = rpxlite_write16, - .write32 = rpxlite_write32, - .copy_to = rpxlite_copy_to +static struct map_info rpxlite_map = { + .name = "RPX", + .size = WINDOW_SIZE, + .buswidth = 4, + .phys = WINDOW_ADDR, }; int __init init_rpxlite(void) { printk(KERN_NOTICE "RPX Lite or CLLF flash device: %x at %x\n", WINDOW_SIZE*4, WINDOW_ADDR); - rpxlite_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); + rpxlite_map.virt = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE * 4); - if (!rpxlite_map.map_priv_1) { + if (!rpxlite_map.virt) { printk("Failed to ioremap\n"); return -EIO; } + simple_map_init(&rpxlite_map); mymtd = do_map_probe("cfi_probe", &rpxlite_map); if (mymtd) { - mymtd->module = THIS_MODULE; + mymtd->owner = THIS_MODULE; add_mtd_device(mymtd); return 0; } - iounmap((void *)rpxlite_map.map_priv_1); + iounmap((void *)rpxlite_map.virt); return -ENXIO; } @@ -100,9 +52,9 @@ static void __exit cleanup_rpxlite(void) 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 --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index d7a8a514b851..b1286dc80e2e 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -3,7 +3,7 @@ * * (C) 2000 Nicolas Pitre <nico@cam.org> * - * $Id: sa1100-flash.c,v 1.28 2002/05/07 13:48:38 abz Exp $ + * $Id: sa1100-flash.c,v 1.35 2003/05/21 12:45:19 dwmw2 Exp $ */ #include <linux/config.h> @@ -11,236 +11,188 @@ #include <linux/types.h> #include <linux/ioport.h> #include <linux/kernel.h> +#include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> -#include <linux/mtd/concat.h> #include <asm/hardware.h> -#include <asm/mach-types.h> #include <asm/io.h> -#include <asm/sizes.h> -#include <asm/arch/h3600.h> #ifndef CONFIG_ARCH_SA1100 #error This is for SA1100 architecture only #endif -/* - * This isnt complete yet, so... - */ -#define CONFIG_MTD_SA1100_STATICMAP 1 - -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); -} +#define WINDOW_ADDR 0xe8000000 -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 __initdata = { - .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, +static struct map_info sa1100_map = { + .name = "SA1100 flash", + .virt = WINDOW_ADDR, + .phys = NO_XIP; }; -#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. * - * 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. + * 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 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 28F320C3 Advanced+ Boot Block Flash (32 Mi bit) + * 1 x Intel 28F320C3BA100 Advanced+ Boot Block Flash (32 Mi bit) * Eight 4 KiW Parameter Bottom Blocks (64 KiB) * Sixty-three 32 KiW Main Blocks (4032 Ki b) - * - * <or> - * - * 1 x Intel 28F640C3 Advanced+ Boot Block Flash (64 Mi bit) - * Eight 4 KiW Parameter Bottom Blocks (64 KiB) - * One-hundred-twenty-seven 32 KiW Main Blocks (8128 Ki b) */ +#define BADGE4_FLASH_SIZE 0x00400000 static struct mtd_partition badge4_partitions[] = { { - .name = "BLOB boot loader", - .offset = 0, - .size = 0x0000A000 + .name = "BLOB boot loader", + .offset = 0, + .size = 0x0000A000 + }, { + .name = "params", + .offset = MTDPART_OFS_APPEND, + .size = 0x00006000 }, { - .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 = "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 = "firmware", + .size = 0x00040000, + .offset = 0, }, { - .name = "params", - .size = 0x00040000, - .offset = 0x00040000, + .name = "params", + .size = 0x00040000, + .offset = 0x00040000, }, { - .name = "kernel", - .size = 0x00100000, - .offset = 0x00080000, + .name = "kernel", + .size = 0x00100000, + .offset = 0x00080000, }, { - .name = "rootdisk", - .size = 0x01E80000, - .offset = 0x00180000, + .name = "rootdisk", + .size = 0x01E80000, + .offset = 0x00180000, } }; #elif defined CONFIG_SA1100_CERF_FLASH_16MB +#define CERF_FLASH_SIZE 0x01000000 static struct mtd_partition cerf_partitions[] = { { - .name = "firmware", - .size = 0x00020000, - .offset = 0, + .name = "firmware", + .size = 0x00020000, + .offset = 0, }, { - .name = "params", - .size = 0x00020000, - .offset = 0x00020000, + .name = "params", + .size = 0x00020000, + .offset = 0x00020000, }, { - .name = "kernel", - .size = 0x00100000, - .offset = 0x00040000, + .name = "kernel", + .size = 0x00100000, + .offset = 0x00040000, }, { - .name = "rootdisk", - .size = 0x00EC0000, - .offset = 0x00140000, + .name = "rootdisk", + .size = 0x00EC0000, + .offset = 0x00140000, } }; #elif defined CONFIG_SA1100_CERF_FLASH_8MB @@ -251,38 +203,39 @@ static struct mtd_partition cerf_partitions[] = { #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 disk", - .offset = 0x00140000, + .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, /* 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,95 +245,96 @@ static struct mtd_partition consus_partitions[] = { #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[] = { -#ifdef CONFIG_SA1100_FREEBIRD_NEW +#if 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 }; @@ -388,215 +342,178 @@ static struct mtd_partition freebird_partitions[] = { #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 = "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 + .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 } }; #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_H3XXX -static struct mtd_partition h3xxx_partitions[] = { +#ifdef CONFIG_SA1100_H3600 +#define H3600_FLASH_SIZE 0x02000000 +static struct mtd_partition h3600_partitions[] = { { - .name = "H3XXX boot firmware", - .size = 0x00040000, - .offset = 0, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { -#ifdef CONFIG_MTD_2PARTS_IPAQ - .name = "H3XXX root jffs2", - .size = MTDPART_SIZ_FULL, - .offset = 0x00040000, -#else - .name = "H3XXX kernel", - .size = 0x00080000, - .offset = 0x00040000, + .name = "H3600 boot firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "H3600 kernel", + .size = 0x00080000, + .offset = 0x00040000, }, { - .name = "H3XXX params", - .size = 0x00040000, - .offset = 0x000C0000, + .name = "H3600 params", + .size = 0x00040000, + .offset = 0x000C0000, }, { #ifdef CONFIG_JFFS2_FS - .name = "H3XXX root jffs2", - .size = MTDPART_SIZ_FULL, - .offset = 0x00100000, + .name = "H3600 root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = 0x00100000, #else - .name = "H3XXX initrd", - .size = 0x00100000, - .offset = 0x00100000, + .name = "H3600 initrd", + .size = 0x00100000, + .offset = 0x00100000, }, { - .name = "H3XXX root cramfs", - .size = 0x00300000, - .offset = 0x00200000, + .name = "H3600 root cramfs", + .size = 0x00300000, + .offset = 0x00200000, }, { - .name = "H3XXX usr cramfs", - .size = 0x00800000, - .offset = 0x00500000, + .name = "H3600 usr cramfs", + .size = 0x00800000, + .offset = 0x00500000, }, { - .name = "H3XXX usr local", - .size = MTDPART_SIZ_FULL, - .offset = 0x00d00000, -#endif + .name = "H3600 usr local", + .size = MTDPART_SIZ_FULL, + .offset = 0x00d00000, #endif } }; -static void h3xxx_set_vpp(struct map_info *map, int vpp) +static void h3600_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 = "Loader", + .size = 0x00040000, + .offset = 0, }, { - .name = "Sector 1", - .size = 0x00040000, - .offset = MTDPART_OFS_APPEND, + .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 kernel", - .size = 0x000c0000, - .offset = 0x00040000, - }, { - .name = "JORNADA720 params", - .size = 0x00040000, - .offset = 0x00100000, - }, { - .name = "JORNADA720 initrd", - .size = 0x00100000, - .offset = 0x00140000, - }, { - .name = "JORNADA720 root cramfs", - .size = 0x00300000, - .offset = 0x00240000, - }, { - .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 boot firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "JORNADA720 kernel", + .size = 0x000c0000, + .offset = 0x00040000, + }, { + .name = "JORNADA720 params", + .size = 0x00040000, + .offset = 0x00100000, + }, { + .name = "JORNADA720 initrd", + .size = 0x00100000, + .offset = 0x00140000, + }, { + .name = "JORNADA720 root cramfs", + .size = 0x00300000, + .offset = 0x00240000, + }, { + .name = "JORNADA720 usr cramfs", + .size = 0x00800000, + .offset = 0x00540000, + }, { + .name = "JORNADA720 usr local", + .size = 0 /* will expand to the end of the flash */ + .offset = 0x00d00000, } }; -static void jornada720_set_vpp(struct map_info *map, int vpp) +static void jornada720_set_vpp(int vpp) { if (vpp) PPSR |= 0x80; @@ -604,753 +521,439 @@ static void jornada720_set_vpp(struct map_info *map, int vpp) 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 boot firmware", + .size = 0x00080000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - .name = "SIMpad kernel", - .size = 0x00100000, - .offset = 0x00080000, + .name = "SIMpad kernel", + .size = 0x00100000, + .offset = 0x00080000, }, { #ifdef CONFIG_JFFS2_FS - .name = "SIMpad root jffs2", - .size = MTDPART_SIZ_FULL, - .offset = 0x00180000, + .name = "SIMpad root jffs2", + .size = MTDPART_SIZ_FULL, + .offset = 0x00180000, #else - .name = "SIMpad initrd", - .size = 0x00300000, - .offset = 0x00180000, + .name = "SIMpad initrd", + .size = 0x00300000, + .offset = 0x00180000, }, { - .name = "SIMpad root cramfs", - .size = 0x00300000, - .offset = 0x00480000, + .name = "SIMpad root cramfs", + .size = 0x00300000, + .offset = 0x00480000, }, { - .name = "SIMpad usr cramfs", - .size = 0x005c0000, - .offset = 0x00780000, + .name = "SIMpad usr cramfs", + .size = 0x005c0000, + .offset = 0x00780000, }, { - .name = "SIMpad usr local", - .size = MTDPART_SIZ_FULL, - .offset = 0x00d40000, + .name = "SIMpad usr local", + .size = MTDPART_SIZ_FULL, + .offset = 0x00d40000, #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 boot firmware", + .size = 0x00040000, + .offset = 0, + .mask_flags = MTD_WRITEABLE, /* force read-only */ }, { - .name = "STORK params", - .size = 0x00040000, - .offset = 0x00040000, + .name = "STORK params", + .size = 0x00040000, + .offset = 0x00040000, }, { - .name = "STORK kernel", - .size = 0x00100000, - .offset = 0x00080000, + .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 initrd", + .size = 0x00100000, + .offset = 0x00180000, }, { - .name = "STORK root cramfs", - .size = 0x00300000, - .offset = 0x00280000, + .name = "STORK root cramfs", + .size = 0x00300000, + .offset = 0x00280000, }, { - .name = "STORK usr cramfs", - .size = 0x00800000, - .offset = 0x00580000, + .name = "STORK usr cramfs", + .size = 0x00800000, + .offset = 0x00580000, }, { - .name = "STORK usr local", - .offset = 0x00d80000, - .size = MTDPART_SIZ_FULL, + .name = "STORK usr local", + .offset = 0x00d80000, + .size = MTDPART_SIZ_FULL, #endif } }; #endif -#ifdef CONFIG_SA1100_TRIZEPS -static struct mtd_partition trizeps_partitions[] = { +#ifdef CONFIG_SA1100_YOPY +#define YOPY_FLASH_SIZE 0x08000000 +static struct mtd_partition yopy_partitions[] = { { - .name = "Bootloader", - .size = 0x00100000, - .offset = 0, + .name = "boot firmware", + .size = 0x00040000, + .offset = 0x00000000, + .mask_flags = MTD_WRITEABLE, /* force read-only */ + }, { + .name = "kernel", + .size = 0x00080000, + .offset = 0x00080000, }, { - .name = "Kernel", - .size = 0x00100000, - .offset = MTDPART_OFS_APPEND, + .name = "initrd", + .size = 0x00300000, + .offset = 0x00100000, }, { - .name = "root", - .size = MTDPART_SIZ_FULL, - .offset = MTDPART_OFS_APPEND, + .name = "root", + .size = 0x01000000, + .offset = 0x00400000, } }; #endif -#ifdef CONFIG_SA1100_YOPY -static struct mtd_partition yopy_partitions[] = { - { - .name = "boot firmware", - .size = 0x00040000, - .offset = 0x00000000, - .mask_flags = MTD_WRITEABLE, /* force read-only */ - }, { - .name = "kernel", - .size = 0x00080000, - .offset = 0x00080000, - }, { - .name = "initrd", - .size = 0x00300000, - .offset = 0x00100000, - }, { - .name = "root", - .size = 0x01000000, - .offset = 0x00400000, - } -}; -#endif +static struct mtd_partition *parsed_parts; +static struct mtd_info *mymtd; +static const char *probes[] = { "RedBoot", "cmdlinepart", NULL }; -static int __init sa1100_static_partitions(struct mtd_partition **parts) +int __init sa1100_mtd_init(void) { - int nb_parts = 0; + struct mtd_partition *parts; + int nb_parts = 0, ret; + int parsed_nr_parts = 0; + unsigned long base = -1UL; + + /* Default flash buswidth */ + sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4; + + /* + * Static partition definition selection + */ #ifdef CONFIG_SA1100_ADSBITSY if (machine_is_adsbitsy()) { - *parts = adsbitsy_partitions; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(assabet_partitions); + parts = assabet_partitions; + nb_parts = ARRAY_SIZE(assabet_partitions); + sa1100_map.size = ASSABET_FLASH_SIZE; + sa1100_map.phys = 0; } #endif #ifdef CONFIG_SA1100_BADGE4 if (machine_is_badge4()) { - *parts = badge4_partitions; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(frodo_partitions); + parts = frodo_partitions; + nb_parts = ARRAY_SIZE(frodo_partitions); + sa1100_map.size = FRODO_FLASH_SIZE; + base = 0x00000000; } -#endif +#endif #ifdef CONFIG_SA1100_GRAPHICSCLIENT if (machine_is_graphicsclient()) { - *parts = graphicsclient_partitions; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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_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); +#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; + sa1100_map.phys = 0; } #endif #ifdef CONFIG_SA1100_HUW_WEBPANEL if (machine_is_huw_webpanel()) { - *parts = huw_webpanel_partitions; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(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; - nb_parts = ARRAY_SIZE(stork_partitions); - } -#endif -#ifdef CONFIG_SA1100_TRIZEPS - if (machine_is_trizeps()) { - *parts = trizeps_partitions; - nb_parts = ARRAY_SIZE(trizeps_partitions); + parts = stork_partitions; + nb_parts = ARRAY_SIZE(stork_partitions); + sa1100_map.size = STORK_FLASH_SIZE; } #endif #ifdef CONFIG_SA1100_YOPY if (machine_is_yopy()) { - *parts = yopy_partitions; - nb_parts = ARRAY_SIZE(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; - 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; - /* - * Allocate the map_info structs in one go. + * For simple flash devices, use ioremap to map the flash. */ - maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); - if (!maps) - return -ENOMEM; - - /* - * 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; - memcpy(sa[i].map, &sa1100_map, sizeof(struct map_info)); - - sa[i].vbase = ioremap(sa[i].base, sa[i].size); - if (!sa[i].vbase) { - ret = -ENOMEM; - break; - } - - sa[i].map->map_priv_1 = (unsigned long)sa[i].vbase; - sa[i].map->buswidth = sa[i].width; - sa[i].map->size = sa[i].size; - - /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. - */ - sa[i].mtd = do_map_probe("cfi_probe", sa[i].map); - if (sa[i].mtd == NULL) { - ret = -ENXIO; - break; - } - sa[i].mtd->module = 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; - } - - /* - * 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. - */ - if (ret == -ENXIO) { - iounmap(sa[i].vbase); - sa[i].vbase = NULL; - release_resource(sa[i].res); - sa[i].res = NULL; - } - - /* - * 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 (base != -1UL) { + if (!request_mem_region(base, sa1100_map.size, "flash")) + return -EBUSY; + sa1100_map.phys = base; + sa1100_map.virt = (unsigned long) + ioremap(base, sa1100_map.size); + ret = -ENOMEM; + if (!sa1100_map.virt) + goto out_err; } /* - * If we failed, clean up. + * Now let's probe for the actual flash. Do it here since + * specific machine settings might have been set above. */ - 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 - - 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); -} + printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); -static int __init sa1100_locate_flash(void) -{ - int i, nr = -ENODEV; - - 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; - } - if (machine_is_graphicsmaster()) { - info[0].base = SA1100_CS1_PHYS; - info[0].size = SZ_16M; - nr = 1; - } - if (machine_is_h3xxx()) { - sa1100_map.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()) { - sa1100_map.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_32M; - nr = 1; - } - 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; - } + simple_map_init(&sa1100_map); - if (nr < 0) - return nr; + mymtd = do_map_probe("cfi_probe", &sa1100_map); + ret = -ENXIO; + if (!mymtd) + goto out_err; + mymtd->owner = THIS_MODULE; /* - * Retrieve the buswidth from the MSC registers. - * We currently only implement CS0 and CS1 here. + * Dynamic partition selection stuff (might override the static ones) */ - 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; + int ret = parse_mtd_partitions(mymtd, probes, &parsed_parts, 0); - case SA1100_CS1_PHYS: - info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; - break; - } - } - - 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 *); + if (ret > 0) + parsed_nr_parts = ret; -static struct mtd_partition *parsed_parts; - -static void __init sa1100_locate_partitions(struct mtd_info *mtd) -{ - const char *part_type = NULL; - int nr_parts = 0; - - do { - /* - * Partition selection stuff. - */ -#ifdef CONFIG_MTD_CMDLINE_PARTS - nr_parts = parse_cmdline_partitions(mtd, &parsed_parts, "sa1100"); - 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_SA1100_STATICMAP - nr_parts = sa1100_static_partitions(&parsed_parts); - if (nr_parts > 0) { - part_type = "static"; - break; - } -#endif - } while (0); + if (parsed_nr_parts > 0) { + parts = parsed_parts; + nb_parts = parsed_nr_parts; + } - if (nr_parts == 0) { - printk(KERN_NOTICE "SA1100 flash: no partition info " - "available, registering whole flash\n"); - add_mtd_device(mtd); + 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 "SA1100 flash: using %s partition " - "definition\n", part_type); - add_mtd_partitions(mtd, parsed_parts, nr_parts); + add_mtd_partitions(mymtd, parts, nb_parts); } + return 0; - /* 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); - + out_err: + if (sa1100_map.virt != WINDOW_ADDR) { + iounmap((void *)sa1100_map.virt); + release_mem_region(sa1100_map.phys, sa1100_map.size); + } return ret; } static void __exit sa1100_mtd_cleanup(void) { - sa1100_destroy_mtd(info, mymtd); - sa1100_destroy_partitions(); + if (mymtd) { + del_mtd_partitions(mymtd); + map_destroy(mymtd); + if (parsed_parts) + kfree(parsed_parts); + } + if (sa1100_map.virt != WINDOW_ADDR) { + iounmap((void *)sa1100_map.virt); + release_mem_region(sa1100_map.phys, sa1100_map.size); + } } module_init(sa1100_mtd_init); diff --git a/drivers/mtd/maps/sbc_gxx.c b/drivers/mtd/maps/sbc_gxx.c index 429d25eb0c10..3f143e3b43c1 100644 --- a/drivers/mtd/maps/sbc_gxx.c +++ b/drivers/mtd/maps/sbc_gxx.c @@ -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.19 2001/10/02 15:05: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. @@ -26,7 +26,7 @@ This driver uses the CFI probe and Intel Extended Command Set drivers. The flash is accessed as follows: - 16 kbyte memory window at 0xdc000-0xdffff + 16 KiB memory window at 0xdc000-0xdffff Two IO address locations for paging @@ -90,20 +90,15 @@ static spinlock_t sbc_gxx_spin = SPIN_LOCK_UNLOCKED; /* 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 = "SBC-GXx flash boot partition", - .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 - } +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 } }; #define NUM_PARTITIONS 3 @@ -208,20 +203,20 @@ static void sbc_gxx_copy_to(struct map_info *map, unsigned long to, const void * } 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 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 + .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 }; /* MTD device for all of the flash. */ @@ -240,12 +235,6 @@ static void cleanup_sbc_gxx(void) 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", @@ -253,7 +242,14 @@ int __init init_sbc_gxx(void) 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, @@ -267,7 +263,7 @@ int __init init_sbc_gxx(void) 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 --git a/drivers/mtd/maps/sc520cdp.c b/drivers/mtd/maps/sc520cdp.c index ea1607584459..d446d55eddc2 100644 --- a/drivers/mtd/maps/sc520cdp.c +++ b/drivers/mtd/maps/sc520cdp.c @@ -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.9 2001/10/02 15:05:14 dwmw2 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 @@ -25,13 +25,15 @@ * For details see http://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html */ +#include <linux/config.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> - +#include <linux/mtd/concat.h> /* ** The Embedded Systems BIOS decodes the first FLASH starting at @@ -83,94 +85,32 @@ #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 }, }; #define NUM_FLASH_BANKS (sizeof(sc520cdp_map)/sizeof(struct map_info)) static struct mtd_info *mymtd[NUM_FLASH_BANKS]; +static struct mtd_info *merged_mtd; #ifdef REPROGRAM_PAR @@ -253,9 +193,9 @@ static void sc520cdp_setup_par(void) /* 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; } @@ -280,7 +220,7 @@ static void sc520cdp_setup_par(void) 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); @@ -298,28 +238,40 @@ static int __init init_sc520cdp(void) #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", &sc520cdp_map[i]); + mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]); if(!mymtd[i]) mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]); if (mymtd[i]) { - mymtd[i]->module = THIS_MODULE; - add_mtd_device(mymtd[i]); + 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) { + /* Combine the two flash banks into a single MTD device & register it: */ + merged_mtd = mtd_concat_create(mymtd, 2, "SC520CDP Flash Banks #0 and #1"); + if(merged_mtd) + add_mtd_device(merged_mtd); + } + if(devices_found == 3) /* register the third (DIL-Flash) device */ + add_mtd_device(mymtd[2]); return(devices_found ? 0 : -ENXIO); } @@ -327,14 +279,19 @@ static void __exit cleanup_sc520cdp(void) { int i; + if (merged_mtd) { + del_mtd_device(merged_mtd); + mtd_concat_destroy(merged_mtd); + } + if (mymtd[2]) + del_mtd_device(mymtd[2]); + for (i = 0; i < NUM_FLASH_BANKS; i++) { - if (mymtd[i]) { - del_mtd_device(mymtd[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 --git a/drivers/mtd/maps/scb2_flash.c b/drivers/mtd/maps/scb2_flash.c new file mode 100644 index 000000000000..3601adfe9193 --- /dev/null +++ b/drivers/mtd/maps/scb2_flash.c @@ -0,0 +1,256 @@ +/* + * MTD map driver for BIOS Flash on Intel SCB2 boards + * $Id: scb2_flash.c,v 1.6 2003/05/21 12:45:20 dwmw2 Exp $ + * Copyright (C) 2002 Sun Microsystems, Inc. + * Tim Hockin <thockin@sun.com> + * + * A few notes on this MTD map: + * + * This was developed with a small number of SCB2 boards to test on. + * Hopefully, Intel has not introducted too many unaccounted variables in the + * making of this board. + * + * The BIOS marks its own memory region as 'reserved' in the e820 map. We + * 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 Mib) 16 bit chip + * * a 1 MiB memory region + * * A20 and A21 pulled up + * * D8-D15 ignored + * What this means is that, while we are addressing bytes linearly, we are + * really addressing words, and discarding the other byte. This means that + * the chip MUST BE at least 2 MiB. This also means that every block is + * actually half as big as the chip reports. It also means that accesses of + * logical address 0 hit higher-address sections of the chip, not physical 0. + * One can only hope that these 4MiB x16 chips were a lot cheaper than 1MiB x8 + * chips. + * + * This driver assumes the chip is not write-protected by an external signal. + * As of the this writing, that is true, but may change, just to spite me. + * + * The actual BIOS layout has been mostly reverse engineered. Intel BIOS + * updates for this board include 10 related (*.bio - &.bi9) binary files and + * another seperate (*.bbo) binary file. The 10 files are 64k of data + a + * small header. If the headers are stripped off, the 10 64k files can be + * concatenated into a 640k image. This is your BIOS image, proper. The + * seperate .bbo file also has a small header. It is the 'Boot Block' + * recovery BIOS. Once the header is stripped, no further prep is needed. + * As best I can tell, the BIOS is arranged as such: + * offset 0x00000 to 0x4ffff (320k): unknown - SCSI BIOS, etc? + * offset 0x50000 to 0xeffff (640k): BIOS proper + * offset 0xf0000 ty 0xfffff (64k): Boot Block region + * + * Intel's BIOS update program flashes the BIOS and Boot Block in seperate + * steps. Probably a wise thing to do. + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/cfi.h> +#include <linux/config.h> +#include <linux/pci.h> +#include <linux/pci_ids.h> + +#define MODNAME "scb2_flash" +#define SCB2_ADDR 0xfff00000 +#define SCB2_WINDOW 0x00100000 + + +static void *scb2_ioaddr; +static struct mtd_info *scb2_mtd; +struct map_info scb2_map = { + .name = "SCB2 BIOS Flash", + .size = 0, + .buswidth = 1, +}; +static int region_fail; + +static int __devinit +scb2_fixup_mtd(struct mtd_info *mtd) +{ + int i; + int done = 0; + struct map_info *map = mtd->priv; + struct cfi_private *cfi = map->fldrv_priv; + + /* barf if this doesn't look right */ + if (cfi->cfiq->InterfaceDesc != 1) { + printk(KERN_ERR MODNAME ": unsupported InterfaceDesc: %#x\n", + cfi->cfiq->InterfaceDesc); + 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; + + /* + * We only REALLY get half the chip, due to the way it is + * wired up - D8-D15 are tossed away. We read linear bytes, + * but in reality we are getting 1/2 of each 16-bit read, + * which LOOKS linear to us. Because CFI code accounts for + * things like lock/unlock/erase by eraseregions, we need to + * fudge them to reflect this. Erases go like this: + * * send an erase to an address + * * the chip samples the address and erases the block + * * add the block erasesize to the address and repeat + * -- the problem is that addresses are 16-bit addressable + * -- we end up erasing every-other block + */ + mtd->erasesize /= 2; + for (i = 0; i < mtd->numeraseregions; i++) { + struct mtd_erase_region_info *region = &mtd->eraseregions[i]; + region->erasesize /= 2; + } + + /* + * If the chip is bigger than the map, it is wired with the high + * address lines pulled up. This makes us access the top portion of + * the chip, so all our erase-region info is wrong. Start cutting from + * the bottom. + */ + for (i = 0; !done && i < mtd->numeraseregions; i++) { + struct mtd_erase_region_info *region = &mtd->eraseregions[i]; + + if (region->numblocks * region->erasesize > mtd->size) { + region->numblocks = (mtd->size / region->erasesize); + done = 1; + } else { + region->numblocks = 0; + } + region->offset = 0; + } + + return 0; +} + +/* CSB5's 'Function Control Register' has bits for decoding @ >= 0xffc00000 */ +#define CSB5_FCR 0x41 +#define CSB5_FCR_DECODE_ALL 0x0e +static int __devinit +scb2_flash_probe(struct pci_dev *dev, const struct pci_device_id *ent) +{ + u8 reg; + + /* enable decoding of the flash region in the south bridge */ + pci_read_config_byte(dev, CSB5_FCR, ®); + pci_write_config_byte(dev, CSB5_FCR, reg | CSB5_FCR_DECODE_ALL); + + if (!request_mem_region(SCB2_ADDR, SCB2_WINDOW, scb2_map.name)) { + /* + * The BIOS seems to mark the flash region as 'reserved' + * in the e820 map. Warn and go about our business. + */ + printk(KERN_WARNING MODNAME + ": warning - can't reserve rom window, continuing\n"); + region_fail = 1; + } + + /* remap the IO window (w/o caching) */ + scb2_ioaddr = ioremap_nocache(SCB2_ADDR, SCB2_WINDOW); + if (!scb2_ioaddr) { + printk(KERN_ERR MODNAME ": Failed to ioremap window!\n"); + if (!region_fail) + release_mem_region(SCB2_ADDR, SCB2_WINDOW); + return -ENOMEM; + } + + 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); + + if (!scb2_mtd) { + printk(KERN_ERR MODNAME ": flash probe failed!\n"); + iounmap(scb2_ioaddr); + if (!region_fail) + release_mem_region(SCB2_ADDR, SCB2_WINDOW); + return -ENODEV; + } + + scb2_mtd->owner = THIS_MODULE; + if (scb2_fixup_mtd(scb2_mtd) < 0) { + del_mtd_device(scb2_mtd); + map_destroy(scb2_mtd); + iounmap(scb2_ioaddr); + if (!region_fail) + release_mem_region(SCB2_ADDR, SCB2_WINDOW); + return -ENODEV; + } + + 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); + + return 0; +} + +static void __devexit +scb2_flash_remove(struct pci_dev *dev) +{ + if (!scb2_mtd) + return; + + /* disable flash writes */ + if (scb2_mtd->lock) + scb2_mtd->lock(scb2_mtd, 0, scb2_mtd->size); + + del_mtd_device(scb2_mtd); + map_destroy(scb2_mtd); + + iounmap(scb2_ioaddr); + scb2_ioaddr = NULL; + + if (!region_fail) + release_mem_region(SCB2_ADDR, SCB2_WINDOW); + pci_set_drvdata(dev, NULL); +} + +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 + }, + { 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), +}; + +static int __init +scb2_flash_init(void) +{ + return pci_module_init(&scb2_flash_driver); +} + +static void __exit +scb2_flash_exit(void) +{ + pci_unregister_driver(&scb2_flash_driver); +} + +module_init(scb2_flash_init); +module_exit(scb2_flash_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Tim Hockin <thockin@sun.com>"); +MODULE_DESCRIPTION("MTD map driver for Intel SCB2 BIOS Flash"); +MODULE_DEVICE_TABLE(pci, scb2_flash_pci_ids); diff --git a/drivers/mtd/maps/scx200_docflash.c b/drivers/mtd/maps/scx200_docflash.c index 64583df88707..c909f4bda5ee 100644 --- a/drivers/mtd/maps/scx200_docflash.c +++ b/drivers/mtd/maps/scx200_docflash.c @@ -2,6 +2,8 @@ Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com> + $Id: scx200_docflash.c,v 1.5 2003/05/21 12:45:20 dwmw2 Exp $ + National Semiconductor SCx200 flash mapped with DOCCS */ @@ -9,6 +11,7 @@ #include <linux/config.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> @@ -36,7 +39,7 @@ MODULE_PARM(flashtype, "s"); MODULE_PARM_DESC(flashtype, "Type of MTD probe to do"); static int probe = 0; /* Don't autoprobe */ -static unsigned size = 0x1000000; /* 16 MB the whole ISA address space */ +static unsigned size = 0x1000000; /* 16 MiB the whole ISA address space */ static unsigned width = 8; /* Default to 8 bits wide */ static char *flashtype = "cfi_probe"; @@ -73,46 +76,9 @@ static struct mtd_partition partition_info[] = { #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) @@ -211,8 +177,11 @@ int __init init_scx200_docflash(void) 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; @@ -221,7 +190,7 @@ int __init init_scx200_docflash(void) 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; } @@ -229,7 +198,7 @@ int __init init_scx200_docflash(void) 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; @@ -251,8 +220,8 @@ static void __exit cleanup_scx200_docflash(void) #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 --git a/drivers/mtd/maps/solutionengine.c b/drivers/mtd/maps/solutionengine.c index 9aa062fcf688..5804c996ba94 100644 --- a/drivers/mtd/maps/solutionengine.c +++ b/drivers/mtd/maps/solutionengine.c @@ -1,5 +1,5 @@ /* - * $Id: solutionengine.c,v 1.3 2001/10/02 15:05:14 dwmw2 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,29 +11,12 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> - - -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); -} +#include <linux/config.h> static struct mtd_info *flash_mtd; @@ -42,36 +25,58 @@ 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, + }, + /* All else is writable (e.g. JFFS) */ + { + .name = "Flash FS", + .offset = MTDPART_OFS_NXTBLK, + .size = MTDPART_SIZ_FULL, + } }; +#endif /* CONFIG_MTD_SUPERH_RESERVE */ static int __init init_soleng_maps(void) { - int nr_parts; + int nr_parts = 0; /* First probe at offset 0 */ - soleng_flash_map.map_priv_1 = P2SEGADDR(0); - soleng_eprom_map.map_priv_1 = P1SEGADDR(0x400000); - - printk(KERN_NOTICE "Probing for flash chips at 0x000000:\n"); + 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 0x400000:\n"); - soleng_flash_map.map_priv_1 = P2SEGADDR(0x400000); - soleng_eprom_map.map_priv_1 = P1SEGADDR(0); + printk(KERN_NOTICE "Probing for flash chips at 0x01000000:\n"); + 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. */ @@ -80,19 +85,28 @@ static int __init init_soleng_maps(void) } } 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); } - nr_parts = parse_redboot_partitions(flash_mtd, &parsed_parts); + nr_parts = parse_mtd_partitions(flash_mtd, probes, &parsed_parts, 0); + +#if CONFIG_MTD_SUPERH_RESERVE + if (nr_parts <= 0) { + printk(KERN_NOTICE "Using configured partition at 0x%08x.\n", + CONFIG_MTD_SUPERH_RESERVE); + parsed_parts = superh_se_partitions; + nr_parts = sizeof(superh_se_partitions)/sizeof(*parsed_parts); + } +#endif /* CONFIG_MTD_SUPERH_RESERVE */ - if (nr_parts) + if (nr_parts > 0) add_mtd_partitions(flash_mtd, parsed_parts, nr_parts); else add_mtd_device(flash_mtd); diff --git a/drivers/mtd/maps/sun_uflash.c b/drivers/mtd/maps/sun_uflash.c index aef5fd170de7..983db7131675 100644 --- a/drivers/mtd/maps/sun_uflash.c +++ b/drivers/mtd/maps/sun_uflash.c @@ -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 uflash_dev { 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 @@ int uflash_devinit(struct linux_ebus_device* edev) 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 @@ int uflash_devinit(struct linux_ebus_device* edev) 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 @@ static void __exit uflash_cleanup(void) 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 --git a/drivers/mtd/maps/tqm8xxl.c b/drivers/mtd/maps/tqm8xxl.c index 5af066d0f2a4..1112440fc992 100644 --- a/drivers/mtd/maps/tqm8xxl.c +++ b/drivers/mtd/maps/tqm8xxl.c @@ -2,7 +2,7 @@ * Handle mapping of the flash memory access routines * on TQM8xxL based devices. * - * $Id: tqm8xxl.c,v 1.3 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: tqm8xxl.c,v 1.8 2003/05/21 12:45:20 dwmw2 Exp $ * * based on rpxlite.c * @@ -26,6 +26,7 @@ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> +#include <linux/init.h> #include <asm/io.h> #include <linux/mtd/mtd.h> @@ -51,59 +52,6 @@ static struct mtd_part_def part_banks[FLASH_BANK_MAX]; 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); -} - -struct map_info tqm8xxl_map = { - .name = "TQM8xxL", - .buswidth = 4, - .read8 = tqm8xxl_read8, - .read16 = tqm8xxl_read16, - .read32 = tqm8xxl_read32, - .copy_from = tqm8xxl_copy_from, - .write8 = tqm8xxl_write8, - .write16 = tqm8xxl_write16, - .write32 = tqm8xxl_write32, - .copy_to = tqm8xxl_copy_to -}; - /* * Here are partition information for all known TQM8xxL series devices. * See include/linux/mtd/partitions.h for definition of the mtd_partition @@ -120,43 +68,44 @@ struct map_info tqm8xxl_map = { 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, + .name = "jffs", + .offset = 0x00200000, + .size = 0x00200000, + .//size = MTDPART_SIZ_FULL, } }; #endif @@ -172,66 +121,73 @@ int __init init_tqm_mtd(void) 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; - map_banks[idx]->map_priv_1 = - start_scan_addr + ((idx > 0) ? + + 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; } @@ -243,11 +199,12 @@ int __init init_tqm_mtd(void) part_banks[0].mtd_part = tqm8xxl_partitions; part_banks[0].type = "Static image"; part_banks[0].nums = NB_OF(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++) - { + + 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]); @@ -265,12 +222,9 @@ int __init init_tqm_mtd(void) #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; } @@ -278,18 +232,15 @@ error_mem: 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]); @@ -299,6 +250,7 @@ static void __exit cleanup_tqm_mtd(void) kfree(map_banks[idx]->name); kfree(map_banks[idx]); } + if (start_scan_addr) { iounmap((void *)start_scan_addr); start_scan_addr = 0; diff --git a/drivers/mtd/maps/tsunami_flash.c b/drivers/mtd/maps/tsunami_flash.c new file mode 100644 index 000000000000..d4c9d28e25e3 --- /dev/null +++ b/drivers/mtd/maps/tsunami_flash.c @@ -0,0 +1,106 @@ +/* + * tsunami_flash.c + * + * flash chip on alpha ds10... + * $Id: tsunami_flash.c,v 1.6 2003/05/21 15:15:08 dwmw2 Exp $ + */ +#include <asm/io.h> +#include <asm/core_tsunami.h> +#include <linux/init.h> +#include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> + +#define FLASH_ENABLE_PORT 0x00C00001 +#define FLASH_ENABLE_BYTE 0x01 +#define FLASH_DISABLE_BYTE 0x00 + +#define MAX_TIG_FLASH_SIZE (12*1024*1024) +static inline __u8 tsunami_flash_read8(struct map_info *map, unsigned long offset) +{ + return tsunami_tig_readb(offset); +} + +static void tsunami_flash_write8(struct map_info *map, __u8 value, unsigned long offset) +{ + tsunami_tig_writeb(value, offset); +} + +static void tsunami_flash_copy_from( + struct map_info *map, void *addr, unsigned long offset, ssize_t len) +{ + unsigned char *dest; + dest = addr; + while(len && (offset < MAX_TIG_FLASH_SIZE)) { + *dest = tsunami_tig_readb(offset); + offset++; + dest++; + len--; + } +} + +static void tsunami_flash_copy_to( + struct map_info *map, unsigned long offset, + const void *addr, ssize_t len) +{ + const unsigned char *src; + src = addr; + while(len && (offset < MAX_TIG_FLASH_SIZE)) { + tsunami_tig_writeb(*src, offset); + offset++; + src++; + len--; + } +} + +/* + * Deliberately don't provide operations wider than 8 bits. I don't + * have then and it scares me to think how you could mess up if + * you tried to use them. Buswidth is correctly so I'm safe. + */ +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, + .copy_from = tsunami_flash_copy_from, + .write8 = tsunami_flash_write8, + .copy_to = tsunami_flash_copy_to, +}; + +static struct mtd_info *tsunami_flash_mtd; + +static void __exit cleanup_tsunami_flash(void) +{ + struct mtd_info *mtd; + mtd = tsunami_flash_mtd; + if (mtd) { + del_mtd_device(mtd); + map_destroy(mtd); + } + tsunami_flash_mtd = 0; +} + + +static int __init init_tsunami_flash(void) +{ + static const char *rom_probe_types[] = { "cfi_probe", "jedec_probe", "map_rom", 0 }; + char **type; + + tsunami_tig_writeb(FLASH_ENABLE_BYTE, FLASH_ENABLE_PORT); + + tsunami_flash_mtd = 0; + type = rom_probe_types; + for(; !tsunami_flash_mtd && *type; type++) { + tsunami_flash_mtd = do_map_probe(*type, &tsunami_flash_map); + } + if (tsunami_flash_mtd) { + tsunami_flash_mtd->owner = THIS_MODULE; + add_mtd_device(tsunami_flash_mtd); + return 0; + } + return -ENXIO; +} + +module_init(init_tsunami_flash); +module_exit(cleanup_tsunami_flash); diff --git a/drivers/mtd/maps/uclinux.c b/drivers/mtd/maps/uclinux.c index 63b3275bcb6f..f722390661f6 100644 --- a/drivers/mtd/maps/uclinux.c +++ b/drivers/mtd/maps/uclinux.c @@ -4,6 +4,8 @@ * uclinux.c -- generic memory mapped MTD driver for uclinux * * (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com) + * + * $Id: uclinux.c,v 1.5 2003/05/20 20:59:32 dwmw2 Exp $ */ /****************************************************************************/ @@ -15,7 +17,6 @@ #include <linux/kernel.h> #include <linux/fs.h> #include <linux/major.h> -#include <linux/root_dev.h> #include <linux/mtd/mtd.h> #include <linux/mtd/map.h> #include <linux/mtd/partitions.h> @@ -23,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; @@ -82,7 +36,7 @@ struct mtd_info *uclinux_ram_mtdinfo; /****************************************************************************/ struct mtd_partition uclinux_romfs[] = { - { .name = "ROMfs", .offset = 0 } + { .name = "ROMfs" } }; #define NUM_PARTITIONS (sizeof(uclinux_romfs) / sizeof(uclinux_romfs[0])) @@ -93,7 +47,7 @@ int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len, 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); } @@ -107,29 +61,31 @@ int __init uclinux_mtd_init(void) 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; @@ -137,7 +93,7 @@ int __init uclinux_mtd_init(void) add_mtd_partitions(mtd, uclinux_romfs, NUM_PARTITIONS); printk("uclinux[mtd]: set %s to be root filesystem\n", - uclinux_romfs[0].name); + uclinux_romfs[0].name); ROOT_DEV = MKDEV(MTD_BLOCK_MAJOR, 0); put_mtd_device(mtd); @@ -154,8 +110,8 @@ void __exit uclinux_mtd_cleanup(void) 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 --git a/drivers/mtd/maps/vmax301.c b/drivers/mtd/maps/vmax301.c index 4c68f5384ead..a649609fdc78 100644 --- a/drivers/mtd/maps/vmax301.c +++ b/drivers/mtd/maps/vmax301.c @@ -1,4 +1,4 @@ -// $Id: vmax301.c,v 1.24 2001/10/02 15:05:14 dwmw2 Exp $ +// $Id: vmax301.c,v 1.28 2003/05/21 15:15:08 dwmw2 Exp $ /* ###################################################################### Tempustech VMAX SBC301 MTD Driver. @@ -24,6 +24,7 @@ #include <asm/io.h> #include <linux/mtd/map.h> +#include <linux/mtd/mtd.h> #define WINDOW_START 0xd8000 @@ -142,33 +143,36 @@ static void vmax301_copy_to(struct map_info *map, unsigned long to, const void * 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", - .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 } }; @@ -205,8 +209,8 @@ int __init init_vmax301(void) 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]); @@ -217,7 +221,7 @@ int __init init_vmax301(void) 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 --git a/drivers/mtd/mtd_blkdevs.c b/drivers/mtd/mtd_blkdevs.c new file mode 100644 index 000000000000..0cc4998ba441 --- /dev/null +++ b/drivers/mtd/mtd_blkdevs.c @@ -0,0 +1,456 @@ +/* + * $Id: mtd_blkdevs.c,v 1.12 2003/05/21 01:00:59 dwmw2 Exp $ + * + * (C) 2003 David Woodhouse <dwmw2@infradead.org> + * + * Interface to Linux 2.5 block layer for MTD 'translation layers'. + * + */ + +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/list.h> +#include <linux/fs.h> +#include <linux/mtd/blktrans.h> +#include <linux/mtd/mtd.h> +#include <linux/blkdev.h> +#include <linux/blk.h> +#include <linux/blkpg.h> +#include <linux/spinlock.h> +#include <linux/init.h> +#include <asm/semaphore.h> +#include <linux/devfs_fs_kernel.h> + +static LIST_HEAD(blktrans_majors); + +extern struct semaphore mtd_table_mutex; +extern struct mtd_info *mtd_table[]; + +struct mtd_blkcore_priv { + struct completion thread_dead; + int exiting; + wait_queue_head_t thread_wq; + struct request_queue rq; + spinlock_t queue_lock; +}; + +static int do_blktrans_request(struct mtd_blktrans_ops *tr, + struct mtd_blktrans_dev *dev, + struct request *req) +{ + unsigned long block, nsect; + char *buf; + + block = req->sector; + nsect = req->current_nr_sectors; + buf = req->buffer; + + if (!req->flags & REQ_CMD) + return 0; + + if (block + nsect > get_capacity(req->rq_disk)) + return 0; + + switch(rq_data_dir(req)) { + case READ: + for (; nsect > 0; nsect--, block++, buf += 512) + if (tr->readsect(dev, block, buf)) + return 0; + return 1; + + case WRITE: + if (!tr->writesect) + return 0; + + for (; nsect > 0; nsect--, block++, buf += 512) + if (tr->writesect(dev, block, buf)) + return 0; + return 1; + + default: + printk(KERN_NOTICE "Unknown request %ld\n", rq_data_dir(req)); + return 0; + } +} + +static int mtd_blktrans_thread(void *arg) +{ + struct mtd_blktrans_ops *tr = arg; + struct request_queue *rq = &tr->blkcore_priv->rq; + + /* we might get involved when memory gets low, so use PF_MEMALLOC */ + current->flags |= PF_MEMALLOC; + + daemonize("%sd", tr->name); + + /* daemonize() doesn't do this for us since some kernel threads + actually want to deal with signals. We can't just call + exit_sighand() since that'll cause an oops when we finally + do exit. */ + spin_lock_irq(¤t->sighand->siglock); + sigfillset(¤t->blocked); + recalc_sigpending(); + spin_unlock_irq(¤t->sighand->siglock); + + while (!tr->blkcore_priv->exiting) { + struct request *req; + struct mtd_blktrans_dev *dev; + int res = 0; + DECLARE_WAITQUEUE(wait, current); + + spin_lock_irq(rq->queue_lock); + + 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); + + 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, i, f))) { + 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, i, f); + + 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; + struct mtd_blktrans_ops *tr; + int ret = -ENOTTY; + + dev = inode->i_bdev->bd_disk->private_data; + tr = dev->tr; + + if (tr->ioctl) + ret = tr->ioctl(dev, inode, file, cmd, arg); + + if (ret == -ENOTTY && (cmd == BLKROSET || cmd == BLKFLSBUF)) { + /* The core code did the work, we had nothing to do. */ + ret = 0; + } + return ret; +} + +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); + + set_capacity(gd, new->size); + 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; + + printk("%s:%s %d: count %d\n", __FILE__, __func__, __LINE__, atomic_read(&mtd_table_mutex.count)); + + 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); + + blk_init_queue(&tr->blkcore_priv->rq, mtd_blktrans_request, + &tr->blkcore_priv->queue_lock); + 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); + + printk("%s:%s %d: count %d\n", __FILE__, __func__, __LINE__, atomic_read(&mtd_table_mutex.count)); + + for (i=0; i<MAX_MTD_DEVICES; i++) { + if (mtd_table[i] && mtd_table[i]->type != MTD_ABSENT) + tr->add_mtd(tr, mtd_table[i]); + } + + up(&mtd_table_mutex); + + return 0; +} + +int deregister_mtd_blktrans(struct mtd_blktrans_ops *tr) +{ + struct list_head *this, *next; + + down(&mtd_table_mutex); + + /* Clean up the kernel thread */ + tr->blkcore_priv->exiting = 1; + wake_up(&tr->blkcore_priv->thread_wq); + wait_for_completion(&tr->blkcore_priv->thread_dead); + + /* Remove it from the list of active majors */ + list_del(&tr->list); + + list_for_each_safe(this, next, &tr->devs) { + struct mtd_blktrans_dev *dev = list_entry(this, struct mtd_blktrans_dev, list); + tr->remove_dev(dev); + } + + devfs_remove(tr->name); + blk_cleanup_queue(&tr->blkcore_priv->rq); + unregister_blkdev(tr->major, tr->name); + + up(&mtd_table_mutex); + + kfree(tr->blkcore_priv); + + if (!list_empty(&tr->devs)) + BUG(); + return 0; +} + +static void __exit mtd_blktrans_exit(void) +{ + /* No race here -- if someone's currently in register_mtd_blktrans + we're screwed anyway. */ + if (blktrans_notifier.list.next) + unregister_mtd_user(&blktrans_notifier); +} + +module_exit(mtd_blktrans_exit); + +EXPORT_SYMBOL_GPL(register_mtd_blktrans); +EXPORT_SYMBOL_GPL(deregister_mtd_blktrans); +EXPORT_SYMBOL_GPL(add_mtd_blktrans_dev); +EXPORT_SYMBOL_GPL(del_mtd_blktrans_dev); + +MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("Common interface to block layer for MTD 'translation layers'"); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 815dbfe4ad83..d4dc3b67807a 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -1,37 +1,25 @@ /* * Direct MTD block device access * - * $Id: mtdblock.c,v 1.47 2001/10/02 15:05:11 dwmw2 Exp $ + * $Id: mtdblock.c,v 1.61 2003/05/21 10:49:38 dwmw2 Exp $ * - * 02-nov-2000 Nicolas Pitre Added read-modify-write with cache + * (C) 2000-2003 Nicolas Pitre <nico@cam.org> + * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> */ #include <linux/config.h> #include <linux/types.h> #include <linux/module.h> #include <linux/kernel.h> +#include <linux/fs.h> +#include <linux/init.h> #include <linux/slab.h> -#include <linux/buffer_head.h> +#include <linux/vmalloc.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/compatmac.h> -#include <linux/buffer_head.h> - -#define MAJOR_NR MTD_BLOCK_MAJOR -#define DEVICE_NAME "mtdblock" -#define DEVICE_NR(device) (device) -#include <linux/blk.h> -#include <linux/devfs_fs_kernel.h> - -static void mtd_notify_add(struct mtd_info* mtd); -static void mtd_notify_remove(struct mtd_info* mtd); -static struct mtd_notifier notifier = { - mtd_notify_add, - mtd_notify_remove, - NULL -}; +#include <linux/mtd/blktrans.h> static struct mtdblk_dev { - struct mtd_info *mtd; /* Locked */ + struct mtd_info *mtd; int count; struct semaphore cache_sem; unsigned char *cache_data; @@ -40,10 +28,6 @@ static struct mtdblk_dev { enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; } *mtdblks[MAX_MTD_DEVICES]; -static struct gendisk *mtddisk[MAX_MTD_DEVICES]; - -static spinlock_t mtdblks_lock; - /* * Cache stuff... * @@ -127,7 +111,7 @@ static int write_cached_data (struct mtdblk_dev *mtdblk) 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 @@ -253,48 +237,39 @@ static int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, return 0; } -static struct block_device_operations mtd_fops; +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]; + 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 inode *inode, struct file *file) { struct mtdblk_dev *mtdblk; - struct mtd_info *mtd; - int dev = minor(inode->i_rdev); - struct gendisk *disk; + struct mtd_info *mtd = mbd->mtd; + int dev = mbd->devnum; DEBUG(MTD_DEBUG_LEVEL1,"mtdblock_open\n"); - - if (dev >= MAX_MTD_DEVICES) - return -EINVAL; - - mtd = get_mtd_device(NULL, dev); - if (!mtd) - return -ENODEV; - if (MTD_ABSENT == mtd->type) { - put_mtd_device(mtd); - 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); 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); - disk = mtddisk[dev]; - if (!mtdblk || !disk) - goto Enomem; + if (!mtdblks) + return -ENOMEM; + memset(mtdblk, 0, sizeof(*mtdblk)); mtdblk->count = 1; mtdblk->mtd = mtd; @@ -305,46 +280,26 @@ static int mtdblock_open(struct inode *inode, struct file *file) mtdblk->mtd->erasesize) { mtdblk->cache_size = mtdblk->mtd->erasesize; mtdblk->cache_data = vmalloc(mtdblk->mtd->erasesize); - if (!mtdblk->cache_data) - goto 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; + if (!mtdblk->cache_data) { + kfree(mtdblk); + return -ENOMEM; + } } mtdblks[dev] = mtdblk; - set_device_ro(inode->i_bdev, !(mtdblk->mtd->flags & MTD_WRITEABLE)); - - spin_unlock(&mtdblks_lock); - + DEBUG(MTD_DEBUG_LEVEL1, "ok\n"); return 0; -Enomem: - put_mtd_device(mtd); - kfree(mtdblk); - return -ENOMEM; } -static release_t mtdblock_release(struct inode *inode, struct file *file) +static int mtdblock_release(struct mtd_blktrans_dev *mbd, + struct inode *inode, struct file *file) { int dev; struct mtdblk_dev *mtdblk; - DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); - if (inode == NULL) - release_return(-ENODEV); + DEBUG(MTD_DEBUG_LEVEL1, "mtdblock_release\n"); dev = minor(inode->i_rdev); mtdblk = mtdblks[dev]; @@ -353,149 +308,30 @@ static release_t mtdblock_release(struct inode *inode, struct file *file) 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"); - 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 queue_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 struct request_queue mtd_queue; -static void handle_mtdblock_request(void) -{ - struct request *req; - struct mtdblk_dev *mtdblk; - unsigned int res; - - while ((req = elv_next_request(&mtd_queue)) != NULL) { - struct mtdblk_dev **p = req->rq_disk->private_data; - spin_unlock_irq(mtd_queue.queue_lock); - mtdblk = *p; - res = 0; - - if (! (req->flags & REQ_CMD)) - goto end_req; - - if ((req->sector + req->current_nr_sectors) > (mtdblk->mtd->size >> 9)) - goto end_req; - - // Handle the request - switch (rq_data_dir(req)) - { - 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(mtd_queue.queue_lock); - if (!end_that_request_first(req, res, req->hard_cur_sectors)) { - blkdev_dequeue_request(req); - end_that_request_last(req); - } - - } -} - -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; - daemonize("mtdblockd"); - - while (!leaving) { - add_wait_queue(&thr_wq, &wait); - set_current_state(TASK_INTERRUPTIBLE); - spin_lock_irq(mtd_queue.queue_lock); - if (!elv_next_request(&mtd_queue) || blk_queue_plugged(&mtd_queue)) { - spin_unlock_irq(mtd_queue.queue_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(mtd_queue.queue_lock); - } - } - - up(&thread_sem); return 0; -} - -static void mtdblock_request(struct request_queue *q) -{ - /* Don't do anything, except wake the thread if necessary */ - wake_up(&thr_wq); -} +} -static int mtdblock_ioctl(struct inode * inode, struct file * file, - unsigned int cmd, unsigned long arg) +static int mtdblock_ioctl(struct mtd_blktrans_dev *dev, + 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 BLKFLSBUF: - fsync_bdev(inode->i_bdev); - invalidate_bdev(inode->i_bdev, 0); down(&mtdblk->cache_sem); write_cached_data(mtdblk); up(&mtdblk->cache_sem); @@ -504,90 +340,59 @@ static int mtdblock_ioctl(struct inode * inode, struct file * file, return 0; default: - return -EINVAL; + return -ENOTTY; } } -static struct block_device_operations mtd_fops = -{ - .owner = THIS_MODULE, - .open = mtdblock_open, - .release = mtdblock_release, - .ioctl = mtdblock_ioctl -}; - -/* Notification that a new device has been added. Create the devfs entry for - * it. */ - -static void mtd_notify_add(struct mtd_info* mtd) +static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - struct gendisk *disk; - char name[16]; + struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); - if (!mtd || mtd->type == MTD_ABSENT) - return; + if (!dev) + return; - disk = alloc_disk(1); - if (disk) { - disk->major = MAJOR_NR; - disk->first_minor = mtd->index; - disk->fops = &mtd_fops; + memset(dev, 0, sizeof(*dev)); - sprintf(disk->disk_name, "mtdblock%d", mtd->index); - sprintf(disk->devfs_name, "mtdblock/%d", mtd->index); + dev->mtd = mtd; + dev->devnum = mtd->index; + dev->blksize = 512; + dev->size = mtd->size >> 9; + dev->tr = tr; - mtddisk[mtd->index] = disk; - set_capacity(disk, mtd->size / 512); - disk->private_data = &mtdblks[mtd->index]; - disk->queue = &mtd_queue; + if (!(mtd->flags & MTD_WRITEABLE)) + dev->readonly = 1; - add_disk(disk); - } + add_mtd_blktrans_dev(dev); } -static void mtd_notify_remove(struct mtd_info* mtd) +static void mtdblock_remove_dev(struct mtd_blktrans_dev *dev) { - if (!mtd || mtd->type == MTD_ABSENT) - return; - - if (mtddisk[mtd->index]) { - del_gendisk(mtddisk[mtd->index]); - put_disk(mtddisk[mtd->index]); - mtddisk[mtd->index] = NULL; - } + del_mtd_blktrans_dev(dev); + kfree(dev); } -static spinlock_t mtddev_lock = SPIN_LOCK_UNLOCKED; +struct mtd_blktrans_ops mtdblock_tr = { + .name = "mtdblock", + .major = 31, + .part_bits = 0, + .open = mtdblock_open, + .ioctl = mtdblock_ioctl, + .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) { - spin_lock_init(&mtdblks_lock); - - if (register_blkdev(MAJOR_NR, DEVICE_NAME)) - return -EAGAIN; - -#ifdef CONFIG_DEVFS_FS - devfs_mk_dir(DEVICE_NAME); -#endif - register_mtd_user(¬ifier); - - init_waitqueue_head(&thr_wq); - blk_init_queue(&mtd_queue, &mtdblock_request, &mtddev_lock); - 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); - unregister_mtd_user(¬ifier); -#ifdef CONFIG_DEVFS_FS - devfs_remove(DEVICE_NAME); -#endif - unregister_blkdev(MAJOR_NR,DEVICE_NAME); - blk_cleanup_queue(&mtd_queue); + deregister_mtd_blktrans(&mtdblock_tr); } module_init(init_mtdblock); diff --git a/drivers/mtd/mtdblock.h b/drivers/mtd/mtdblock.h new file mode 100644 index 000000000000..f4c77fe41f92 --- /dev/null +++ b/drivers/mtd/mtdblock.h @@ -0,0 +1,35 @@ +/* + * drivers/mtd/mtdblock.h + * + * common defines for mtdblock-core and mtdblock-2x + * + * $Id: mtdblock.h,v 1.1 2002/11/27 10:33:37 gleixner Exp $ + * + */ + +#ifndef __MTD_MTDBLOCK_H__ +#define __MTD_MTDBLOCK_H__ + +#define MAJOR_NR MTD_BLOCK_MAJOR +#define DEVICE_NAME "mtdblock" + +struct mtdblk_dev { + struct mtd_info *mtd; /* Locked */ + int count; + struct semaphore cache_sem; + unsigned char *cache_data; + unsigned long cache_offset; + unsigned int cache_size; + enum { STATE_EMPTY, STATE_CLEAN, STATE_DIRTY } cache_state; +}; + +extern int write_cached_data (struct mtdblk_dev *mtdblk); +extern int do_cached_write (struct mtdblk_dev *mtdblk, unsigned long pos, + int len, const char *buf); +extern int do_cached_read (struct mtdblk_dev *mtdblk, unsigned long pos, + int len, char *buf); + +extern void __exit cleanup_mtdblock(void); +extern int __init init_mtdblock(void); + +#endif diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index f29b2c23e1fa..e9133d25f6f2 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -1,265 +1,86 @@ /* - * $Id: mtdblock_ro.c,v 1.13 2002/03/11 16:03:29 sioux Exp $ + * $Id: mtdblock_ro.c,v 1.17 2003/05/18 19:27:27 dwmw2 Exp $ * - * Read-only flash, read-write RAM version of the mtdblock device, - * without caching. + * (C) 2003 David Woodhouse <dwmw2@infradead.org> + * + * Simple read-only (writable only for RAM) mtdblock driver */ -#ifdef MTDBLOCK_DEBUG -#define DEBUGLVL debug -#endif - - -#include <linux/module.h> -#include <linux/types.h> - +#include <linux/slab.h> #include <linux/mtd/mtd.h> -#include <linux/mtd/compatmac.h> -#include <linux/buffer_head.h> -#include <linux/genhd.h> +#include <linux/mtd/blktrans.h> -#define MAJOR_NR MTD_BLOCK_MAJOR -#define DEVICE_NAME "mtdblock" -#include <linux/blk.h> - -#ifdef MTDBLOCK_DEBUG -static int debug = MTDBLOCK_DEBUG; -MODULE_PARM(debug, "i"); -#endif - -struct mtdro_dev { - struct gendisk *disk; - struct mtd_info *mtd; - int open; -}; - -static struct mtdro_dev mtd_dev[MAX_MTD_DEVICES]; -static DECLARE_MUTEX(mtd_sem); - -static struct request_queue mtdro_queue; -static spinlock_t mtdro_lock = SPIN_LOCK_UNLOCKED; - -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 mtdro_dev *mdev = inode->i_bdev->bd_disk->private_data; - int ret = 0; - - DEBUG(1,"mtdblock_open\n"); - - down(&mtd_sem); - if (mdev->mtd == NULL) { - mdev->mtd = get_mtd_device(NULL, minor(inode->i_rdev)); - if (!mdev->mtd || mdev->mtd->type == MTD_ABSENT) { - if (mdev->mtd) - put_mtd_device(mdev->mtd); - ret = -ENODEV; - } - } - - if (ret == 0) { - set_device_ro(inode->i_bdev, !(mdev->mtd->flags & MTD_CAP_RAM)); - mdev->open++; - } - up(&mtd_sem); + size_t retlen; - DEBUG(1, "%s\n", ret ? "ok" : "nodev"); - - return ret; + 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) { - struct mtdro_dev *mdev = inode->i_bdev->bd_disk->private_data; - - DEBUG(1, "mtdblock_release\n"); - - down(&mtd_sem); - if (mdev->open-- == 0) { - struct mtd_info *mtd = mdev->mtd; - - mdev->mtd = NULL; - if (mtd->sync) - mtd->sync(mtd); - - put_mtd_device(mtd); - } - up(&mtd_sem); + size_t retlen; - DEBUG(1, "ok\n"); - - release_return(0); + if (dev->mtd->write(dev->mtd, (block * 512), 512, &retlen, buf)) + return 1; + return 0; } -static void mtdblock_request(request_queue_t *q) +static void mtdblock_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - struct request *req; - - while ((req = elv_next_request(q)) != NULL) { - struct mtdro_dev *mdev = req->rq_disk->private_data; - struct mtd_info *mtd = mdev->mtd; - unsigned int res; - - if (!(req->flags & REQ_CMD)) { - res = 0; - goto end_req; - } - - if ((req->sector + req->current_nr_sectors) > (mtd->size >> 9)) { - printk("mtd: Attempt to read past end of device!\n"); - printk("size: %x, sector: %lx, nr_sectors: %x\n", - mtd->size, req->sector, req->current_nr_sectors); - res = 0; - goto end_req; - } - - /* 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(q->queue_lock); - - /* Handle the request */ - switch (rq_data_dir(req)) { - size_t retlen; - - case READ: - if (MTD_READ(mtd, req->sector << 9, - req->current_nr_sectors << 9, - &retlen, req->buffer) == 0) - res = 1; - else - res = 0; - break; + struct mtd_blktrans_dev *dev = kmalloc(sizeof(*dev), GFP_KERNEL); - case WRITE: - /* printk("mtdblock_request WRITE sector=%d(%d)\n", - req->sector, req->current_nr_sectors); - */ + if (!dev) + return; - /* Read only device */ - if ((mtd->flags & MTD_CAP_RAM) == 0) { - res = 0; - break; - } + memset(dev, 0, sizeof(*dev)); - /* Do the write */ - if (MTD_WRITE(mtd, req->sector << 9, - req->current_nr_sectors << 9, - &retlen, req->buffer) == 0) - res = 1; - else - res = 0; - break; + 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; - /* Shouldn't happen */ - default: - printk("mtd: unknown request\n"); - res = 0; - break; - } - - /* Grab the lock and re-thread the item onto the linked list */ - spin_lock_irq(q->queue_lock); - end_req: - if (!end_that_request_first(req, res, req->hard_cur_sectors)) { - blkdev_dequeue_request(req); - end_that_request_last(req); - } - } + 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 mtdro_dev *mdev = inode->i_bdev->bd_disk->private_data; - - if (cmd != BLKFLSBUF) - return -EINVAL; - - fsync_bdev(inode->i_bdev); - invalidate_bdev(inode->i_bdev, 0); - if (mdev->mtd->sync) - mdev->mtd->sync(mdev->mtd); - - return 0; + del_mtd_blktrans_dev(dev); + kfree(dev); } -static struct block_device_operations mtd_fops = { +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, - .open = mtdblock_open, - .release = mtdblock_release, - .ioctl = mtdblock_ioctl -}; - -/* Called with mtd_table_mutex held. */ -static void mtd_notify_add(struct mtd_info* mtd) -{ - struct gendisk *disk; - - if (!mtd || mtd->type == MTD_ABSENT || mtd->index >= MAX_MTD_DEVICES) - return; - - disk = alloc_disk(1); - if (disk) { - disk->major = MAJOR_NR; - disk->first_minor = mtd->index; - disk->fops = &mtd_fops; - sprintf(disk->disk_name, "mtdblock%d", mtd->index); - - mtd_dev[mtd->index].disk = disk; - set_capacity(disk, mtd->size / 512); - disk->queue = &mtdro_queue; - disk->private_data = &mtd_dev[mtd->index]; - add_disk(disk); - } -} - -/* Called with mtd_table_mutex held. */ -static void mtd_notify_remove(struct mtd_info* mtd) -{ - struct mtdro_dev *mdev; - struct gendisk *disk; - - if (!mtd || mtd->type == MTD_ABSENT || mtd->index >= MAX_MTD_DEVICES) - return; - - mdev = &mtd_dev[mtd->index]; - - disk = mdev->disk; - mdev->disk = NULL; - - if (disk) { - del_gendisk(disk); - put_disk(disk); - } -} - -static struct mtd_notifier notifier = { - .add = mtd_notify_add, - .remove = mtd_notify_remove, }; -int __init init_mtdblock(void) +static int __init mtdblock_init(void) { - if (register_blkdev(MAJOR_NR, DEVICE_NAME)) - return -EAGAIN; - - blk_init_queue(&mtdro_queue, &mtdblock_request, &mtdro_lock); - register_mtd_user(¬ifier); - - return 0; + return register_mtd_blktrans(&mtdblock_tr); } -static void __exit cleanup_mtdblock(void) +static void __exit mtdblock_exit(void) { - unregister_mtd_user(¬ifier); - unregister_blkdev(MAJOR_NR,DEVICE_NAME); - blk_cleanup_queue(&mtdro_queue); + deregister_mtd_blktrans(&mtdblock_tr); } -module_init(init_mtdblock); -module_exit(cleanup_mtdblock); - +module_init(mtdblock_init); +module_exit(mtdblock_exit); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Erwin Authried <eauth@softsys.co.at> et al."); -MODULE_DESCRIPTION("Simple uncached block device emulation access to MTD devices"); +MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>"); +MODULE_DESCRIPTION("Simple read-only block device emulation access to MTD devices"); diff --git a/drivers/mtd/mtdchar.c b/drivers/mtd/mtdchar.c index dc2d9fc18231..5a7782514437 100644 --- a/drivers/mtd/mtdchar.c +++ b/drivers/mtd/mtdchar.c @@ -1,8 +1,7 @@ /* - * $Id: mtdchar.c,v 1.44 2001/10/02 15:05:11 dwmw2 Exp $ + * $Id: mtdchar.c,v 1.54 2003/05/21 10:50:43 dwmw2 Exp $ * * Character-device access to raw MTD devices. - * Pure 2.4 version - compatibility cruft removed to mtdchar-compat.c * */ @@ -10,8 +9,10 @@ #include <linux/kernel.h> #include <linux/module.h> #include <linux/mtd/mtd.h> -#include <linux/smp_lock.h> #include <linux/slab.h> +#include <linux/init.h> +#include <linux/fs.h> +#include <asm/uaccess.h> #ifdef CONFIG_DEVFS_FS #include <linux/devfs_fs_kernel.h> @@ -29,7 +30,6 @@ static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) { struct mtd_info *mtd=(struct mtd_info *)file->private_data; - lock_kernel(); switch (orig) { case 0: /* SEEK_SET */ @@ -44,7 +44,6 @@ static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) file->f_pos =mtd->size + offset; break; default: - unlock_kernel(); return -EINVAL; } @@ -53,7 +52,6 @@ static loff_t mtd_lseek (struct file *file, loff_t offset, int orig) else if (file->f_pos >= mtd->size) file->f_pos = mtd->size - 1; - unlock_kernel(); return file->f_pos; } @@ -288,7 +286,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, case MEMERASE: { - struct erase_info *erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL); + struct erase_info *erase; + + if(!(file->f_mode & 2)) + return -EPERM; + + erase=kmalloc(sizeof(struct erase_info),GFP_KERNEL); if (!erase) ret = -ENOMEM; else { @@ -339,6 +342,9 @@ static int mtd_ioctl(struct inode *inode, struct file *file, void *databuf; ssize_t retlen; + if(!(file->f_mode & 2)) + return -EPERM; + if (copy_from_user(&buf, (struct mtd_oob_buf *)arg, sizeof(struct mtd_oob_buf))) return -EFAULT; @@ -435,6 +441,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, break; } + case MEMSETOOBSEL: + { + if (copy_from_user(&mtd->oobinfo ,(void *)arg, sizeof(struct nand_oobinfo))) + return -EFAULT; + break; + } default: DEBUG(MTD_DEBUG_LEVEL0, "Invalid ioctl %x (MEMGETINFO = %x)\n", cmd, MEMGETINFO); @@ -446,12 +458,12 @@ static int mtd_ioctl(struct inode *inode, struct file *file, 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 */ + .llseek = mtd_lseek, + .read = mtd_read, + .write = mtd_write, + .ioctl = mtd_ioctl, + .open = mtd_open, + .release = mtd_close, }; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c index 4c16c0e43e0f..9d5dffc00b86 100644 --- a/drivers/mtd/mtdconcat.c +++ b/drivers/mtd/mtdconcat.c @@ -3,9 +3,11 @@ * * (C) 2002 Robert Kaiser <rkaiser@sysgo.de> * + * NAND support by Christian Gan <cgan@iders.ca> + * * This code is GPL * - * $Id: mtdconcat.c,v 1.3 2002/05/21 21:04:25 dwmw2 Exp $ + * $Id: mtdconcat.c,v 1.4 2003/03/07 17:44:59 rkaiser Exp $ */ #include <linux/module.h> @@ -63,16 +65,16 @@ static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, size_t size, retsize; if (from >= subdev->size) - { + { /* Not destined for this subdev */ size = 0; from -= subdev->size; } else { if (from + len > subdev->size) - size = subdev->size - from; + size = subdev->size - from; /* First part goes into this subdev */ else - size = len; + size = len; /* Entire transaction goes into this subdev */ err = subdev->read(subdev, from, size, &retsize, buf); @@ -142,6 +144,214 @@ static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, 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; + } + else + { + if (from + len > subdev->size) + size = subdev->size - from; /* First part goes into this subdev */ + else + size = len; /* Entire transaction goes into this subdev */ + + 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 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) +{ + 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; + } + else + { + 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; + } + else + { + if (from + len > subdev->size) + size = subdev->size - from; /* First part goes into this subdev */ + else + size = len; /* Entire transaction goes into this subdev */ + + 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; + } + else + { + 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); @@ -526,14 +736,18 @@ struct mtd_info *mtd_concat_create( * because they are messy to implement and they are not * used to a great extent anyway. */ - concat->mtd.erase = concat_erase; - concat->mtd.read = concat_read; - concat->mtd.write = concat_write; - concat->mtd.sync = concat_sync; - concat->mtd.lock = concat_lock; - concat->mtd.unlock = concat_unlock; - concat->mtd.suspend = concat_suspend; - concat->mtd.resume = concat_resume; + concat->mtd.erase = concat_erase; + concat->mtd.read = concat_read; + concat->mtd.write = concat_write; + concat->mtd.read_ecc = concat_read_ecc; + concat->mtd.write_ecc = concat_write_ecc; + concat->mtd.read_oob = concat_read_oob; + concat->mtd.write_oob = concat_write_oob; + concat->mtd.sync = concat_sync; + concat->mtd.lock = concat_lock; + concat->mtd.unlock = concat_unlock; + concat->mtd.suspend = concat_suspend; + concat->mtd.resume = concat_resume; /* diff --git a/drivers/mtd/mtdcore.c b/drivers/mtd/mtdcore.c index 4d283458d7e6..d9e24b21cc0e 100644 --- a/drivers/mtd/mtdcore.c +++ b/drivers/mtd/mtdcore.c @@ -1,5 +1,5 @@ /* - * $Id: mtdcore.c,v 1.31 2001/10/02 15:05:11 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,7 +17,7 @@ #include <linux/major.h> #include <linux/fs.h> #include <linux/ioctl.h> -#include <stdarg.h> +#include <linux/init.h> #include <linux/mtd/compatmac.h> #ifdef CONFIG_PROC_FS #include <linux/proc_fs.h> @@ -25,9 +25,15 @@ #include <linux/mtd/mtd.h> -static DECLARE_MUTEX(mtd_table_mutex); -static struct mtd_info *mtd_table[MAX_MTD_DEVICES]; -static struct mtd_notifier *mtd_notifiers = NULL; +/* These are exported solely for the purpose of mtd_blkdevs.c. You + should not use them for _anything_ else */ +DECLARE_MUTEX(mtd_table_mutex); +struct mtd_info *mtd_table[MAX_MTD_DEVICES]; + +EXPORT_SYMBOL_GPL(mtd_table_mutex); +EXPORT_SYMBOL_GPL(mtd_table); + +static LIST_HEAD(mtd_notifiers); /** * add_mtd_device - register an MTD device @@ -45,21 +51,28 @@ int add_mtd_device(struct mtd_info *mtd) 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; } @@ -79,29 +92,34 @@ int add_mtd_device(struct mtd_info *mtd) 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; } /** @@ -119,10 +137,9 @@ void register_mtd_user (struct mtd_notifier *new) 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]) @@ -143,34 +160,24 @@ void register_mtd_user (struct mtd_notifier *new) 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]); + for (i=0; i< MAX_MTD_DEVICES; i++) + if (mtd_table[i]) + old->remove(mtd_table[i]); - up(&mtd_table_mutex); - return 0; - } - prev = &cur->next; - } + list_del(&old->list); up(&mtd_table_mutex); - return 1; + return 0; } /** - * __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 * @@ -178,11 +185,10 @@ int unregister_mtd_user (struct mtd_notifier *old) * 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,16 +204,97 @@ struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num) if (mtd && mtd != ret) 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 + */ + +int default_mtd_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; + + if(!mtd->write) { + ret = -EROFS; + } else { + for (i=0; i<count; i++) { + if (!vecs[i].iov_len) + continue; + ret = mtd->write(mtd, to, vecs[i].iov_len, &thislen, vecs[i].iov_base); + totlen += thislen; + if (ret || thislen != vecs[i].iov_len) + break; + to += vecs[i].iov_len; + } + } + if (retlen) + *retlen = totlen; + return ret; +} + + +/* default_mtd_readv - default mtd readv method for MTD devices that dont + * implement their own + */ + +int default_mtd_readv(struct mtd_info *mtd, struct iovec *vecs, + unsigned long count, loff_t from, size_t *retlen) +{ + unsigned long i; + size_t totlen = 0, thislen; + int ret = 0; + + if(!mtd->read) { + ret = -EIO; + } else { + for (i=0; i<count; i++) { + if (!vecs[i].iov_len) + continue; + ret = mtd->read(mtd, from, vecs[i].iov_len, &thislen, vecs[i].iov_base); + totlen += thislen; + if (ret || thislen != vecs[i].iov_len) + break; + from += vecs[i].iov_len; + } + } + if (retlen) + *retlen = totlen; + return ret; +} + + 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); +EXPORT_SYMBOL(default_mtd_readv); /*====================================================================*/ /* Power management code */ @@ -296,7 +383,7 @@ done: up(&mtd_table_mutex); if (off >= len+begin) return 0; - *start = page + (begin-off); + *start = page + (off-begin); return ((count < begin+len-off) ? count : begin+len-off); } diff --git a/drivers/mtd/mtdpart.c b/drivers/mtd/mtdpart.c index dc1044061ad2..c0095f99d679 100644 --- a/drivers/mtd/mtdpart.c +++ b/drivers/mtd/mtdpart.c @@ -5,18 +5,22 @@ * * This code is GPL * - * $Id: mtdpart.c,v 1.23 2001/10/02 15:05:11 dwmw2 Exp $ - */ + * $Id: mtdpart.c,v 1.39 2003/05/21 15:15:03 dwmw2 Exp $ + * + * 02-21-2002 Thomas Gleixner <gleixner@autronix.de> + * added support for read_oob, write_oob + */ #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> #include <linux/slab.h> #include <linux/list.h> - +#include <linux/config.h> +#include <linux/kmod.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> - +#include <linux/mtd/compatmac.h> /* Our partition linked list */ static LIST_HEAD(mtd_partitions); @@ -28,6 +32,7 @@ struct mtd_part { u_int32_t offset; int index; struct list_head list; + int registered; }; /* @@ -50,7 +55,72 @@ static int part_read (struct mtd_info *mtd, loff_t from, size_t len, len = 0; else if (from + len > mtd->size) len = mtd->size - from; - return part->master->read (part->master, from + part->offset, + 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, + size_t *retlen, u_char **buf) +{ + struct mtd_part *part = PART(mtd); + if (from >= mtd->size) + len = 0; + else if (from + len > mtd->size) + len = mtd->size - from; + return part->master->point (part->master, from + part->offset, + len, retlen, buf); +} +static void part_unpoint (struct mtd_info *mtd, u_char *addr, loff_t from, size_t len) +{ + struct mtd_part *part = PART(mtd); + + part->master->unpoint (part->master, addr, from + part->offset, len); +} + + +static int part_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_part *part = PART(mtd); + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + if (from >= mtd->size) + len = 0; + else if (from + len > mtd->size) + len = mtd->size - from; + return part->master->read_ecc (part->master, from + part->offset, + len, retlen, buf, eccbuf, oobsel); +} + +static int part_read_oob (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + if (from >= mtd->size) + len = 0; + else if (from + len > mtd->size) + len = mtd->size - from; + return part->master->read_oob (part->master, from + part->offset, + len, retlen, buf); +} + +static int part_read_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + return part->master->read_user_prot_reg (part->master, from, + len, retlen, buf); +} + +static int part_read_fact_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + return part->master->read_user_prot_reg (part->master, from, len, retlen, buf); } @@ -64,7 +134,51 @@ static int part_write (struct mtd_info *mtd, loff_t to, size_t len, len = 0; else if (to + len > mtd->size) len = mtd->size - to; - return part->master->write (part->master, to + part->offset, + 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, 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) + len = mtd->size - to; + return part->master->write_ecc (part->master, to + part->offset, + len, retlen, buf, eccbuf, oobsel); +} + +static int part_write_oob (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + struct mtd_part *part = PART(mtd); + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + if (to >= mtd->size) + len = 0; + else if (to + len > mtd->size) + len = mtd->size - to; + return part->master->write_oob (part->master, to + part->offset, + len, retlen, buf); +} + +static int part_write_user_prot_reg (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + struct mtd_part *part = PART(mtd); + return part->master->write_user_prot_reg (part->master, from, len, retlen, buf); } @@ -74,16 +188,52 @@ static int part_writev (struct mtd_info *mtd, const struct iovec *vecs, struct mtd_part *part = PART(mtd); if (!(mtd->flags & MTD_WRITEABLE)) return -EROFS; - return part->master->writev (part->master, vecs, count, + 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); - return part->master->readv (part->master, vecs, count, + 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, 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); +} + +static int part_readv_ecc (struct mtd_info *mtd, struct iovec *vecs, + unsigned long count, loff_t from, size_t *retlen, + 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); } static int part_erase (struct mtd_info *mtd, struct erase_info *instr) @@ -148,7 +298,8 @@ int del_mtd_partitions(struct mtd_info *master) if (slave->master == master) { struct list_head *prev = node->prev; __list_del(prev, node->next); - del_mtd_device(&slave->mtd); + if(slave->registered) + del_mtd_device(&slave->mtd); kfree(slave); node = prev; } @@ -198,22 +349,44 @@ int add_mtd_partitions(struct mtd_info *master, 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; + + if(master->point && master->unpoint){ + slave->mtd.point = part_point; + slave->mtd.unpoint = part_unpoint; + } + + if (master->read_ecc) + slave->mtd.read_ecc = part_read_ecc; + if (master->write_ecc) + slave->mtd.write_ecc = part_write_ecc; + if (master->read_oob) + slave->mtd.read_oob = part_read_oob; + if (master->write_oob) + slave->mtd.write_oob = part_write_oob; + if(master->read_user_prot_reg) + slave->mtd.read_user_prot_reg = part_read_user_prot_reg; + if(master->read_fact_prot_reg) + slave->mtd.read_fact_prot_reg = part_read_fact_prot_reg; + if(master->write_user_prot_reg) + slave->mtd.write_user_prot_reg = part_write_user_prot_reg; if (master->sync) slave->mtd.sync = part_sync; if (!i && master->suspend && master->resume) { slave->mtd.suspend = part_suspend; slave->mtd.resume = part_resume; } - if (master->writev) slave->mtd.writev = part_writev; if (master->readv) slave->mtd.readv = part_readv; + if (master->writev_ecc) + slave->mtd.writev_ecc = part_writev_ecc; + if (master->readv_ecc) + slave->mtd.readv_ecc = part_readv_ecc; if (master->lock) slave->mtd.lock = part_lock; if (master->unlock) @@ -225,6 +398,15 @@ int add_mtd_partitions(struct mtd_info *master, if (slave->offset == MTDPART_OFS_APPEND) slave->offset = cur_offset; + if (slave->offset == MTDPART_OFS_NXTBLK) { + u_int32_t emask = master->erasesize-1; + slave->offset = (cur_offset + emask) & ~emask; + if (slave->offset != cur_offset) { + printk(KERN_NOTICE "Moving partition %d: " + "0x%08x -> 0x%08x\n", i, + cur_offset, slave->offset); + } + } if (slave->mtd.size == MTDPART_SIZ_FULL) slave->mtd.size = master->size - slave->offset; cur_offset = slave->offset + slave->mtd.size; @@ -279,8 +461,17 @@ int add_mtd_partitions(struct mtd_info *master, parts[i].name); } - /* register our partition */ - add_mtd_device(&slave->mtd); + if(parts[i].mtdp) + { /* store the object pointer (caller may or may not register it */ + *parts[i].mtdp = &slave->mtd; + slave->registered = 0; + } + else + { + /* register our partition */ + add_mtd_device(&slave->mtd); + slave->registered = 1; + } } return 0; @@ -289,6 +480,75 @@ int add_mtd_partitions(struct mtd_info *master, EXPORT_SYMBOL(add_mtd_partitions); EXPORT_SYMBOL(del_mtd_partitions); +static spinlock_t part_parser_lock = SPIN_LOCK_UNLOCKED; +static LIST_HEAD(part_parsers); + +struct mtd_part_parser *get_partition_parser(const char *name) +{ + struct list_head *this; + void *ret = NULL; + spin_lock(&part_parser_lock); + + list_for_each(this, &part_parsers) { + struct mtd_part_parser *p = list_entry(this, struct mtd_part_parser, list); + + if (!strcmp(p->name, name) && try_module_get(p->owner)) { + ret = p; + break; + } + } + spin_unlock(&part_parser_lock); + + return ret; +} + +int register_mtd_parser(struct mtd_part_parser *p) +{ + spin_lock(&part_parser_lock); + list_add(&p->list, &part_parsers); + spin_unlock(&part_parser_lock); + + return 0; +} + +int deregister_mtd_parser(struct mtd_part_parser *p) +{ + spin_lock(&part_parser_lock); + list_del(&p->list); + spin_unlock(&part_parser_lock); + return 0; +} + +int parse_mtd_partitions(struct mtd_info *master, const char **types, + struct mtd_partition **pparts, unsigned long origin) +{ + struct mtd_part_parser *parser; + int ret = 0; + + for ( ; ret <= 0 && *types; types++) { + parser = get_partition_parser(*types); +#ifdef CONFIG_KMOD + if (!parser && !request_module("%s", *types)) + parser = get_partition_parser(*types); +#endif + if (!parser) { + printk(KERN_NOTICE "%s partition parsing not available\n", + *types); + continue; + } + ret = (*parser->parse_fn)(master, pparts, origin); + if (ret > 0) { + printk(KERN_NOTICE "%d %s partitions found on MTD device %s\n", + ret, parser->name, master->name); + } + put_partition_parser(parser); + } + return ret; +} + +EXPORT_SYMBOL_GPL(parse_mtd_partitions); +EXPORT_SYMBOL_GPL(register_mtd_parser); +EXPORT_SYMBOL_GPL(deregister_mtd_parser); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Nicolas Pitre <nico@cam.org>"); diff --git a/drivers/mtd/nand/Kconfig b/drivers/mtd/nand/Kconfig index e1fa71285c9c..583fe14044d1 100644 --- a/drivers/mtd/nand/Kconfig +++ b/drivers/mtd/nand/Kconfig @@ -1,5 +1,5 @@ -# drivers/mtd/nand/Config.in -# $Id: Config.in,v 1.4 2001/09/19 09:35:23 dwmw2 Exp $ +# drivers/mtd/nand/Kconfig +# $Id: Kconfig,v 1.4 2003/05/28 10:04:23 dwmw2 Exp $ menu "NAND Flash Device Drivers" depends on MTD!=n @@ -9,16 +9,8 @@ config MTD_NAND depends on MTD help This enables support for accessing all type of NAND flash - devices. - -config MTD_NAND_ECC - bool "Enable ECC correction algorithm" - depends on MTD_NAND - help - This enables software-based ECC for use with NAND flash chips. It - can detect and correct 1 bit errors per 256 byte blocks. This - should be used to increase the reliability of the data stored and - read on the device. + devices with an 8-bit data bus interface. For further + information see www.linux-mtd.infradead.org/tech/nand.html. config MTD_NAND_VERIFY_WRITE bool "Verify NAND page writes" @@ -28,8 +20,21 @@ config MTD_NAND_VERIFY_WRITE NAND flash device internally checks only bits transitioning from 1 to 0. There is a rare possibility that even though the device thinks the write was successful, a bit could have been - flipped accidentaly due to device wear, gamma rays, whatever. - Enable this if you are really paranoid. + flipped accidentaly due to device wear or something else. + +config MTD_NAND_AUTCPU12 + tristate "SmartMediaCard on autronix autcpu12 board" + depends on ARM && MTD_NAND && ARCH_AUTCPU12 + help + This enables the driver for the autronix autcpu12 board to + access the SmartMediaCard. + +config MTD_NAND_EDB7312 + tristate "Support for Cirrus Logic EBD7312 evaluation board" + depends on ARM && MTD_NAND && ARCH_EDB7312 + help + This enables the driver for the Cirrus Logic EBD7312 evaluation + board to access the onboard NAND Flash. config MTD_NAND_SPIA tristate "NAND Flash device on SPIA board" @@ -37,5 +42,10 @@ config MTD_NAND_SPIA help If you had to ask, you don't have one. Say 'N'. +config MTD_NAND_IDS + tristate + default y if MTD_NAND = y || MTD_DOC2000 = y || MTD_DOC2001 = y || MTD_DOC2001PLUS = y + default m if MTD_NAND = m || MTD_DOC2000 = m || MTD_DOC2001 = m || MTD_DOC2001PLUS = m + endmenu diff --git a/drivers/mtd/nand/Makefile b/drivers/mtd/nand/Makefile index acac6447e90b..dfc4c1f986a6 100644 --- a/drivers/mtd/nand/Makefile +++ b/drivers/mtd/nand/Makefile @@ -1,10 +1,10 @@ # # linux/drivers/nand/Makefile # -# $Id: Makefile,v 1.5 2001/09/19 22:39:59 dwmw2 Exp $ +# $Id: Makefile.common,v 1.2 2003/05/28 11:38:54 dwmw2 Exp $ -nandobjs-y := nand.o -nandobjs-$(CONFIG_MTD_NAND_ECC) += nand_ecc.o - -obj-$(CONFIG_MTD_NAND) += $(nandobjs-y) +obj-$(CONFIG_MTD_NAND) += nand.o nand_ecc.o obj-$(CONFIG_MTD_NAND_SPIA) += spia.o +obj-$(CONFIG_MTD_NAND_AUTCPU12) += autcpu12.o +obj-$(CONFIG_MTD_NAND_EDB7312) += edb7312.o +obj-$(CONFIG_MTD_NAND_IDS) += nand_ids.o diff --git a/drivers/mtd/nand/autcpu12.c b/drivers/mtd/nand/autcpu12.c new file mode 100644 index 000000000000..5d928bfee613 --- /dev/null +++ b/drivers/mtd/nand/autcpu12.c @@ -0,0 +1,254 @@ +/* + * drivers/mtd/autcpu12.c + * + * Copyright (c) 2002 Thomas Gleixner <tgxl@linutronix.de> + * + * Derived from drivers/mtd/spia.c + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * + * $Id: autcpu12.c,v 1.10 2003/04/20 07:24:40 gleixner 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 + * published by the Free Software Foundation. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * autronix autcpu12 board, which is a SmartMediaCard. It supports + * 16MB, 32MB and 64MB cards. + * + * + * 02-12-2002 TG Cleanup of module params + * + * 02-20-2002 TG adjusted for different rd/wr adress support + * added support for read device ready/busy line + * added page_cache + * + * 10-06-2002 TG 128K card support added + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> +#include <asm/sizes.h> +#include <asm/arch/autcpu12.h> + +/* + * MTD structure for AUTCPU12 board + */ +static struct mtd_info *autcpu12_mtd = NULL; + +/* + * Module stuff + */ +#if LINUX_VERSION_CODE < 0x20212 && defined(MODULE) +#define autcpu12_init init_module +#define autcpu12_cleanup cleanup_module +#endif + +static int autcpu12_io_base = CS89712_VIRT_BASE; +static int autcpu12_fio_pbase = AUTCPU12_PHYS_SMC; +static int autcpu12_fio_ctrl = AUTCPU12_SMC_SELECT_OFFSET; +static int autcpu12_pedr = AUTCPU12_SMC_PORT_OFFSET; +static int autcpu12_fio_base; + +#ifdef MODULE +MODULE_PARM(autcpu12_fio_pbase, "i"); +MODULE_PARM(autcpu12_fio_ctrl, "i"); +MODULE_PARM(autcpu12_pedr, "i"); + +__setup("autcpu12_fio_pbase=",autcpu12_fio_pbase); +__setup("autcpu12_fio_ctrl=",autcpu12_fio_ctrl); +__setup("autcpu12_pedr=",autcpu12_pedr); +#endif + +/* + * Define partitions for flash devices + */ +extern struct nand_oobinfo jffs2_oobinfo; + +static struct mtd_partition partition_info16k[] = { + { name: "AUTCPU12 flash partition 1", + offset: 0, + size: 8 * SZ_1M }, + { name: "AUTCPU12 flash partition 2", + offset: 8 * SZ_1M, + size: 8 * SZ_1M }, +}; + +static struct mtd_partition partition_info32k[] = { + { name: "AUTCPU12 flash partition 1", + offset: 0, + size: 8 * SZ_1M }, + { name: "AUTCPU12 flash partition 2", + offset: 8 * SZ_1M, + size: 24 * SZ_1M }, +}; + +static struct mtd_partition partition_info64k[] = { + { name: "AUTCPU12 flash partition 1", + offset: 0, + size: 16 * SZ_1M }, + { name: "AUTCPU12 flash partition 2", + offset: 16 * SZ_1M, + size: 48 * SZ_1M }, +}; + +static struct mtd_partition partition_info128k[] = { + { name: "AUTCPU12 flash partition 1", + offset: 0, + size: 16 * SZ_1M }, + { name: "AUTCPU12 flash partition 2", + offset: 16 * SZ_1M, + size: 112 * SZ_1M }, +}; + +#define NUM_PARTITIONS16K 2 +#define NUM_PARTITIONS32K 2 +#define NUM_PARTITIONS64K 2 +#define NUM_PARTITIONS128K 2 +/* + * hardware specific access to control-lines +*/ +void autcpu12_hwcontrol(int cmd) +{ + + switch(cmd){ + + case NAND_CTL_SETCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |= AUTCPU12_SMC_CLE; break; + case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_CLE; break; + + case NAND_CTL_SETALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) |= AUTCPU12_SMC_ALE; break; + case NAND_CTL_CLRALE: (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) &= ~AUTCPU12_SMC_ALE; break; + + case NAND_CTL_SETNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x01; break; + case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (autcpu12_fio_base + autcpu12_fio_ctrl)) = 0x00; break; + } +} + +/* +* read device ready pin +*/ +int autcpu12_device_ready(void) +{ + + return ( (*(volatile unsigned char *) (autcpu12_io_base + autcpu12_pedr)) & AUTCPU12_SMC_RDY) ? 1 : 0; + +} +/* + * Main initialization routine + */ +int __init autcpu12_init (void) +{ + struct nand_chip *this; + int err = 0; + + /* Allocate memory for MTD device structure and private data */ + autcpu12_mtd = kmalloc (sizeof(struct mtd_info) + sizeof (struct nand_chip), + GFP_KERNEL); + if (!autcpu12_mtd) { + printk ("Unable to allocate AUTCPU12 NAND MTD device structure.\n"); + err = -ENOMEM; + goto out; + } + + /* map physical adress */ + autcpu12_fio_base=(unsigned long)ioremap(autcpu12_fio_pbase,SZ_1K); + if(!autcpu12_fio_base){ + printk("Ioremap autcpu12 SmartMedia Card failed\n"); + err = -EIO; + goto out_mtd; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&autcpu12_mtd[1]); + + /* Initialize structures */ + memset((char *) autcpu12_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + autcpu12_mtd->priv = this; + + /* Set address of NAND IO lines */ + this->IO_ADDR_R = autcpu12_fio_base; + this->IO_ADDR_W = autcpu12_fio_base; + this->hwcontrol = autcpu12_hwcontrol; + this->dev_ready = autcpu12_device_ready; + /* 20 us command delay time */ + this->chip_delay = 20; + this->eccmode = NAND_ECC_SOFT; + + /* Scan to find existance of the device */ + if (nand_scan (autcpu12_mtd)) { + err = -ENXIO; + goto out_ior; + } + + /* Allocate memory for internal data buffer */ + this->data_buf = kmalloc (sizeof(u_char) * (autcpu12_mtd->oobblock + autcpu12_mtd->oobsize), GFP_KERNEL); + if (!this->data_buf) { + printk ("Unable to allocate NAND data buffer for AUTCPU12.\n"); + err = -ENOMEM; + goto out_ior; + } + + /* Register the partitions */ + switch(autcpu12_mtd->size){ + case SZ_16M: add_mtd_partitions(autcpu12_mtd, partition_info16k, NUM_PARTITIONS16K); break; + case SZ_32M: add_mtd_partitions(autcpu12_mtd, partition_info32k, NUM_PARTITIONS32K); break; + case SZ_64M: add_mtd_partitions(autcpu12_mtd, partition_info64k, NUM_PARTITIONS64K); break; + case SZ_128M: add_mtd_partitions(autcpu12_mtd, partition_info128k, NUM_PARTITIONS128K); break; + default: { + printk ("Unsupported SmartMedia device\n"); + err = -ENXIO; + goto out_buf; + } + } + goto out; + +out_buf: + kfree (this->data_buf); +out_ior: + iounmap((void *)autcpu12_fio_base); +out_mtd: + kfree (autcpu12_mtd); +out: + return err; +} + +module_init(autcpu12_init); + +/* + * Clean up routine + */ +#ifdef MODULE +static void __exit autcpu12_cleanup (void) +{ + struct nand_chip *this = (struct nand_chip *) &autcpu12_mtd[1]; + + /* Unregister partitions */ + del_mtd_partitions(autcpu12_mtd); + + /* Unregister the device */ + del_mtd_device (autcpu12_mtd); + + /* Free internal data buffers */ + kfree (this->data_buf); + + /* unmap physical adress */ + iounmap((void *)autcpu12_fio_base); + + /* Free the MTD device structure */ + kfree (autcpu12_mtd); +} +module_exit(autcpu12_cleanup); +#endif + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Thomas Gleixner <tglx@linutronix.de>"); +MODULE_DESCRIPTION("Glue layer for SmartMediaCard on autronix autcpu12"); diff --git a/drivers/mtd/nand/edb7312.c b/drivers/mtd/nand/edb7312.c new file mode 100644 index 000000000000..f0be399dc28b --- /dev/null +++ b/drivers/mtd/nand/edb7312.c @@ -0,0 +1,233 @@ +/* + * drivers/mtd/nand/edb7312.c + * + * Copyright (C) 2002 Marius Gröger (mag@sysgo.de) + * + * Derived from drivers/mtd/nand/autcpu12.c + * Copyright (c) 2001 Thomas Gleixner (gleixner@autronix.de) + * + * $Id: edb7312.c,v 1.5 2003/04/20 07:24:40 gleixner 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 + * published by the Free Software Foundation. + * + * Overview: + * This is a device driver for the NAND flash device found on the + * CLEP7312 board which utilizes the Toshiba TC58V64AFT part. This is + * a 64Mibit (8MiB x 8 bits) NAND flash device. + */ + +#include <linux/slab.h> +#include <linux/module.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> +#include <linux/mtd/partitions.h> +#include <asm/io.h> +#include <asm/arch/hardware.h> /* for CLPS7111_VIRT_BASE */ +#include <asm/sizes.h> +#include <asm/hardware/clps7111.h> + +/* + * MTD structure for EDB7312 board + */ +static struct mtd_info *ep7312_mtd = NULL; + +/* + * Values specific to the EDB7312 board (used with EP7312 processor) + */ +#define EP7312_FIO_PBASE 0x10000000 /* Phys address of flash */ +#define EP7312_PXDR 0x0001 /* + * IO offset to Port B data register + * where the CLE, ALE and NCE pins + * are wired to. + */ +#define EP7312_PXDDR 0x0041 /* + * IO offset to Port B data direction + * register so we can control the IO + * lines. + */ + +/* + * Module stuff + */ + +static int ep7312_fio_pbase = EP7312_FIO_PBASE; +static int ep7312_pxdr = EP7312_PXDR; +static int ep7312_pxddr = EP7312_PXDDR; + +#ifdef MODULE +MODULE_PARM(ep7312_fio_pbase, "i"); +MODULE_PARM(ep7312_pxdr, "i"); +MODULE_PARM(ep7312_pxddr, "i"); + +__setup("ep7312_fio_pbase=",ep7312_fio_pbase); +__setup("ep7312_pxdr=",ep7312_pxdr); +__setup("ep7312_pxddr=",ep7312_pxddr); +#endif + +#ifdef CONFIG_MTD_PARTITIONS +/* + * Define static partitions for flash device + */ +static struct mtd_partition partition_info[] = { + { name: "EP7312 Nand Flash", + offset: 0, + size: 8*1024*1024 } +}; +#define NUM_PARTITIONS 1 + +#endif + + +/* + * hardware specific access to control-lines + */ +static void ep7312_hwcontrol(int cmd) +{ + switch(cmd) { + + case NAND_CTL_SETCLE: + clps_writeb(clps_readb(ep7312_pxdr) | 0x10, ep7312_pxdr); + break; + case NAND_CTL_CLRCLE: + clps_writeb(clps_readb(ep7312_pxdr) & ~0x10, ep7312_pxdr); + break; + + case NAND_CTL_SETALE: + clps_writeb(clps_readb(ep7312_pxdr) | 0x20, ep7312_pxdr); + break; + case NAND_CTL_CLRALE: + clps_writeb(clps_readb(ep7312_pxdr) & ~0x20, ep7312_pxdr); + break; + + case NAND_CTL_SETNCE: + clps_writeb((clps_readb(ep7312_pxdr) | 0x80) & ~0x40, ep7312_pxdr); + break; + case NAND_CTL_CLRNCE: + clps_writeb((clps_readb(ep7312_pxdr) | 0x80) | 0x40, ep7312_pxdr); + break; + } +} + +/* + * read device ready pin + */ +static int ep7312_device_ready(void) +{ + return 1; +} + +/* + * Main initialization routine + */ +static int __init ep7312_init (void) +{ + struct nand_chip *this; + const char *part_type = 0; + int mtd_parts_nb = 0; + struct mtd_partition *mtd_parts = 0; + int ep7312_fio_base; + + /* Allocate memory for MTD device structure and private data */ + ep7312_mtd = kmalloc(sizeof(struct mtd_info) + + sizeof(struct nand_chip), + GFP_KERNEL); + if (!ep7312_mtd) { + printk("Unable to allocate EDB7312 NAND MTD device structure.\n"); + return -ENOMEM; + } + + /* map physical adress */ + ep7312_fio_base = (unsigned long)ioremap(ep7312_fio_pbase, SZ_1K); + if(!ep7312_fio_base) { + printk("ioremap EDB7312 NAND flash failed\n"); + kfree(ep7312_mtd); + return -EIO; + } + + /* Get pointer to private data */ + this = (struct nand_chip *) (&ep7312_mtd[1]); + + /* Initialize structures */ + memset((char *) ep7312_mtd, 0, sizeof(struct mtd_info)); + memset((char *) this, 0, sizeof(struct nand_chip)); + + /* Link the private data with the MTD structure */ + ep7312_mtd->priv = this; + + /* + * Set GPIO Port B control register so that the pins are configured + * to be outputs for controlling the NAND flash. + */ + clps_writeb(0xf0, ep7312_pxddr); + + /* insert callbacks */ + this->IO_ADDR_R = ep7312_fio_base; + this->IO_ADDR_W = ep7312_fio_base; + this->hwcontrol = ep7312_hwcontrol; + this->dev_ready = ep7312_device_ready; + /* 15 us command delay time */ + this->chip_delay = 15; + + /* Scan to find existence of the device */ + if (nand_scan (ep7312_mtd)) { + iounmap((void *)ep7312_fio_base); + kfree (ep7312_mtd); + return -ENXIO; + } + + /* Allocate memory for internal data buffer */ + this->data_buf = kmalloc (sizeof(u_char) * (ep7312_mtd->oobblock + ep7312_mtd->oobsize), GFP_KERNEL); + if (!this->data_buf) { + printk("Unable to allocate NAND data buffer for EDB7312.\n"); + iounmap((void *)ep7312_fio_base); + kfree (ep7312_mtd); + return -ENOMEM; + } + +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd_parts_nb = parse_cmdline_partitions(ep7312_mtd, &mtd_parts, + "edb7312-nand"); + if (mtd_parts_nb > 0) + part_type = "command line"; + else + mtd_parts_nb = 0; +#endif + if (mtd_parts_nb == 0) + { + mtd_parts = partition_info; + mtd_parts_nb = NUM_PARTITIONS; + part_type = "static"; + } + + /* Register the partitions */ + printk(KERN_NOTICE "Using %s partition definition\n", part_type); + add_mtd_partitions(ep7312_mtd, mtd_parts, mtd_parts_nb); + + /* Return happy */ + return 0; +} +module_init(ep7312_init); + +/* + * Clean up routine + */ +static void __exit ep7312_cleanup (void) +{ + struct nand_chip *this = (struct nand_chip *) &ep7312_mtd[1]; + + /* Unregister the device */ + del_mtd_device (ep7312_mtd); + + /* Free internal data buffer */ + kfree (this->data_buf); + + /* Free the MTD device structure */ + kfree (ep7312_mtd); +} +module_exit(ep7312_cleanup); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); +MODULE_DESCRIPTION("MTD map driver for Cogent EDB7312 board"); diff --git a/drivers/mtd/nand/nand.c b/drivers/mtd/nand/nand.c index bcd322ad9d49..dc11ca9a6f8a 100644 --- a/drivers/mtd/nand/nand.c +++ b/drivers/mtd/nand/nand.c @@ -1,17 +1,137 @@ /* * drivers/mtd/nand.c * - * Copyright (C) 2000 Steven J. Hill (sjhill@cotw.com) + * Overview: + * This is the generic MTD driver for NAND flash devices. It should be + * capable of working with almost all NAND chips currently available. + * + * Additional technical information is available on + * http://www.linux-mtd.infradead.org/tech/nand.html + * + * Copyright (C) 2000 Steven J. Hill (sjhill@realitydiluted.com) + * 2002 Thomas Gleixner (tglx@linutronix.de) + * + * 10-29-2001 Thomas Gleixner (tglx@linutronix.de) + * - Changed nand_chip structure for controlline function to + * support different hardware structures (Access to + * controllines ALE,CLE,NCE via hardware specific function. + * - exit out of "failed erase block" changed, to avoid + * driver hangup + * - init_waitqueue_head added in function nand_scan !! + * + * 01-30-2002 Thomas Gleixner (tglx@linutronix.de) + * change in nand_writev to block invalid vecs entries + * + * 02-11-2002 Thomas Gleixner (tglx@linutronix.de) + * - major rewrite to avoid duplicated code + * common nand_write_page function + * common get_chip function + * - added oob_config structure for out of band layouts + * - write_oob changed for partial programming + * - read cache for faster access for subsequent reads + * from the same page. + * - support for different read/write address + * - support for device ready/busy line + * - read oob for more than one page enabled + * + * 02-27-2002 Thomas Gleixner (tglx@linutronix.de) + * - command-delay can be programmed + * - fixed exit from erase with callback-function enabled + * + * 03-21-2002 Thomas Gleixner (tglx@linutronix.de) + * - DEBUG improvements provided by Elizabeth Clarke + * (eclarke@aminocom.com) + * - added zero check for this->chip_delay + * + * 04-03-2002 Thomas Gleixner (tglx@linutronix.de) + * - added added hw-driver supplied command and wait functions + * - changed blocking for erase (erase suspend enabled) + * - check pointers before accessing flash provided by + * John Hall (john.hall@optionexist.co.uk) + * + * 04-09-2002 Thomas Gleixner (tglx@linutronix.de) + * - nand_wait repaired + * + * 04-28-2002 Thomas Gleixner (tglx@linutronix.de) + * - OOB config defines moved to nand.h + * + * 08-01-2002 Thomas Gleixner (tglx@linutronix.de) + * - changed my mailaddress, added pointer to tech/nand.html + * + * 08-07-2002 Thomas Gleixner (tglx@linutronix.de) + * forced bad block location to byte 5 of OOB, even if + * CONFIG_MTD_NAND_ECC_JFFS2 is not set, to prevent + * erase /dev/mtdX from erasing bad blocks and destroying + * bad block info + * + * 08-10-2002 Thomas Gleixner (tglx@linutronix.de) + * Fixed writing tail of data. Thanks to Alice Hennessy + * <ahennessy@mvista.com>. + * + * 08-10-2002 Thomas Gleixner (tglx@linutronix.de) + * nand_read_ecc and nand_write_page restructured to support + * hardware ECC. Thanks to Steven Hein (ssh@sgi.com) + * for basic implementation and suggestions. + * 3 new pointers in nand_chip structure: + * calculate_ecc, correct_data, enabled_hwecc + * forcing all hw-drivers to support page cache + * eccvalid_pos is now mandatory * - * $Id: nand.c,v 1.12 2001/10/02 15:05:14 dwmw2 Exp $ + * 08-17-2002 tglx: fixed signed/unsigned missmatch in write.c + * Thanks to Ken Offer <koffer@arlut.utexas.edu> + * + * 08-29-2002 tglx: use buffered read/write only for non pagealigned + * access, speed up the aligned path by using the fs-buffer + * reset chip removed from nand_select(), implicit done + * only, when erase is interrupted + * waitfuntion use yield, instead of schedule_timeout + * support for 6byte/512byte hardware ECC + * read_ecc, write_ecc extended for different oob-layout + * selections: Implemented NAND_NONE_OOB, NAND_JFFS2_OOB, + * NAND_YAFFS_OOB. fs-driver gives one of these constants + * to select the oob-layout fitting the filesystem. + * oobdata can be read together with the raw data, when + * the fs-driver supplies a big enough buffer. + * size = 12 * number of pages to read (256B pagesize) + * 24 * number of pages to read (512B pagesize) + * the buffer contains 8/16 byte oobdata and 4/8 byte + * returncode from calculate_ecc + * oobdata can be given from filesystem to program them + * in one go together with the raw data. ECC codes are + * filled in at the place selected by oobsel. + * + * 09-04-2002 tglx: fixed write_verify (John Hall (john.hall@optionexist.co.uk)) + * + * 11-11-2002 tglx: fixed debug output in nand_write_page + * (John Hall (john.hall@optionexist.co.uk)) + * + * 11-25-2002 tglx: Moved device ID/ manufacturer ID from nand_ids.h + * Splitted device ID and manufacturer ID table. + * Removed CONFIG_MTD_NAND_ECC, as it defaults to ECC_NONE for + * mtd->read / mtd->write and is controllable by the fs driver + * for mtd->read_ecc / mtd->write_ecc + * some minor cleanups + * + * 12-05-2002 tglx: Dave Ellis (DGE@sixnetio) provided the fix for + * WRITE_VERIFY long time ago. Thanks for remembering me. + * + * 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) + * + * $Id: nand.c,v 1.45 2003/05/20 21:01:30 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 * published by the Free Software Foundation. * - * Overview: - * This is the generic MTD driver for NAND flash devices. It should be - * capable of working with almost all NAND chips currently available. */ #include <linux/delay.h> @@ -20,178 +140,426 @@ #include <linux/types.h> #include <linux/mtd/mtd.h> #include <linux/mtd/nand.h> -#include <linux/mtd/nand_ids.h> +#include <linux/mtd/nand_ecc.h> #include <linux/interrupt.h> #include <asm/io.h> -#ifdef CONFIG_MTD_NAND_ECC -#include <linux/mtd/nand_ecc.h> -#endif - /* * Macros for low-level register control */ -#define NAND_CTRL (*(volatile unsigned char *) \ - ((struct nand_chip *) mtd->priv)->CTRL_ADDR) -#define nand_select() NAND_CTRL &= ~this->NCE; \ - nand_command(mtd, NAND_CMD_RESET, -1, -1); \ - udelay (10); -#define nand_deselect() NAND_CTRL |= ~this->NCE; +#define nand_select() this->hwcontrol(NAND_CTL_SETNCE); +#define nand_deselect() this->hwcontrol(NAND_CTL_CLRNCE); /* * NAND low-level MTD interface functions */ -static int nand_read (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf); +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 *ecc_code); -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); + 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 *ecc_code); -static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf); + 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); + 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, 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, u_char *oob_buf, struct nand_oobinfo *oobsel); + /* * Send command to NAND device */ -static void nand_command (struct mtd_info *mtd, unsigned command, - int column, int page_addr) +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; + register unsigned long NAND_IO_ADDR = this->IO_ADDR_W; /* Begin command latch cycle */ - NAND_CTRL |= this->CLE; - + this->hwcontrol (NAND_CTL_SETCLE); /* * Write out the command to the device. */ - if (command != NAND_CMD_SEQIN) + if (command != NAND_CMD_SEQIN) writeb (command, NAND_IO_ADDR); else { if (mtd->oobblock == 256 && column >= 256) { column -= 256; - writeb(NAND_CMD_RESET, NAND_IO_ADDR); - writeb(NAND_CMD_READOOB, NAND_IO_ADDR); - writeb(NAND_CMD_SEQIN, NAND_IO_ADDR); - } - else if (mtd->oobblock == 512 && 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 { + 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); + writeb (NAND_CMD_READOOB, NAND_IO_ADDR); + writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); } - } - else { - writeb(NAND_CMD_READ0, NAND_IO_ADDR); - writeb(NAND_CMD_SEQIN, NAND_IO_ADDR); + } else { + writeb (NAND_CMD_READ0, NAND_IO_ADDR); + writeb (NAND_CMD_SEQIN, NAND_IO_ADDR); } } /* Set ALE and clear CLE to start address cycle */ - NAND_CTRL &= ~this->CLE; - NAND_CTRL |= this->ALE; - - /* Serially input address */ - if (column != -1) - writeb (column, NAND_IO_ADDR); - if (page_addr != -1) { - writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR); - writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR); - /* One more address cycle for higher density devices */ - if (mtd->size & 0x0c000000) { - writeb ((unsigned char) ((page_addr >> 16) & 0x0f), - NAND_IO_ADDR); + this->hwcontrol (NAND_CTL_CLRCLE); + + if (column != -1 || page_addr != -1) { + this->hwcontrol (NAND_CTL_SETALE); + + /* Serially input address */ + if (column != -1) + writeb (column, NAND_IO_ADDR); + if (page_addr != -1) { + writeb ((unsigned char) (page_addr & 0xff), NAND_IO_ADDR); + writeb ((unsigned char) ((page_addr >> 8) & 0xff), NAND_IO_ADDR); + /* One more address cycle for higher density devices */ + if (mtd->size & 0x0c000000) + writeb ((unsigned char) ((page_addr >> 16) & 0x0f), NAND_IO_ADDR); } + /* Latch in address */ + this->hwcontrol (NAND_CTL_CLRALE); } + + /* + * program and erase have their own busy handlers + * status and sequential in needs no delay + */ + switch (command) { + + case NAND_CMD_PAGEPROG: + case NAND_CMD_ERASE1: + case NAND_CMD_ERASE2: + case NAND_CMD_SEQIN: + case NAND_CMD_STATUS: + return; + + 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)); + return; - /* Latch in address */ - NAND_CTRL &= ~this->ALE; + /* 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()); +} + +/* + * Get chip for selected access + */ +static inline void nand_get_chip (struct nand_chip *this, struct mtd_info *mtd, int new_state, int *erase_state) +{ - /* Pause for 15us */ - udelay (15); + DECLARE_WAITQUEUE (wait, current); + + /* + * Grab the lock and see if the device is available + * For erasing, we keep the spinlock until the + * erase command is written. + */ +retry: + spin_lock_bh (&this->chip_lock); + + if (this->state == FL_READY) { + this->state = new_state; + if (new_state != FL_ERASING) + spin_unlock_bh (&this->chip_lock); + return; + } + + 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->cmdfunc(mtd, NAND_CMD_RESET, -1, -1); + return; + } + } + + set_current_state (TASK_UNINTERRUPTIBLE); + add_wait_queue (&this->wq, &wait); + spin_unlock_bh (&this->chip_lock); + schedule (); + remove_wait_queue (&this->wq, &wait); + goto retry; } /* - * NAND read + * Wait for command done. This applies to erase and program only + * Erase can take up to 400ms and program up to 20ms according to + * general NAND and SmartMedia specs + * +*/ +static int nand_wait(struct mtd_info *mtd, struct nand_chip *this, int state) +{ + + unsigned long timeo = jiffies; + int status; + + if (state == FL_ERASING) + timeo += (HZ * 400) / 1000; + else + timeo += (HZ * 20) / 1000; + + spin_lock_bh (&this->chip_lock); + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + + while (time_before(jiffies, timeo)) { + /* Check, if we were interrupted */ + if (this->state != state) { + spin_unlock_bh (&this->chip_lock); + return 0; + } + if (this->dev_ready) { + if (this->dev_ready ()) + break; + } + if (readb (this->IO_ADDR_R) & 0x40) + break; + + spin_unlock_bh (&this->chip_lock); + yield (); + spin_lock_bh (&this->chip_lock); + } + status = (int) readb (this->IO_ADDR_R); + spin_unlock_bh (&this->chip_lock); + + return status; +} + +/* + * 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_read (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int nand_write_page (struct mtd_info *mtd, struct nand_chip *this, int page, u_char *oob_buf, struct nand_oobinfo *oobsel) { -#ifdef CONFIG_MTD_NAND_ECC - struct nand_chip *this = mtd->priv; + int i, status; + u_char ecc_code[6], *oob_data; + 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) { + oob_data = &this->data_buf[mtd->oobblock]; + for (i = 0; i < mtd->oobsize; i++) + oob_data[i] = 0xff; + } else + oob_data = oob_buf; - return nand_read_ecc (mtd, from, len, retlen, buf, this->ecc_code_buf); -#else - return nand_read_ecc (mtd, from, len, retlen, buf, NULL); + /* 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 (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"); + for (i = 0; i < mtd->oobblock; i++) + writeb ( this->data_poi[i] , this->IO_ADDR_W); + break; + case NAND_ECC_SOFT: + 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 for 512 Byte page size */ + if (mtd->oobblock == 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]; + } + for (i = 0; i < mtd->oobblock; i++) + writeb ( this->data_poi[i] , this->IO_ADDR_W); + 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->calculate_ecc (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])); + for (i = 3; i < 6; i++) + oob_data[oob_config[i]] = ecc_code[i]; + } + break; + + /* 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])); + 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])); + for (i = 0; i < 6; i++) + oob_data[oob_config[i]] = ecc_code[i]; + break; + + default: + printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); + BUG(); + } + + /* Write out OOB data */ + for (i = 0; i < mtd->oobsize; i++) + writeb ( oob_data[i] , this->IO_ADDR_W); + + /* Send command to actually program the data */ + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); + + /* call wait ready function */ + status = this->waitfunc (mtd, this, FL_WRITING); + + /* See if device thinks it succeeded */ + if (status & 0x01) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write, page 0x%08x, ", __FUNCTION__, page); + return -EIO; + } + +#ifdef CONFIG_MTD_NAND_VERIFY_WRITE + /* + * The NAND device assumes that it is always writing to + * a cleanly erased page. Hence, it performs its internal + * write verification only on bits that transitioned from + * 1 to 0. The device does NOT verify the whole page on a + * byte by byte basis. It is possible that the page was + * not completely erased or the page is becoming unusable + * due to wear. The read with ECC would catch the error + * later when the ECC page check fails, but we would rather + * catch it early in the page write stage. Better to write + * no data than invalid data. + */ + + /* Send command to read back the page */ + this->cmdfunc (mtd, NAND_CMD_READ0, 0, page); + /* Loop through and verify the data */ + for (i = 0; i < mtd->oobblock; i++) { + if (this->data_poi[i] != readb (this->IO_ADDR_R)) { + 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)) { + DEBUG (MTD_DEBUG_LEVEL0, "%s: " "Failed write verify, page 0x%08x ", __FUNCTION__, page); + return -EIO; + } + } + } else { + if (eccmode != NAND_ECC_NONE) { + int ecc_bytes = 0; + + switch (this->eccmode) { + case NAND_ECC_SOFT: + case NAND_ECC_HW3_256: ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; break; + case NAND_ECC_HW3_512: ecc_bytes = 3; break; + case NAND_ECC_HW6_512: ecc_bytes = 6; break; + } + + for (i = 0; i < mtd->oobsize; i++) + oob_data[i] = readb (this->IO_ADDR_R); + + for (i = 0; i < ecc_bytes; i++) { + if (oob_data[oob_config[i]] != ecc_code[i]) { + DEBUG (MTD_DEBUG_LEVEL0, + "%s: Failed ECC write " + "verify, page 0x%08x, " "%6i bytes were succesful\n", __FUNCTION__, page, i); + return -EIO; + } + } + } + } #endif + return 0; } /* +* Use NAND read ECC +*/ +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, NULL)); +} + + +/* * 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 *ecc_code) + size_t * retlen, u_char * buf, u_char * oob_buf, struct nand_oobinfo *oobsel) { - int j, col, page, state; + int j, col, page, end, ecc; int erase_state = 0; + int read = 0, oob = 0, ecc_status = 0, ecc_failed = 0; struct nand_chip *this = mtd->priv; - DECLARE_WAITQUEUE(wait, current); -#ifdef CONFIG_MTD_NAND_ECC - int ecc_result; + u_char *data_poi, *oob_data = oob_buf; u_char ecc_calc[6]; -#endif + u_char ecc_code[6]; + int eccmode; + int *oob_config; - DEBUG (MTD_DEBUG_LEVEL3, - "nand_read_ecc: from = 0x%08x, len = %i\n", (unsigned int) from, - (int) len); + // use chip default if zero + if (oobsel == NULL) + oobsel = &mtd->oobinfo; + + 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); /* Do not allow reads past end of device */ if ((from + len) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_read_ecc: Attempt read beyond end of device\n"); + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: Attempt read beyond end of device\n"); *retlen = 0; return -EINVAL; } /* Grab the lock and see if the device is available */ -retry: - spin_lock_bh (&this->chip_lock); + nand_get_chip (this, mtd ,FL_READING, &erase_state); - switch (this->state) { - case FL_READY: - this->state = FL_READING; - spin_unlock_bh (&this->chip_lock); - break; - - case FL_ERASING: - this->state = FL_READING; - erase_state = 1; - spin_unlock_bh (&this->chip_lock); - break; - - default: - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&this->wq, &wait); - spin_unlock_bh (&this->chip_lock); - schedule(); - - remove_wait_queue (&this->wq, &wait); - goto retry; - }; + /* Select the NAND device */ + nand_select (); /* First we calculate the starting page */ page = from >> this->page_shift; @@ -199,122 +567,127 @@ retry: /* Get raw starting column */ col = from & (mtd->oobblock - 1); - /* State machine for devices having pages larger than 256 bytes */ - state = (col < mtd->eccsize) ? 0 : 1; - - /* Calculate column address within ECC block context */ - col = (col >= mtd->eccsize) ? (col - mtd->eccsize) : col; - - /* Initialize return value */ - *retlen = 0; - - /* Select the NAND device */ - nand_select (); + end = mtd->oobblock; + ecc = mtd->eccsize; + /* Send the read command */ + this->cmdfunc (mtd, NAND_CMD_READ0, 0x00, page); + /* Loop until all data read */ - while (*retlen < len) { - -#ifdef CONFIG_MTD_NAND_ECC - /* Send the read command */ - if (!state) - nand_command (mtd, NAND_CMD_READ0, 0x00, page); - else - nand_command (mtd, NAND_CMD_READ1, 0x00, page); - - /* Read in a block big enough for ECC */ - for (j=0 ; j < mtd->eccsize ; j++) - this->data_buf[j] = readb (this->IO_ADDR); - - /* Read in the out-of-band data */ - if (!state) { - nand_command (mtd, NAND_CMD_READOOB, 0x00, page); - for (j=0 ; j<3 ; j++) - ecc_code[j] = readb(this->IO_ADDR); - nand_command (mtd, NAND_CMD_READ0, 0x00, page); - } - else { - nand_command (mtd, NAND_CMD_READOOB, 0x03, page); - for (j=3 ; j<6 ; j++) - ecc_code[j] = readb(this->IO_ADDR); - nand_command (mtd, NAND_CMD_READ0, 0x00, page); - } - - /* Calculate the ECC and verify it */ - if (!state) { - nand_calculate_ecc (&this->data_buf[0], - &ecc_calc[0]); - ecc_result = nand_correct_data (&this->data_buf[0], - &ecc_code[0], &ecc_calc[0]); - } - else { - nand_calculate_ecc (&this->data_buf[0], - &ecc_calc[3]); - ecc_result = nand_correct_data (&this->data_buf[0], - &ecc_code[3], &ecc_calc[3]); - } - if (ecc_result == -1) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_read_ecc: " \ - "Failed ECC read, page 0x%08x\n", page); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - if (erase_state) - this->state = FL_ERASING; + while (read < len) { + + /* If we have consequent page reads, apply delay or wait for ready/busy pin */ + if (read) { + if (!this->dev_ready) + udelay (this->chip_delay); else - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; + while (!this->dev_ready()); } - /* Read the data from ECC data buffer into return buffer */ - if ((*retlen + (mtd->eccsize - col)) >= len) { - while (*retlen < len) - buf[(*retlen)++] = this->data_buf[col++]; - /* We're done */ - continue; - } - else - for (j=col ; j < mtd->eccsize ; j++) - buf[(*retlen)++] = this->data_buf[j]; -#else - /* Send the read command */ - if (!state) - nand_command (mtd, NAND_CMD_READ0, col, page); + /* + * If the read is not page aligned, we have to read into data buffer + * due to ecc, else we read into return buffer direct + */ + if (!col && (len - read) >= end) + data_poi = &buf[read]; else - nand_command (mtd, NAND_CMD_READ1, col, page); + data_poi = this->data_buf; + + /* get oob area, if we have no oob buffer from fs-driver */ + if (!oob_buf) { + oob_data = &this->data_buf[end]; + oob = 0; + } + + j = 0; + switch (eccmode) { + case NAND_ECC_NONE: /* No ECC, Read in a page */ + printk (KERN_WARNING "Reading data from NAND FLASH without ECC is not recommended\n"); + while (j < end) + data_poi[j++] = readb (this->IO_ADDR_R); + 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]); + if (mtd->oobblock == 512) + this->calculate_ecc (&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 */ + + 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 */ + } + 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 */ + break; - /* Read the data directly into the return buffer */ - if ((*retlen + (mtd->eccsize - col)) >= len) { - while (*retlen < len) - buf[(*retlen)++] = readb (this->IO_ADDR); - /* We're done */ - continue; + default: + printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); + BUG(); } - else - for (j=col ; j < mtd->eccsize ; j++) - buf[(*retlen)++] = readb (this->IO_ADDR); -#endif - - /* - * If the amount of data to be read is greater than - * (256 - col), then all subsequent reads will take - * place on page or half-page (in the case of 512 byte - * page devices) aligned boundaries and the column - * address will be zero. Setting the column address to - * to zero after the first read allows us to simplify - * the reading of data and the if/else statements above. - */ - if (col) - col = 0x00; + /* read oobdata */ + for (j = 0; j < mtd->oobsize; j++) + oob_data[oob + j] = readb (this->IO_ADDR_R); + + /* Skip ECC, if not active */ + if (eccmode == NAND_ECC_NONE) + goto readdata; + + /* Pick the ECC bytes out of the oob data */ + for (j = 0; j < 6; j++) + 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]); + /* check, if we have a fs supplied oob-buffer */ + if (oob_buf) { + oob += mtd->oobsize; + *((int *)&oob_data[oob]) = ecc_status; + oob += sizeof(int); + } + if (ecc_status == -1) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); + ecc_failed++; + } + + if (mtd->oobblock == 512 && eccmode != NAND_ECC_HW3_512) { + ecc_status = this->correct_data (&data_poi[256], &ecc_code[3], &ecc_calc[3]); + if (oob_buf) { + *((int *)&oob_data[oob]) = ecc_status; + oob += sizeof(int); + } + if (ecc_status == -1) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_ecc: " "Failed ECC read, page 0x%08x\n", page); + ecc_failed++; + } + } +readdata: + if (col || (len - read) < end) { + for (j = col; j < end && read < len; j++) + buf[read++] = data_poi[j]; + } else + read += mtd->oobblock; + /* For subsequent reads align to page boundary. */ + col = 0; /* Increment page address */ - if ((mtd->oobblock == 256) || state) - page++; - - /* Toggle state machine */ - if (mtd->oobblock == 512) - state = state ? 0 : 1; + page++; } /* De-select the NAND device */ @@ -322,31 +695,29 @@ retry: /* Wake up anyone waiting on the device */ spin_lock_bh (&this->chip_lock); - if (erase_state) - this->state = FL_ERASING; - else - this->state = FL_READY; + this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); - - /* Return happy */ - return 0; + + /* + * Return success, if no ECC failures, else -EIO + * fs driver will take care of that, because + * retlen == desired len and result == -EIO + */ + *retlen = read; + return ecc_failed ? -EIO : 0; } /* * NAND read out-of-band */ -static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, - size_t *retlen, u_char *buf) +static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, size_t * retlen, u_char * buf) { int i, col, page; int erase_state = 0; struct nand_chip *this = mtd->priv; - DECLARE_WAITQUEUE(wait, current); - - DEBUG (MTD_DEBUG_LEVEL3, - "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, - (int) len); + + DEBUG (MTD_DEBUG_LEVEL3, "nand_read_oob: from = 0x%08x, len = %i\n", (unsigned int) from, (int) len); /* Shift to get page */ page = ((int) from) >> this->page_shift; @@ -357,59 +728,36 @@ static int nand_read_oob (struct mtd_info *mtd, loff_t from, size_t len, /* Initialize return length value */ *retlen = 0; - /* Do not allow read past end of page */ - if ((col + len) > mtd->oobsize) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_read_oob: Attempt read past end of page " \ - "0x%08x, column %i, length %i\n", page, col, len); + /* Do not allow reads past end of device */ + if ((from + len) > mtd->size) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_read_oob: Attempt read beyond end of device\n"); + *retlen = 0; return -EINVAL; } -retry: /* Grab the lock and see if the device is available */ - spin_lock_bh (&this->chip_lock); - - switch (this->state) { - case FL_READY: - this->state = FL_READING; - spin_unlock_bh (&this->chip_lock); - break; - - case FL_ERASING: - this->state = FL_READING; - erase_state = 1; - spin_unlock_bh (&this->chip_lock); - break; - - default: - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&this->wq, &wait); - spin_unlock_bh (&this->chip_lock); - schedule(); - - remove_wait_queue (&this->wq, &wait); - goto retry; - }; + nand_get_chip (this, mtd , FL_READING, &erase_state); /* Select the NAND device */ nand_select (); /* Send the read command */ - nand_command (mtd, NAND_CMD_READOOB, col, page); - - /* Read the data */ - for (i = 0 ; i < len ; i++) - buf[i] = readb (this->IO_ADDR); - + this->cmdfunc (mtd, NAND_CMD_READOOB, col, page); + /* + * 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)) + udelay (this->chip_delay); + } /* De-select the NAND device */ nand_deselect (); /* Wake up anyone waiting on the device */ spin_lock_bh (&this->chip_lock); - if (erase_state) - this->state = FL_ERASING; - else - this->state = FL_READY; + this->state = FL_READY; wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); @@ -418,265 +766,80 @@ retry: return 0; } +#define NOTALIGNED(x) (x & (mtd->oobblock-1)) != 0 + /* - * NAND write - */ -static int nand_write (struct mtd_info *mtd, loff_t to, size_t len, - size_t *retlen, const u_char *buf) +* 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) { -#ifdef CONFIG_MTD_NAND_ECC - struct nand_chip *this = mtd->priv; - - return nand_write_ecc (mtd, to, len, retlen, buf, this->ecc_code_buf); -#else - return nand_write_ecc (mtd, to, len, retlen, buf, NULL); -#endif -} - + 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 *ecc_code) + size_t * retlen, const u_char * buf, u_char * eccbuf, struct nand_oobinfo *oobsel) { - int i, page, col, cnt, status; + int page, ret = 0, oob = 0, written = 0; struct nand_chip *this = mtd->priv; - DECLARE_WAITQUEUE(wait, current); -#ifdef CONFIG_MTD_NAND_ECC - int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; -#endif - DEBUG (MTD_DEBUG_LEVEL3, - "nand_write_ecc: to = 0x%08x, len = %i\n", (unsigned int) to, - (int) len); + 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 page */ + /* Do not allow write past end of device */ if ((to + len) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_write_ecc: Attempted write past end of device\n"); + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Attempt to write past end of page\n"); return -EINVAL; } -retry: - /* Grab the lock and see if the device is available */ - spin_lock_bh (&this->chip_lock); - - switch (this->state) { - case FL_READY: - this->state = FL_WRITING; - spin_unlock_bh (&this->chip_lock); - break; - - default: - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&this->wq, &wait); - spin_unlock_bh (&this->chip_lock); - schedule(); + /* 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; + } - remove_wait_queue (&this->wq, &wait); - goto retry; - }; + // 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); - - /* Initialize return length value */ - *retlen = 0; + /* Grab the lock and see if the device is available */ + nand_get_chip (this, mtd, FL_WRITING, NULL); /* Select the NAND device */ nand_select (); /* Check the WP bit */ - nand_command (mtd, NAND_CMD_STATUS, -1, -1); - if (!(readb (this->IO_ADDR) & 0x80)) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_write_ecc: Device is write protected!!!\n"); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + if (!(readb (this->IO_ADDR_R) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_ecc: Device is write protected!!!\n"); + ret = -EIO; + goto out; } /* Loop until all data is written */ - while (*retlen < len) { - /* Write data into buffer */ - if ((col + len) >= mtd->oobblock) - for(i=col, cnt=0 ; i < mtd->oobblock ; i++, cnt++) - this->data_buf[i] = buf[(*retlen + cnt)]; - else - for(i=col, cnt=0 ; cnt < (len - *retlen) ; i++, cnt++) - this->data_buf[i] = buf[(*retlen + cnt)]; - -#ifdef CONFIG_MTD_NAND_ECC - /* Zero out the ECC array */ - for (i=0 ; i < 6 ; i++) - ecc_code[i] = 0x00; - - /* Calculate and write the ECC if we have enough data */ - if ((col < mtd->eccsize) && - ((col + (len - *retlen)) >= mtd->eccsize)) { - nand_command (mtd, NAND_CMD_READ0, col, page); - for (i=0 ; i < col ; i++) - this->data_buf[i] = readb (this->IO_ADDR); - nand_calculate_ecc (&this->data_buf[0], &ecc_code[0]); - for (i=0 ; i<3 ; i++) - this->data_buf[(mtd->oobblock + i)] = - ecc_code[i]; - } - - /* Calculate and write the second ECC if we have enough data */ - if ((mtd->oobblock == 512) && - ((col + (len - *retlen)) >= mtd->oobblock)) { - nand_calculate_ecc (&this->data_buf[256], &ecc_code[3]); - for (i=3 ; i<6 ; i++) - this->data_buf[(mtd->oobblock + i)] = - ecc_code[i]; - } - - /* Write ones for partial page programming */ - for (i=ecc_bytes ; i < mtd->oobsize ; i++) - this->data_buf[(mtd->oobblock + i)] = 0xff; -#else - /* Write ones for partial page programming */ - for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++) - this->data_buf[i] = 0xff; -#endif - - /* Write pre-padding bytes into buffer */ - for (i=0 ; i < col ; i++) - this->data_buf[i] = 0xff; - - /* Write post-padding bytes into buffer */ - if ((col + (len - *retlen)) < mtd->oobblock) { - for(i=(col + cnt) ; i < mtd->oobblock ; i++) - this->data_buf[i] = 0xff; - } - - /* Send command to begin auto page programming */ - nand_command (mtd, NAND_CMD_SEQIN, 0x00, page); - - /* Write out complete page of data */ - for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++) - writeb (this->data_buf[i], this->IO_ADDR); - - /* Send command to actually program the data */ - nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); - - /* - * Wait for program operation to complete. This could - * take up to 3000us (3ms) on some devices, so we try - * and exit as quickly as possible. - */ - status = 0; - for (i=0 ; i<24 ; i++) { - /* Delay for 125us */ - udelay (125); - - /* Check the status */ - nand_command (mtd, NAND_CMD_STATUS, -1, -1); - status = (int) readb (this->IO_ADDR); - if (status & 0x40) - break; - } - - /* See if device thinks it succeeded */ - if (status & 0x01) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_write_ecc: " \ - "Failed write, page 0x%08x, " \ - "%6i bytes were succesful\n", page, *retlen); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; - } - -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* - * The NAND device assumes that it is always writing to - * a cleanly erased page. Hence, it performs its internal - * write verification only on bits that transitioned from - * 1 to 0. The device does NOT verify the whole page on a - * byte by byte basis. It is possible that the page was - * not completely erased or the page is becoming unusable - * due to wear. The read with ECC would catch the error - * later when the ECC page check fails, but we would rather - * catch it early in the page write stage. Better to write - * no data than invalid data. - */ + while (written < len) { + int cnt = mtd->oobblock; + this->data_poi = (u_char*) &buf[written]; + /* We use the same function for write and writev */ + if (eccbuf) { + ret = nand_write_page (mtd, this, page, &eccbuf[oob], oobsel); + oob += mtd->oobsize; + } else + ret = nand_write_page (mtd, this, page, NULL, oobsel); - /* Send command to read back the page */ - if (col < mtd->eccsize) - nand_command (mtd, NAND_CMD_READ0, col, page); - else - nand_command (mtd, NAND_CMD_READ1, col - 256, page); - - /* Loop through and verify the data */ - for (i=col ; i < cnt ; i++) { - if (this->data_buf[i] != readb (this->IO_ADDR)) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_write_ecc: " \ - "Failed write verify, page 0x%08x, " \ - "%6i bytes were succesful\n", - page, *retlen); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; - } - } - -#ifdef CONFIG_MTD_NAND_ECC - /* - * We also want to check that the ECC bytes wrote - * correctly for the same reasons stated above. - */ - nand_command (mtd, NAND_CMD_READOOB, 0x00, page); - for (i=0 ; i < ecc_bytes ; i++) { - if ((readb (this->IO_ADDR) != ecc_code[i]) && - ecc_code[i]) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_write_ecc: Failed ECC write " \ - "verify, page 0x%08x, " \ - "%6i bytes were succesful\n", - page, i); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; - } - } -#endif - -#endif - - /* - * If we are writing a large amount of data and/or it - * crosses page or half-page boundaries, we set the - * the column to zero. It simplifies the program logic. - */ - if (col) - col = 0x00; + if (ret) + goto out; /* Update written bytes count */ - *retlen += cnt; - + written += cnt; /* Increment page address */ page++; } +out: /* De-select the NAND device */ nand_deselect (); @@ -686,24 +849,19 @@ retry: wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); - /* Return happy */ - *retlen = len; - return 0; + *retlen = written; + return ret; } /* * 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) +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; + int i, column, page, status, ret = 0; struct nand_chip *this = mtd->priv; - DECLARE_WAITQUEUE(wait, current); - - DEBUG (MTD_DEBUG_LEVEL3, - "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, - (int) len); + + DEBUG (MTD_DEBUG_LEVEL3, "nand_write_oob: to = 0x%08x, len = %i\n", (unsigned int) to, (int) len); /* Shift to get page */ page = ((int) to) >> this->page_shift; @@ -716,105 +874,65 @@ static int nand_write_oob (struct mtd_info *mtd, loff_t to, size_t len, /* Do not allow write past end of page */ if ((column + len) > mtd->oobsize) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_write_oob: Attempt to write past end of page\n"); + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Attempt to write past end of page\n"); return -EINVAL; } -retry: /* Grab the lock and see if the device is available */ - spin_lock_bh (&this->chip_lock); - - switch (this->state) { - case FL_READY: - this->state = FL_WRITING; - spin_unlock_bh (&this->chip_lock); - break; - - default: - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&this->wq, &wait); - spin_unlock_bh (&this->chip_lock); - schedule(); - - remove_wait_queue (&this->wq, &wait); - goto retry; - }; + nand_get_chip (this, mtd, FL_WRITING, NULL); /* Select the NAND device */ nand_select (); /* Check the WP bit */ - nand_command (mtd, NAND_CMD_STATUS, -1, -1); - if (!(readb (this->IO_ADDR) & 0x80)) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_write_oob: Device is write protected!!!\n"); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + if (!(readb (this->IO_ADDR_R) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: Device is write protected!!!\n"); + ret = -EIO; + goto out; } /* Write out desired data */ - nand_command (mtd, NAND_CMD_SEQIN, column + 512, page); - for (i=0 ; i<len ; i++) - writeb (buf[i], this->IO_ADDR); + 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); + /* write data */ + for (i = 0; i < len; i++) + writeb (buf[i], this->IO_ADDR_W); + /* postpad 0xff for partial programming */ + for (i = len + column; i < mtd->oobsize; i++) + writeb (0xff, this->IO_ADDR_W); /* Send command to program the OOB data */ - nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); + this->cmdfunc (mtd, NAND_CMD_PAGEPROG, -1, -1); - /* - * Wait for program operation to complete. This could - * take up to 3000us (3ms) on some devices, so we try - * and exit as quickly as possible. - */ - status = 0; - for (i=0 ; i<24 ; i++) { - /* Delay for 125us */ - udelay (125); - - /* Check the status */ - nand_command (mtd, NAND_CMD_STATUS, -1, -1); - status = (int) readb (this->IO_ADDR); - if (status & 0x40) - break; - } + status = this->waitfunc (mtd, this, FL_WRITING); /* See if device thinks it succeeded */ if (status & 0x01) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_write_oob: " \ - "Failed write, page 0x%08x\n", page); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write, page 0x%08x\n", page); + ret = -EIO; + goto out; } + /* Return happy */ + *retlen = len; #ifdef CONFIG_MTD_NAND_VERIFY_WRITE /* Send command to read back the data */ - nand_command (mtd, NAND_CMD_READOOB, column, page); + this->cmdfunc (mtd, NAND_CMD_READOOB, column, page); /* Loop through and verify the data */ - for (i=0 ; i<len ; i++) { - if (buf[i] != readb (this->IO_ADDR)) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_write_oob: " \ - "Failed write verify, page 0x%08x\n", page); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; + for (i = 0; i < len; i++) { + if (buf[i] != readb (this->IO_ADDR_R)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_write_oob: " "Failed write verify, page 0x%08x\n", page); + ret = -EIO; + goto out; } } #endif +out: /* De-select the NAND device */ nand_deselect (); @@ -824,258 +942,117 @@ retry: wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); - /* Return happy */ - *retlen = len; - return 0; + return ret; } + /* * NAND write with iovec */ -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 (struct mtd_info *mtd, const struct iovec *vecs, unsigned long count, + loff_t to, size_t * retlen) { - int i, page, col, cnt, len, total_len, status; + return (nand_writev_ecc (mtd, vecs, count, to, retlen, NULL, 0)); +} + +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, struct nand_oobinfo *oobsel) +{ + int i, page, len, total_len, ret = 0, written = 0; struct nand_chip *this = mtd->priv; - DECLARE_WAITQUEUE(wait, current); -#ifdef CONFIG_MTD_NAND_ECC - int ecc_bytes = (mtd->oobblock == 512) ? 6 : 3; -#endif /* Calculate total length of data */ total_len = 0; - for (i=0 ; i < count ; i++) + for (i = 0; i < count; i++) total_len += (int) vecs[i].iov_len; DEBUG (MTD_DEBUG_LEVEL3, - "nand_writev: to = 0x%08x, len = %i\n", (unsigned int) to, - (unsigned int) total_len); + "nand_writev: to = 0x%08x, len = %i, count = %ld\n", (unsigned int) to, (unsigned int) total_len, count); /* Do not allow write past end of page */ if ((to + total_len) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_writev: Attempted write past end of device\n"); + DEBUG (MTD_DEBUG_LEVEL0, "nand_writev: Attempted write past end of device\n"); return -EINVAL; } -retry: - /* Grab the lock and see if the device is available */ - spin_lock_bh (&this->chip_lock); - - switch (this->state) { - case FL_READY: - this->state = FL_WRITING; - spin_unlock_bh (&this->chip_lock); - break; - - default: - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&this->wq, &wait); - spin_unlock_bh (&this->chip_lock); - schedule(); + /* 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; + } - remove_wait_queue (&this->wq, &wait); - goto retry; - }; + // 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); - - /* Initialize return length value */ - *retlen = 0; + /* Grab the lock and see if the device is available */ + nand_get_chip (this, mtd, FL_WRITING, NULL); /* Select the NAND device */ nand_select (); /* Check the WP bit */ - nand_command (mtd, NAND_CMD_STATUS, -1, -1); - if (!(readb (this->IO_ADDR) & 0x80)) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_writev: Device is write protected!!!\n"); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + if (!(readb (this->IO_ADDR_R) & 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) { - /* Do any need pre-fill for partial page programming */ - for (i=0 ; i < cnt ; i++) - this->data_buf[i] = 0xff; - - /* - * Read data out of each tuple until we have a full page - * to write or we've read all the tuples. + /* + * 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. */ - while ((cnt < mtd->oobblock) && count) { - this->data_buf[cnt++] = - ((u_char *) vecs->iov_base)[len++]; + if ((vecs->iov_len - len) >= mtd->oobblock) { + this->data_poi = (u_char *) vecs->iov_base; + this->data_poi += len; + len += mtd->oobblock; + /* Check, if we have to switch to the next tuple */ if (len >= (int) vecs->iov_len) { vecs++; len = 0; count--; } + } else { + /* + * 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++]; + } + /* Check, if we have to switch to the next tuple */ + if (len >= (int) vecs->iov_len) { + vecs++; + len = 0; + count--; + } + } + this->data_poi = this->data_buf; } - /* Do any need post-fill for partial page programming */ - for (i=cnt ; i < mtd->oobblock ; i++) - this->data_buf[i] = 0xff; - -#ifdef CONFIG_MTD_NAND_ECC - /* Zero out the ECC array */ - for (i=0 ; i < 6 ; i++) - this->ecc_code_buf[i] = 0x00; - - /* Calculate and write the first ECC */ - if (col >= mtd->eccsize) { - nand_command (mtd, NAND_CMD_READ0, col, page); - for (i=0 ; i < col ; i++) - this->data_buf[i] = readb (this->IO_ADDR); - nand_calculate_ecc (&this->data_buf[0], - &(this->ecc_code_buf[0])); - for (i=0 ; i<3 ; i++) - this->data_buf[(mtd->oobblock + i)] = - this->ecc_code_buf[i]; - } - - /* Calculate and write the second ECC */ - if ((mtd->oobblock == 512) && (cnt == mtd->oobblock)) { - nand_calculate_ecc (&this->data_buf[256], - &(this->ecc_code_buf[3])); - for (i=3 ; i<6 ; i++) - this->data_buf[(mtd->oobblock + i)] = - this->ecc_code_buf[i]; - } - - /* Write ones for partial page programming */ - for (i=ecc_bytes ; i < mtd->oobsize ; i++) - this->data_buf[(mtd->oobblock + i)] = 0xff; -#else - /* Write ones for partial page programming */ - for (i=mtd->oobblock ; i < (mtd->oobblock + mtd->oobsize) ; i++) - this->data_buf[i] = 0xff; -#endif - /* Send command to begin auto page programming */ - nand_command (mtd, NAND_CMD_SEQIN, 0x00, page); - - /* Write out complete page of data */ - for (i=0 ; i < (mtd->oobblock + mtd->oobsize) ; i++) - writeb (this->data_buf[i], this->IO_ADDR); - - /* Send command to actually program the data */ - nand_command (mtd, NAND_CMD_PAGEPROG, -1, -1); - - /* - * Wait for program operation to complete. This could - * take up to 3000us (3ms) on some devices, so we try - * and exit as quickly as possible. - */ - status = 0; - for (i=0 ; i<24 ; i++) { - /* Delay for 125us */ - udelay (125); - - /* Check the status */ - nand_command (mtd, NAND_CMD_STATUS, -1, -1); - status = (int) readb (this->IO_ADDR); - if (status & 0x40) - break; - } - - /* See if device thinks it succeeded */ - if (status & 0x01) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_writev: " \ - "Failed write, page 0x%08x, " \ - "%6i bytes were succesful\n", page, *retlen); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; - } - -#ifdef CONFIG_MTD_NAND_VERIFY_WRITE - /* - * The NAND device assumes that it is always writing to - * a cleanly erased page. Hence, it performs its internal - * write verification only on bits that transitioned from - * 1 to 0. The device does NOT verify the whole page on a - * byte by byte basis. It is possible that the page was - * not completely erased or the page is becoming unusable - * due to wear. The read with ECC would catch the error - * later when the ECC page check fails, but we would rather - * catch it early in the page write stage. Better to write - * no data than invalid data. - */ - - /* Send command to read back the page */ - if (col < mtd->eccsize) - nand_command (mtd, NAND_CMD_READ0, col, page); - else - nand_command (mtd, NAND_CMD_READ1, col - 256, page); - - /* Loop through and verify the data */ - for (i=col ; i < cnt ; i++) { - if (this->data_buf[i] != readb (this->IO_ADDR)) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_writev: " \ - "Failed write verify, page 0x%08x, " \ - "%6i bytes were succesful\n", - page, *retlen); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; - } - } - -#ifdef CONFIG_MTD_NAND_ECC - /* - * We also want to check that the ECC bytes wrote - * correctly for the same reasons stated above. - */ - nand_command (mtd, NAND_CMD_READOOB, 0x00, page); - for (i=0 ; i < ecc_bytes ; i++) { - if ((readb (this->IO_ADDR) != this->ecc_code_buf[i]) && - this->ecc_code_buf[i]) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_writev: Failed ECC write " \ - "verify, page 0x%08x, " \ - "%6i bytes were succesful\n", - page, i); - nand_deselect (); - spin_lock_bh (&this->chip_lock); - this->state = FL_READY; - wake_up (&this->wq); - spin_unlock_bh (&this->chip_lock); - return -EIO; - } - } -#endif + /* We use the same function for write and writev !) */ + ret = nand_write_page (mtd, this, page, NULL, oobsel); + if (ret) + goto out; -#endif /* Update written bytes count */ - *retlen += (cnt - col); - - /* Reset written byte counter and column */ - col = cnt = 0; + written += mtd->oobblock;; /* Increment page address */ page++; } +out: /* De-select the NAND device */ nand_deselect (); @@ -1085,8 +1062,8 @@ retry: wake_up (&this->wq); spin_unlock_bh (&this->chip_lock); - /* Return happy */ - return 0; + *retlen = written; + return ret; } /* @@ -1094,53 +1071,33 @@ retry: */ static int nand_erase (struct mtd_info *mtd, struct erase_info *instr) { - int i, page, len, status, pages_per_block; + int page, len, status, pages_per_block, ret; struct nand_chip *this = mtd->priv; - DECLARE_WAITQUEUE(wait, current); + DECLARE_WAITQUEUE (wait, current); DEBUG (MTD_DEBUG_LEVEL3, - "nand_erase: start = 0x%08x, len = %i\n", - (unsigned int) instr->addr, (unsigned int) instr->len); + "nand_erase: start = 0x%08x, len = %i\n", (unsigned int) instr->addr, (unsigned int) instr->len); /* Start address must align on block boundary */ if (instr->addr & (mtd->erasesize - 1)) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_erase: Unaligned address\n"); + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Unaligned address\n"); return -EINVAL; } /* Length must align on block boundary */ if (instr->len & (mtd->erasesize - 1)) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_erase: Length not block aligned\n"); + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Length not block aligned\n"); return -EINVAL; } /* Do not allow erase past end of device */ if ((instr->len + instr->addr) > mtd->size) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_erase: Erase past end of device\n"); + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Erase past end of device\n"); return -EINVAL; } -retry: /* Grab the lock and see if the device is available */ - spin_lock_bh (&this->chip_lock); - - switch (this->state) { - case FL_READY: - this->state = FL_ERASING; - break; - - default: - set_current_state (TASK_UNINTERRUPTIBLE); - add_wait_queue (&this->wq, &wait); - spin_unlock_bh (&this->chip_lock); - schedule(); - - remove_wait_queue (&this->wq, &wait); - goto retry; - }; + nand_get_chip (this, mtd, FL_ERASING, NULL); /* Shift to get first page */ page = (int) (instr->addr >> this->page_shift); @@ -1152,81 +1109,78 @@ retry: nand_select (); /* Check the WP bit */ - nand_command (mtd, NAND_CMD_STATUS, -1, -1); - if (!(readb (this->IO_ADDR) & 0x80)) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_erase: Device is write protected!!!\n"); - nand_deselect (); - this->state = FL_READY; - spin_unlock_bh (&this->chip_lock); - return -EIO; + this->cmdfunc (mtd, NAND_CMD_STATUS, -1, -1); + if (!(readb (this->IO_ADDR_R) & 0x80)) { + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: Device is write protected!!!\n"); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; } /* Loop through the pages */ len = instr->len; + + instr->state = MTD_ERASING; + 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) { + 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; + } + /* Send commands to erase a page */ - nand_command(mtd, NAND_CMD_ERASE1, -1, page); - nand_command(mtd, NAND_CMD_ERASE2, -1, -1); + this->cmdfunc (mtd, NAND_CMD_ERASE1, -1, page); + this->cmdfunc (mtd, NAND_CMD_ERASE2, -1, -1); - /* - * Wait for program operation to complete. This could - * take up to 4000us (4ms) on some devices, so we try - * and exit as quickly as possible. - */ - status = 0; - for (i=0 ; i<32 ; i++) { - /* Delay for 125us */ - udelay (125); - - /* Check the status */ - nand_command (mtd, NAND_CMD_STATUS, -1, -1); - status = (int) readb (this->IO_ADDR); - if (status & 0x40) - break; - } + spin_unlock_bh (&this->chip_lock); + status = this->waitfunc (mtd, this, FL_ERASING); + /* Get spinlock, in case we exit */ + spin_lock_bh (&this->chip_lock); /* See if block erase succeeded */ if (status & 0x01) { - DEBUG (MTD_DEBUG_LEVEL0, - "nand_erase: " \ - "Failed erase, page 0x%08x\n", page); - nand_deselect (); - this->state = FL_READY; - spin_unlock_bh (&this->chip_lock); - return -EIO; + DEBUG (MTD_DEBUG_LEVEL0, "nand_erase: " "Failed erase, page 0x%08x\n", page); + instr->state = MTD_ERASE_FAILED; + goto erase_exit; + } + + /* Check, if we were interupted */ + if (this->state == FL_ERASING) { + /* Increment page address and decrement length */ + len -= mtd->erasesize; + page += pages_per_block; } - - /* Increment page address and decrement length */ - len -= mtd->erasesize; - page += pages_per_block; - /* Release the spin lock */ spin_unlock_bh (&this->chip_lock); - erase_retry: - /* Check the state and sleep if it changed */ spin_lock_bh (&this->chip_lock); - if (this->state == FL_ERASING) { + /* Check the state and sleep if it changed */ + if (this->state == FL_ERASING || this->state == FL_READY) { + /* Select the NAND device again, if we were interrupted */ + this->state = FL_ERASING; + nand_select (); continue; - } - else { + } else { set_current_state (TASK_UNINTERRUPTIBLE); add_wait_queue (&this->wq, &wait); spin_unlock_bh (&this->chip_lock); - schedule(); - + schedule (); remove_wait_queue (&this->wq, &wait); goto erase_retry; } } - spin_unlock_bh (&this->chip_lock); + instr->state = MTD_ERASE_DONE; +erase_exit: /* De-select the NAND device */ nand_deselect (); + spin_unlock_bh (&this->chip_lock); + ret = instr->state == MTD_ERASE_DONE ? 0 : -EIO;; /* Do call back function */ - if (instr->callback) + if (!ret && instr->callback) instr->callback (instr); /* The device is ready */ @@ -1234,8 +1188,8 @@ erase_retry: this->state = FL_READY; spin_unlock_bh (&this->chip_lock); - /* Return happy */ - return 0; + /* Return more or less happy */ + return ret; } /* @@ -1244,16 +1198,16 @@ erase_retry: static void nand_sync (struct mtd_info *mtd) { struct nand_chip *this = mtd->priv; - DECLARE_WAITQUEUE(wait, current); + DECLARE_WAITQUEUE (wait, current); DEBUG (MTD_DEBUG_LEVEL3, "nand_sync: called\n"); retry: /* Grab the spinlock */ - spin_lock_bh(&this->chip_lock); + spin_lock_bh (&this->chip_lock); /* See what's going on */ - switch(this->state) { + switch (this->state) { case FL_READY: case FL_SYNCING: this->state = FL_SYNCING; @@ -1270,7 +1224,7 @@ retry: goto retry; } - /* Lock the device */ + /* Lock the device */ spin_lock_bh (&this->chip_lock); /* Set the device to be ready again */ @@ -1279,7 +1233,7 @@ retry: wake_up (&this->wq); } - /* Unlock the device */ + /* Unlock the device */ spin_unlock_bh (&this->chip_lock); } @@ -1291,46 +1245,100 @@ int nand_scan (struct mtd_info *mtd) int i, nand_maf_id, nand_dev_id; struct nand_chip *this = mtd->priv; + /* check for proper chip_delay setup, set 20us if not */ + if (!this->chip_delay) + this->chip_delay = 20; + + /* check, if a user supplied command function given */ + if (this->cmdfunc == NULL) + this->cmdfunc = nand_command; + + /* check, if a user supplied wait function given */ + if (this->waitfunc == NULL) + this->waitfunc = nand_wait; + /* Select the device */ nand_select (); /* Send the command for reading device ID */ - nand_command (mtd, NAND_CMD_READID, 0x00, -1); + this->cmdfunc (mtd, NAND_CMD_READID, 0x00, -1); /* Read manufacturer and device IDs */ - nand_maf_id = readb (this->IO_ADDR); - nand_dev_id = readb (this->IO_ADDR); + nand_maf_id = readb (this->IO_ADDR_R); + nand_dev_id = readb (this->IO_ADDR_R); /* Print and store flash device information */ for (i = 0; nand_flash_ids[i].name != NULL; i++) { - if (nand_maf_id == nand_flash_ids[i].manufacture_id && - nand_dev_id == nand_flash_ids[i].model_id) { - if (!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; - if (nand_flash_ids[i].page256) { - mtd->oobblock = 256; - mtd->oobsize = 8; - this->page_shift = 8; - } - else { - mtd->oobblock = 512; - mtd->oobsize = 16; - this->page_shift = 9; - } + 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; + if (nand_flash_ids[i].page256) { + mtd->oobblock = 256; + mtd->oobsize = 8; + this->page_shift = 8; + } else { + mtd->oobblock = 512; + mtd->oobsize = 16; + this->page_shift = 9; } - printk (KERN_INFO "NAND device: Manufacture ID:" \ - " 0x%02x, Chip ID: 0x%02x (%s)\n", - nand_maf_id, nand_dev_id, mtd->name); + /* Try to identify manufacturer */ + for (i = 0; nand_manuf_ids[i].id != 0x0; i++) { + if (nand_manuf_ids[i].id == nand_maf_id) + break; + } + printk (KERN_INFO "NAND device: Manufacture ID:" + " 0x%02x, Chip ID: 0x%02x (%s %s)\n", nand_maf_id, nand_dev_id, + nand_manuf_ids[i].name , mtd->name); break; } } - /* Initialize state and spinlock */ + /* + * check ECC mode, default to software + * if 3byte/512byte hardware ECC is selected and we have 256 byte pagesize + * fallback to software ECC + */ + this->eccsize = 256; /* set default eccsize */ + + switch (this->eccmode) { + + case NAND_ECC_HW3_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; + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + break; + } else + this->eccsize = 512; /* set eccsize to 512 and fall through for function check */ + + case NAND_ECC_HW3_256: + if (this->calculate_ecc && this->correct_data && this->enable_hwecc) + break; + printk (KERN_WARNING "No ECC functions supplied, Hardware ECC not possible\n"); + 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; + + case NAND_ECC_SOFT: + this->calculate_ecc = nand_calculate_ecc; + this->correct_data = nand_correct_data; + break; + + default: + printk (KERN_WARNING "Invalid NAND_ECC_MODE %d\n", this->eccmode); + BUG(); + } + + /* Initialize state, waitqueue and spinlock */ this->state = FL_READY; - spin_lock_init(&this->chip_lock); + init_waitqueue_head (&this->wq); + spin_lock_init (&this->chip_lock); /* De-select the device */ nand_deselect (); @@ -1344,7 +1352,6 @@ int nand_scan (struct mtd_info *mtd) /* 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; @@ -1357,18 +1364,20 @@ int nand_scan (struct mtd_info *mtd) mtd->write_oob = nand_write_oob; mtd->readv = NULL; mtd->writev = nand_writev; + mtd->writev_ecc = nand_writev_ecc; mtd->sync = nand_sync; mtd->lock = NULL; mtd->unlock = NULL; mtd->suspend = NULL; mtd->resume = NULL; + mtd->owner = THIS_MODULE; /* Return happy */ return 0; } -EXPORT_SYMBOL(nand_scan); +EXPORT_SYMBOL (nand_scan); -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com"); -MODULE_DESCRIPTION("Generic NAND flash driver code"); +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Steven J. Hill <sjhill@realitydiluted.com>, Thomas Gleixner <tglx@linutronix.de>"); +MODULE_DESCRIPTION ("Generic NAND flash driver code"); diff --git a/drivers/mtd/nand/nand_ecc.c b/drivers/mtd/nand/nand_ecc.c index c4723d6b9a91..bb57398b3e79 100644 --- a/drivers/mtd/nand/nand_ecc.c +++ b/drivers/mtd/nand/nand_ecc.c @@ -1,14 +1,14 @@ /* * 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.6 2001/06/28 10:52:26 dwmw2 Exp $ + * $Id: nand_ecc.c,v 1.9 2003/02/20 13:34:19 sjhill 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 - * published by the Free Software Foundation. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * version 2.1 as published by the Free Software Foundation. * * This file contains an ECC algorithm from Toshiba that detects and * corrects 1 bit errors in a 256 byte block of data. @@ -209,5 +209,5 @@ EXPORT_SYMBOL(nand_calculate_ecc); EXPORT_SYMBOL(nand_correct_data); MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com>"); +MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com>"); MODULE_DESCRIPTION("Generic NAND ECC support"); diff --git a/drivers/mtd/nand/nand_ids.c b/drivers/mtd/nand/nand_ids.c new file mode 100644 index 000000000000..5a5518fe33eb --- /dev/null +++ b/drivers/mtd/nand/nand_ids.c @@ -0,0 +1,56 @@ +/* + * drivers/mtd/nandids.c + * + * Copyright (C) 2002 Thomas Gleixner (tglx@linutronix.de) + * + * + * $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 + * published by the Free Software Foundation. + * + */ +#include <linux/module.h> +#include <linux/mtd/nand.h> + +/* +* Chip ID list +*/ +struct nand_flash_dev nand_flash_ids[] = { + {"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,} +}; + +/* +* Manufacturer ID list +*/ +struct nand_manufacturers nand_manuf_ids[] = { + {NAND_MFR_TOSHIBA, "Toshiba"}, + {NAND_MFR_SAMSUNG, "Samsung"}, + {NAND_MFR_FUJITSU, "Fujitsu"}, + {NAND_MFR_NATIONAL, "National"}, + {0x0, "Unknown"} +}; + + +EXPORT_SYMBOL (nand_manuf_ids); +EXPORT_SYMBOL (nand_flash_ids); + +MODULE_LICENSE ("GPL"); +MODULE_AUTHOR ("Thomas Gleixner <tglx@linutronix.de>"); +MODULE_DESCRIPTION ("Nand device & manufacturer ID's"); diff --git a/drivers/mtd/nand/spia.c b/drivers/mtd/nand/spia.c index e798ddf2a7c9..e5e25eb3602b 100644 --- a/drivers/mtd/nand/spia.c +++ b/drivers/mtd/nand/spia.c @@ -1,9 +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) * - * $Id: spia.c,v 1.12 2001/10/02 15:05:14 dwmw2 Exp $ + * + * 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.19 2003/04/20 07:24:40 gleixner 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 @@ -68,6 +73,7 @@ __setup("spia_peddr=",spia_peddr); const static struct mtd_partition partition_info[] = { { .name = "SPIA flash partition 1", + .offset = 0, .size = 2*1024*1024 }, { @@ -78,6 +84,25 @@ const static struct mtd_partition partition_info[] = { }; #define NUM_PARTITIONS 2 + +/* + * hardware specific access to control-lines +*/ +void spia_hwcontrol(int cmd){ + + switch(cmd){ + + case NAND_CTL_SETCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x01; break; + case NAND_CTL_CLRCLE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x01; break; + + case NAND_CTL_SETALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x02; break; + case NAND_CTL_CLRALE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x02; break; + + case NAND_CTL_SETNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) &= ~0x04; break; + case NAND_CTL_CLRNCE: (*(volatile unsigned char *) (spia_io_base + spia_pedr)) |= 0x04; break; + } +} + /* * Main initialization routine */ @@ -110,11 +135,12 @@ int __init spia_init (void) (*(volatile unsigned char *) (spia_io_base + spia_peddr)) = 0x07; /* Set address of NAND IO lines */ - this->IO_ADDR = spia_fio_base; - this->CTRL_ADDR = spia_io_base + spia_pedr; - this->CLE = 0x01; - this->ALE = 0x02; - this->NCE = 0x04; + this->IO_ADDR_R = spia_fio_base; + this->IO_ADDR_W = spia_fio_base; + /* Set address of hardware control function */ + this->hwcontrol = spia_hwcontrol; + /* 15 us command delay time */ + this->chip_delay = 15; /* Scan to find existence of the device */ if (nand_scan (spia_mtd)) { @@ -159,5 +185,5 @@ module_exit(spia_cleanup); #endif MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Steven J. Hill <sjhill@cotw.com"); +MODULE_AUTHOR("Steven J. Hill <sjhill@realitydiluted.com"); MODULE_DESCRIPTION("Board-specific glue layer for NAND flash on SPIA board"); diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 6b5ab7bd56db..13cd52025caf 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -1,7 +1,7 @@ /* Linux driver for NAND Flash Translation Layer */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse <dwmw2@infradead.org> */ -/* $Id: nftlcore.c,v 1.82 2001/10/02 15:05:11 dwmw2 Exp $ */ +/* $Id: nftlcore.c,v 1.92 2003/05/23 11:41:47 dwmw2 Exp $ */ /* The contents of this file are distributed under the GNU General @@ -23,15 +23,13 @@ #include <linux/slab.h> #include <linux/sched.h> #include <linux/init.h> -#include <linux/blkpg.h> -#include <linux/buffer_head.h> +#include <linux/hdreg.h> -#ifdef CONFIG_KMOD #include <linux/kmod.h> -#endif #include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> #include <linux/mtd/nftl.h> -#include <linux/mtd/compatmac.h> +#include <linux/mtd/blktrans.h> /* maximum number of loops while examining next block, to have a chance to detect consistency problems (they should never happen @@ -39,154 +37,97 @@ #define MAX_LOOPS 10000 -/* NFTL block device stuff */ -#define MAJOR_NR NFTL_MAJOR - -#include <linux/blk.h> -#include <linux/hdreg.h> - -/* Linux-specific block device functions */ - -struct NFTLrecord *NFTLs[MAX_NFTLS]; -static struct request_queue nftl_queue; - -static void NFTL_setup(struct mtd_info *mtd) +static void nftl_add_mtd(struct mtd_blktrans_ops *tr, struct mtd_info *mtd) { - int i; struct NFTLrecord *nftl; unsigned long temp; - int firstfree = -1; - struct gendisk *gd; - - DEBUG(MTD_DEBUG_LEVEL1,"NFTL_setup\n"); - - for (i = 0; i < MAX_NFTLS; i++) { - if (!NFTLs[i] && firstfree == -1) - firstfree = i; - else if (NFTLs[i] && NFTLs[i]->mtd == mtd) { - /* This is a Spare Media Header for an NFTL we've already found */ - DEBUG(MTD_DEBUG_LEVEL1, "MTD already mounted as NFTL\n"); - return; - } - } - if (firstfree == -1) { - printk(KERN_WARNING "No more NFTL slot available\n"); + + if (mtd->ecctype != MTD_ECC_RS_DiskOnChip) return; - } + + DEBUG(MTD_DEBUG_LEVEL1, "NFTL: add_mtd for %s\n", mtd->name); nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); - gd = alloc_disk(1 << NFTL_PARTN_BITS); - if (!nftl || !gd) { - kfree(nftl); - put_disk(gd); - printk(KERN_WARNING "Out of memory for NFTL data structures\n"); + + if (!nftl) { + printk(KERN_WARNING "NFTL: out of memory for data structures\n"); return; } + memset(nftl, 0, sizeof(*nftl)); - init_MUTEX(&nftl->mutex); - - /* get physical parameters */ - nftl->EraseSize = mtd->erasesize; - nftl->nb_blocks = mtd->size / mtd->erasesize; - 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); - put_disk(gd); 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 */ + /* 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%lx.\n", nftl->nr_sects); - printk(KERN_WARNING "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 ); + 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; */ + if (add_mtd_blktrans_dev) { + if (nftl->ReplUnitTable) + kfree(nftl->ReplUnitTable); + if (nftl->EUNtable) + kfree(nftl->EUNtable); + kfree(nftl); + return; } - NFTLs[firstfree] = nftl; - sprintf(gd->disk_name, "nftl%c", 'a' + firstfree); - gd->major = MAJOR_NR; - gd->first_minor = firstfree << NFTL_PARTN_BITS; - set_capacity(gd, nftl->nr_sects); - nftl->disk = gd; - gd->private_data = nftl; - gd->queue = &nftl_queue; - add_disk(gd); +#ifdef PSYCHO_DEBUG + printk(KERN_INFO "NFTL: Found new nftl%c\n", nftl->mbd.devnum + 'a'); +#endif } -static void NFTL_unsetup(int i) +static void nftl_remove_dev(struct mtd_blktrans_dev *dev) { - struct NFTLrecord *nftl = NFTLs[i]; + struct NFTLrecord *nftl = (void *)dev; - DEBUG(MTD_DEBUG_LEVEL1, "NFTL_unsetup %d\n", i); - - NFTLs[i] = NULL; - + 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); - del_gendisk(nftl->disk); - put_disk(nftl->disk); 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); - } -} - -static void NFTL_notify_remove(struct mtd_info *mtd) -{ - int i; - - for (i = 0; i < MAX_NFTLS; i++) { - if (NFTLs[i] && NFTLs[i]->mtd == mtd) - NFTL_unsetup(i); - } -} - #ifdef CONFIG_NFTL_RW /* Actual NFTL access routines */ @@ -268,7 +209,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p 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) { @@ -385,7 +326,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p 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); } @@ -409,17 +350,17 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p if (BlockMap[block] == BLOCK_NIL) continue; - ret = MTD_READECC(nftl->mtd, (nftl->EraseSize * BlockMap[block]) - + (block * 512), 512, &retlen, movebuf, (char *)&oob); + 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); + 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), - 512, &retlen, movebuf, (char *)&oob); + MTD_WRITEECC(nftl->mbd.mtd, (nftl->EraseSize * targetEUN) + (block * 512), + 512, &retlen, movebuf, (char *)&oob, NAND_ECC_DISKONCHIP); } /* add the header so that it is now a valid chain */ @@ -427,7 +368,7 @@ static u16 NFTL_foldchain (struct NFTLrecord *nftl, unsigned thisVUC, unsigned p = 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. */ @@ -547,7 +488,7 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) 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", @@ -635,12 +576,12 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) 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 @@ -650,13 +591,13 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) /* 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); } @@ -669,8 +610,10 @@ static inline u16 NFTL_findwriteunit(struct NFTLrecord *nftl, unsigned block) 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; @@ -685,16 +628,18 @@ static int NFTL_writeblock(struct NFTLrecord *nftl, unsigned block, char *buffer return 1; } - MTD_WRITEECC(nftl->mtd, (writeEUN * nftl->EraseSize) + blockofs, - 512, &retlen, (char *)buffer, (char *)eccbuf); + 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 */ return 0; } #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); @@ -707,7 +652,7 @@ static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer) 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 @@ -726,13 +671,13 @@ static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer) 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; } @@ -748,15 +693,18 @@ static int NFTL_readblock(struct NFTLrecord *nftl, unsigned block, char *buffer) loff_t ptr = (lastgoodEUN * nftl->EraseSize) + blockofs; size_t retlen; u_char eccbuf[6]; - if (MTD_READECC(nftl->mtd, ptr, 512, &retlen, buffer, eccbuf)) + 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) +static int nftl_ioctl(struct mtd_blktrans_dev *dev, + struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg) { - struct NFTLrecord *nftl = inode->i_bdev->bd_disk->private_data; + struct NFTLrecord *nftl = (void *)dev; + switch (cmd) { case HDIO_GETGEO: { struct hd_geometry g; @@ -764,149 +712,15 @@ static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd g.heads = nftl->heads; g.sectors = nftl->sectors; g.cylinders = nftl->cylinders; - g.start = get_start_sect(inode->i_bdev); + g.start = 0; return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; } - case BLKFLSBUF: - fsync_bdev(inode->i_bdev); - invalidate_bdev(inode->i_bdev, 0); - if (nftl->mtd->sync) - nftl->mtd->sync(nftl->mtd); - return 0; - default: - return -EINVAL; - } -} - -void nftl_request(struct request_queue *q) -{ - struct request *req; - - while ((req = elv_next_request(q)) != NULL) { - unsigned block = req->sector; - unsigned nsect = req->current_nr_sectors; - char *buffer = req->buffer; - struct NFTLrecord *nftl = req->rq_disk->private_data; - int res = 1; /* succeed */ - - /* We can do this because the generic code knows not to - touch the request at the head of the queue */ - spin_unlock_irq(q->queue_lock); - - DEBUG(MTD_DEBUG_LEVEL2, "NFTL_request\n"); - DEBUG(MTD_DEBUG_LEVEL3, - "NFTL %s request, from sector 0x%04llx for %d sectors\n", - (req->cmd == READ) ? "Read " : "Write", - (unsigned long long)req->sector, req->current_nr_sectors); - - DEBUG(MTD_DEBUG_LEVEL3, "Waiting for mutex\n"); - down(&nftl->mutex); - DEBUG(MTD_DEBUG_LEVEL3, "Got mutex\n"); - - if (block + nsect > get_capacity(nftl->disk)) { - /* access past the end of device */ - printk("%s: bad access: block = %d, count = %d\n", - nftl->disk->disk_name, block, nsect); - up(&nftl->mutex); - res = 0; /* fail */ - goto repeat; - } - - 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 (rq_data_dir(req) == 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(q->queue_lock); - end_request(req, res); + default: + return -ENOTTY; } } -static struct kobject *nftl_probe(dev_t dev, int *part, void *data) -{ - request_module("docprobe"); - return NULL; -} - -static int nftl_open(struct inode *ip, struct file *fp) -{ - struct NFTLrecord *thisNFTL = ip->i_bdev->bd_disk->private_data; - - DEBUG(MTD_DEBUG_LEVEL2,"NFTL_open\n"); - if (!thisNFTL) - return -ENODEV; - -#ifndef CONFIG_NFTL_RW - if (fp->f_mode & FMODE_WRITE) - return -EROFS; -#endif /* !CONFIG_NFTL_RW */ - - if (!get_mtd_device(thisNFTL->mtd, -1)) - return /* -E'SBUGGEREDOFF */ -ENXIO; - - return 0; -} - -static int nftl_release(struct inode *inode, struct file *fp) -{ - struct NFTLrecord *thisNFTL = inode->i_bdev->bd_disk->private_data; - - DEBUG(MTD_DEBUG_LEVEL2, "NFTL_release\n"); - - if (thisNFTL->mtd->sync) - thisNFTL->mtd->sync(thisNFTL->mtd); - - put_mtd_device(thisNFTL->mtd); - - return 0; -} -static struct block_device_operations nftl_fops = -{ - .owner = THIS_MODULE, - .open = nftl_open, - .release = nftl_release, - .ioctl = nftl_ioctl -}; /**************************************************************************** * @@ -914,40 +728,33 @@ static struct block_device_operations nftl_fops = * ****************************************************************************/ -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, + .ioctl = nftl_ioctl, + .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[]; -static spinlock_t nftl_lock = SPIN_LOCK_UNLOCKED; int __init init_nftl(void) { + printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.92 $, nftlmount.c %s\n", nftlmountrev); -#ifdef PRERELEASE - printk(KERN_INFO "NFTL driver: nftlcore.c $Revision: 1.82 $, nftlmount.c %s\n", nftlmountrev); -#endif - - if (register_blkdev(MAJOR_NR, "nftl")) - return -EBUSY; - - blk_register_region(MKDEV(MAJOR_NR, 0), 256, - THIS_MODULE, nftl_probe, NULL, NULL); - - blk_init_queue(&nftl_queue, &nftl_request, &nftl_lock); - - register_mtd_user(&nftl_notifier); - - return 0; + return register_mtd_blktrans(&nftl_tr); } static void __exit cleanup_nftl(void) { - unregister_mtd_user(&nftl_notifier); - blk_unregister_region(MKDEV(MAJOR_NR, 0), 256); - unregister_blkdev(MAJOR_NR, "nftl"); - blk_cleanup_queue(&nftl_queue); + deregister_mtd_blktrans(&nftl_tr); } module_init(init_nftl); diff --git a/drivers/mtd/nftlmount.c b/drivers/mtd/nftlmount.c index 53622c7ce658..394a0a115d82 100644 --- a/drivers/mtd/nftlmount.c +++ b/drivers/mtd/nftlmount.c @@ -4,7 +4,7 @@ * Author: Fabrice Bellard (fabrice.bellard@netgem.com) * Copyright (C) 2000 Netgem S.A. * - * $Id: nftlmount.c,v 1.25 2001/11/30 16:46:27 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 @@ -22,23 +22,16 @@ */ #include <linux/kernel.h> -#include <linux/module.h> #include <asm/errno.h> -#include <asm/io.h> -#include <asm/uaccess.h> -#include <linux/miscdevice.h> -#include <linux/pci.h> #include <linux/delay.h> #include <linux/slab.h> -#include <linux/sched.h> -#include <linux/init.h> #include <linux/mtd/mtd.h> +#include <linux/mtd/nand.h> #include <linux/mtd/nftl.h> -#include <linux/mtd/compatmac.h> #define SECTORSIZE 512 -char nftlmountrev[]="$Revision: 1.25 $"; +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 @@ -50,11 +43,16 @@ static int find_boot_record(struct NFTLrecord *nftl) struct nftl_uci1 h1; struct nftl_oob oob; unsigned int block, boot_record_count = 0; - int retlen; + size_t retlen; u8 buf[SECTORSIZE]; struct NFTLMediaHeader *mh = &nftl->MediaHdr; unsigned int i; + /* 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->mbd.mtd->erasesize; + nftl->nb_blocks = nftl->mbd.mtd->size / nftl->EraseSize; + nftl->MediaUnit = BLOCK_NIL; nftl->SpareMediaUnit = BLOCK_NIL; @@ -64,12 +62,12 @@ static int find_boot_record(struct NFTLrecord *nftl) /* 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"); } @@ -80,16 +78,16 @@ static int find_boot_record(struct NFTLrecord *nftl) /* 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, + 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; } @@ -99,29 +97,28 @@ static int find_boot_record(struct NFTLrecord *nftl) */ 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) < 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; } #endif - /* OK, we like it. */ if (boot_record_count) { @@ -131,11 +128,19 @@ static int find_boot_record(struct NFTLrecord *nftl) 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 */ - return -1; + if (boot_record_count < 2) { + /* We haven't yet seen two real ones */ + return -1; + } + continue; } if (boot_record_count == 1) nftl->SpareMediaUnit = block; + /* Mark this boot record (NFTL MediaHeader) block as reserved */ + nftl->ReplUnitTable[block] = BLOCK_RESERVED; + + boot_record_count++; continue; } @@ -144,12 +149,18 @@ static int find_boot_record(struct NFTLrecord *nftl) memcpy(mh, buf, sizeof(struct NFTLMediaHeader)); /* Do some sanity checks on it */ - if (mh->UnitSizeFactor != 0xff) { - printk(KERN_NOTICE "Sorry, we don't support UnitSizeFactor " - "of != 1 yet.\n"); + if (mh->UnitSizeFactor == 0) { + printk(KERN_NOTICE "NFTL: UnitSizeFactor 0x00 detected. This violates the spec but we think we know what it means...\n"); + } else if (mh->UnitSizeFactor < 0xfc) { + printk(KERN_NOTICE "Sorry, we don't support UnitSizeFactor 0x%02x\n", + mh->UnitSizeFactor); return -1; + } else if (mh->UnitSizeFactor != 0xff) { + printk(KERN_NOTICE "WARNING: Support for NFTL with UnitSizeFactor 0x%02x is experimental\n", + mh->UnitSizeFactor); + 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) { printk(KERN_NOTICE "NFTL Media Header sanity check failed:\n"); @@ -166,21 +177,51 @@ static int find_boot_record(struct NFTLrecord *nftl) 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 */ nftl->nb_blocks = le16_to_cpu(mh->NumEraseUnits) + le16_to_cpu(mh->FirstPhysicalEUN); + /* XXX: will be suppressed */ + nftl->lastEUN = nftl->nb_blocks - 1; + + /* memory alloc */ + nftl->EUNtable = kmalloc(nftl->nb_blocks * sizeof(u16), GFP_KERNEL); + if (!nftl->EUNtable) { + printk(KERN_NOTICE "NFTL: allocation of EUNtable failed\n"); + return -ENOMEM; + } + + nftl->ReplUnitTable = kmalloc(nftl->nb_blocks * sizeof(u16), GFP_KERNEL); + if (!nftl->ReplUnitTable) { + kfree(nftl->EUNtable); + printk(KERN_NOTICE "NFTL: allocation of ReplUnitTable failed\n"); + return -ENOMEM; + } + + /* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */ + for (i = 0; i < nftl->nb_boot_blocks; i++) + nftl->ReplUnitTable[i] = BLOCK_RESERVED; + /* mark all remaining blocks as potentially containing data */ + for (; i < nftl->nb_blocks; i++) { + nftl->ReplUnitTable[i] = BLOCK_NOTEXPLORED; + } + + /* Mark this boot record (NFTL MediaHeader) block as reserved */ + nftl->ReplUnitTable[block] = BLOCK_RESERVED; + /* read the Bad Erase Unit Table and modify ReplUnitTable[] accordingly */ 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 + - i + SECTORSIZE, SECTORSIZE, - &retlen, buf, (char *)&oob)) < 0) { + 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", ret); + kfree(nftl->ReplUnitTable); + kfree(nftl->EUNtable); return -1; } } @@ -217,16 +258,16 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int 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; @@ -245,13 +286,13 @@ static int check_free_sectors(struct NFTLrecord *nftl, unsigned int address, int */ int NFTL_formatblock(struct NFTLrecord *nftl, int block) { - int retlen; + size_t retlen; unsigned int nb_erases, erase_mark; struct nftl_uci1 uci; 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; @@ -268,7 +309,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) /* 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 @@ -291,7 +332,7 @@ int NFTL_formatblock(struct NFTLrecord *nftl, int block) 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; @@ -317,7 +358,7 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b 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 @@ -337,7 +378,7 @@ static void check_sectors_in_chain(struct NFTLrecord *nftl, unsigned int first_b /* 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); } @@ -427,10 +468,10 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) { struct nftl_uci1 h1; unsigned int erase_mark; - int retlen; + 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; @@ -445,7 +486,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) 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 { @@ -457,7 +498,7 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) 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) { @@ -485,9 +526,9 @@ static int check_and_mark_free_block(struct NFTLrecord *nftl, int block) static int get_fold_mark(struct NFTLrecord *nftl, unsigned int block) { struct nftl_uci2 uci; - int retlen; + 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; @@ -502,44 +543,14 @@ int NFTL_mount(struct NFTLrecord *s) int chain_length, do_format_chain; struct nftl_uci0 h0; struct nftl_uci1 h1; - int retlen; - - /* XXX: will be suppressed */ - s->lastEUN = s->nb_blocks - 1; - - /* memory alloc */ - s->EUNtable = kmalloc(s->nb_blocks * sizeof(u16), GFP_KERNEL); - s->ReplUnitTable = kmalloc(s->nb_blocks * sizeof(u16), GFP_KERNEL); - if (!s->EUNtable || !s->ReplUnitTable) { - fail: - if (s->EUNtable) - kfree(s->EUNtable); - if (s->ReplUnitTable) - kfree(s->ReplUnitTable); - return -1; - } - - /* mark all blocks as potentially containing data */ - for (i = 0; i < s->nb_blocks; i++) { - s->ReplUnitTable[i] = BLOCK_NOTEXPLORED; - } + size_t retlen; /* search for NFTL MediaHeader and Spare NFTL Media Header */ if (find_boot_record(s) < 0) { printk("Could not find valid boot record\n"); - goto fail; + return -1; } - /* mark the bios blocks (blocks before NFTL MediaHeader) as reserved */ - for (i = 0; i < s->nb_boot_blocks; i++) - s->ReplUnitTable[i] = BLOCK_RESERVED; - - /* also mark the boot records (NFTL MediaHeader) blocks as reserved */ - if (s->MediaUnit != BLOCK_NIL) - s->ReplUnitTable[s->MediaUnit] = BLOCK_RESERVED; - if (s->SpareMediaUnit != BLOCK_NIL) - s->ReplUnitTable[s->SpareMediaUnit] = BLOCK_RESERVED; - /* init the logical to physical table */ for (i = 0; i < s->nb_blocks; i++) { s->EUNtable[i] = BLOCK_NIL; @@ -556,9 +567,9 @@ int NFTL_mount(struct NFTLrecord *s) 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; @@ -724,7 +735,7 @@ int NFTL_mount(struct NFTLrecord *s) /* second pass to format unreferenced blocks and init free block count */ s->numfreeEUNs = 0; - s->LastFreeEUN = BLOCK_NIL; + s->LastFreeEUN = le16_to_cpu(s->MediaHdr.FirstPhysicalEUN); for (block = 0; block < s->nb_blocks; block++) { if (s->ReplUnitTable[block] == BLOCK_NOTEXPLORED) { diff --git a/drivers/mtd/redboot.c b/drivers/mtd/redboot.c index 08d029fd9668..3e279992f6a9 100644 --- a/drivers/mtd/redboot.c +++ b/drivers/mtd/redboot.c @@ -1,5 +1,5 @@ /* - * $Id: redboot.c,v 1.6 2001/10/25 09:16:06 dwmw2 Exp $ + * $Id: redboot.c,v 1.11 2003/05/21 10:39:26 dwmw2 Exp $ * * Parse RedBoot-style Flash Image System (FIS) tables and * produce a Linux partition array to match. @@ -7,6 +7,7 @@ #include <linux/kernel.h> #include <linux/slab.h> +#include <linux/init.h> #include <linux/mtd/mtd.h> #include <linux/mtd/partitions.h> @@ -34,7 +35,9 @@ static inline int redboot_checksum(struct fis_image_desc *img) 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 parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **ppa 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 @@ int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **ppa goto out; } new_fl->img = &buf[i]; - buf[i].flash_base &= master->size-1; + 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 @@ int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **ppa 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; } @@ -137,7 +152,7 @@ int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **ppa 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 @@ int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **ppa return ret; } -EXPORT_SYMBOL(parse_redboot_partitions); +static struct mtd_part_parser redboot_parser = { + .owner = THIS_MODULE, + .parse_fn = parse_redboot_partitions, + .name = "RedBoot", +}; + +static int __init redboot_parser_init(void) +{ + return register_mtd_parser(&redboot_parser); +} + +static void __exit redboot_parser_exit(void) +{ + deregister_mtd_parser(&redboot_parser); +} + +module_init(redboot_parser_init); +module_exit(redboot_parser_exit); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Red Hat, Inc. - David Woodhouse <dwmw2@cambridge.redhat.com>"); diff --git a/fs/jffs2/background.c b/fs/jffs2/background.c index f72b1107c766..3cc44044a70a 100644 --- a/fs/jffs2/background.c +++ b/fs/jffs2/background.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: background.c,v 1.33 2002/11/12 09:44:30 dwmw2 Exp $ + * $Id: background.c,v 1.38 2003/05/26 09:50:38 dwmw2 Exp $ * */ @@ -16,9 +16,8 @@ #include <linux/kernel.h> #include <linux/jffs2.h> #include <linux/mtd/mtd.h> -#include <linux/interrupt.h> #include <linux/completion.h> -#include <linux/mtd/compatmac.h> /* recalc_sigpending() */ +#include <linux/sched.h> #include <linux/unistd.h> #include "nodelist.h" @@ -28,10 +27,10 @@ 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); + spin_lock(&c->erase_completion_lock); if (c->gc_task && 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 */ @@ -69,12 +68,12 @@ void jffs2_stop_garbage_collect_thread(struct jffs2_sb_info *c) flush_scheduled_work(); } - 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); } @@ -126,9 +125,9 @@ static int jffs2_garbage_collect_thread(void *_c) case SIGKILL: D1(printk(KERN_DEBUG "jffs2_garbage_collect_thread(): SIGKILL received.\n")); - spin_lock_bh(&c->erase_completion_lock); + 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: @@ -152,6 +151,7 @@ static int jffs2_garbage_collect_thread(void *_c) static int thread_should_wake(struct jffs2_sb_info *c) { int ret = 0; + uint32_t dirty; if (c->unchecked_size) { D1(printk(KERN_DEBUG "thread_should_wake(): unchecked_size %d, checked_ino #%d\n", @@ -159,8 +159,18 @@ static int thread_should_wake(struct jffs2_sb_info *c) 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 < JFFS2_RESERVED_BLOCKS_GCTRIGGER && - (c->dirty_size > c->sector_size)) + (dirty > c->sector_size)) ret = 1; D1(printk(KERN_DEBUG "thread_should_wake(): nr_free_blocks %d, nr_erasing_blocks %d, dirty_size 0x%x: %s\n", diff --git a/fs/jffs2/build.c b/fs/jffs2/build.c index 2049d0d1e4d2..01a575d6bfae 100644 --- a/fs/jffs2/build.c +++ b/fs/jffs2/build.c @@ -7,19 +7,42 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: build.c,v 1.42 2002/09/09 16:29:08 dwmw2 Exp $ + * $Id: build.c,v 1.46 2003/04/29 17:12:26 gleixner Exp $ * */ #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/slab.h> #include "nodelist.h" int jffs2_build_inode_pass1(struct jffs2_sb_info *, struct jffs2_inode_cache *); int jffs2_build_remove_unlinked_inode(struct jffs2_sb_info *, struct jffs2_inode_cache *); +static inline struct jffs2_inode_cache * +first_inode_chain(int *i, struct jffs2_sb_info *c) +{ + for (; *i < INOCACHE_HASHSIZE; (*i)++) { + if (c->inocache_list[*i]) + return c->inocache_list[*i]; + } + return NULL; +} + +static inline struct jffs2_inode_cache * +next_inode(int *i, struct jffs2_inode_cache *ic, struct jffs2_sb_info *c) +{ + /* More in this chain? */ + if (ic->next) + return ic->next; + (*i)++; + return first_inode_chain(i, c); +} -#define for_each_inode(i, c, ic) for (i=0; i<INOCACHE_HASHSIZE; i++) for (ic=c->inocache_list[i]; ic; ic=ic->next) +#define for_each_inode(i, c, ic) \ + for (i = 0, ic = first_inode_chain(&i, (c)); \ + ic; \ + ic = next_inode(&i, ic, (c))) /* Scan plan: - Scan physical nodes. Build map of inodes/dirents. Allocate inocaches as we go @@ -229,6 +252,7 @@ int jffs2_do_mount_fs(struct jffs2_sb_info *c) 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); diff --git a/fs/jffs2/compr.c b/fs/jffs2/compr.c index 5750fc2aac6e..a2479e837acd 100644 --- a/fs/jffs2/compr.c +++ b/fs/jffs2/compr.c @@ -7,14 +7,15 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr.c,v 1.24 2002/05/20 14:56:37 dwmw2 Exp $ + * $Id: compr.c,v 1.26 2003/01/12 13:21:28 dwmw2 Exp $ * */ -#ifdef __KERNEL__ +#if defined(__KERNEL__) || defined (__ECOS) #include <linux/kernel.h> #include <linux/string.h> #include <linux/errno.h> +#include <linux/types.h> #else #define KERN_DEBUG #define KERN_NOTICE diff --git a/fs/jffs2/compr_rtime.c b/fs/jffs2/compr_rtime.c index 83dfe53fe91b..9dec50194a2c 100644 --- a/fs/jffs2/compr_rtime.c +++ b/fs/jffs2/compr_rtime.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_rtime.c,v 1.9 2002/05/20 14:56:37 dwmw2 Exp $ + * $Id: compr_rtime.c,v 1.10 2003/05/11 10:47:13 dwmw2 Exp $ * * * Very simple lz77-ish encoder. @@ -30,7 +30,7 @@ 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; @@ -70,7 +70,7 @@ int jffs2_rtime_compress(unsigned char *data_in, unsigned char *cpage_out, 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 --git a/fs/jffs2/compr_zlib.c b/fs/jffs2/compr_zlib.c index e1dbbbf169ec..76ad2570a301 100644 --- a/fs/jffs2/compr_zlib.c +++ b/fs/jffs2/compr_zlib.c @@ -7,20 +7,22 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: compr_zlib.c,v 1.18 2002/05/20 14:56:37 dwmw2 Exp $ + * $Id: compr_zlib.c,v 1.23 2003/05/26 09:15:19 dwmw2 Exp $ * */ -#ifndef __KERNEL__ +#if !defined(__KERNEL__) && !defined(__ECOS) #error "The userspace support got too messy and was removed. Update your mkfs.jffs2" #endif #include <linux/config.h> #include <linux/kernel.h> -#include <linux/mtd/compatmac.h> /* for min() */ +#include <linux/vmalloc.h> +#include <linux/init.h> #include <linux/slab.h> -#include <linux/jffs2.h> #include <linux/zlib.h> +#include <linux/zutil.h> +#include <asm/semaphore.h> #include "nodelist.h" /* Plan: call deflate() with avail_in == *sourcelen, @@ -34,21 +36,21 @@ 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 */ 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())); @@ -57,97 +59,120 @@ int __init jffs2_zlib_init(void) void jffs2_zlib_exit(void) { - vfree(deflate_workspace); - vfree(inflate_workspace); + vfree(def_strm.workspace); + vfree(inf_strm.workspace); } +#endif /* __KERNEL__ */ 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 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 --git a/fs/jffs2/dir.c b/fs/jffs2/dir.c index 9c60f565699a..dceb9cbce3b9 100644 --- a/fs/jffs2/dir.c +++ b/fs/jffs2/dir.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: dir.c,v 1.73 2002/08/26 15:00:51 dwmw2 Exp $ + * $Id: dir.c,v 1.76 2003/05/26 09:50:38 dwmw2 Exp $ * */ @@ -16,13 +16,20 @@ #include <linux/sched.h> #include <linux/fs.h> #include <linux/crc32.h> -#include <linux/mtd/compatmac.h> /* For completion */ #include <linux/jffs2.h> #include <linux/jffs2_fs_i.h> #include <linux/jffs2_fs_sb.h> #include <linux/time.h> #include "nodelist.h" +/* Urgh. Please tell me there's a nicer way of doing this. */ +#include <linux/version.h> +#if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,48) +typedef int mknod_arg_t; +#else +typedef dev_t mknod_arg_t; +#endif + static int jffs2_readdir (struct file *, void *, filldir_t); static int jffs2_create (struct inode *,struct dentry *,int); @@ -32,7 +39,7 @@ 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,dev_t); +static int jffs2_mknod (struct inode *,struct dentry *,int,mknod_arg_t); static int jffs2_rename (struct inode *, struct dentry *, struct inode *, struct dentry *); @@ -211,8 +218,7 @@ static int jffs2_create(struct inode *dir_i, struct dentry *dentry, int mode) return ret; } - dir_i->i_mtime.tv_sec = dir_i->i_ctime.tv_sec = je32_to_cpu(ri->ctime); - dir_i->i_mtime.tv_nsec = dir_i->i_ctime.tv_nsec = 0; + dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(ri->ctime)); jffs2_free_raw_inode(ri); d_instantiate(dentry, inode); @@ -402,8 +408,7 @@ static int jffs2_symlink (struct inode *dir_i, struct dentry *dentry, const char return PTR_ERR(fd); } - dir_i->i_mtime.tv_sec = dir_i->i_ctime.tv_sec = je32_to_cpu(rd->mctime); - dir_i->i_mtime.tv_nsec = dir_i->i_ctime.tv_nsec = 0; + dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); jffs2_free_raw_dirent(rd); @@ -540,8 +545,7 @@ static int jffs2_mkdir (struct inode *dir_i, struct dentry *dentry, int mode) return PTR_ERR(fd); } - dir_i->i_mtime.tv_sec = dir_i->i_ctime.tv_sec = je32_to_cpu(rd->mctime); - dir_i->i_mtime.tv_nsec = dir_i->i_ctime.tv_nsec = 0; + dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); dir_i->i_nlink++; jffs2_free_raw_dirent(rd); @@ -573,7 +577,7 @@ static int jffs2_rmdir (struct inode *dir_i, struct dentry *dentry) return ret; } -static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, dev_t 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; @@ -583,7 +587,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de struct jffs2_full_dnode *fn; struct jffs2_full_dirent *fd; int namelen; - unsigned short dev; + jint16_t dev; int devlen = 0; uint32_t alloclen, phys_ofs; uint32_t writtenlen; @@ -596,7 +600,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de c = JFFS2_SB_INFO(dir_i->i_sb); if (S_ISBLK(mode) || S_ISCHR(mode)) { - dev = (MAJOR(rdev) << 8) | MINOR(rdev); + dev = cpu_to_je16((MAJOR(rdev) << 8) | MINOR(rdev)); devlen = sizeof(dev); } @@ -704,8 +708,7 @@ static int jffs2_mknod (struct inode *dir_i, struct dentry *dentry, int mode, de return PTR_ERR(fd); } - dir_i->i_mtime.tv_sec = dir_i->i_ctime.tv_sec = je32_to_cpu(rd->mctime); - dir_i->i_mtime.tv_nsec = dir_i->i_ctime.tv_nsec = 0; + dir_i->i_mtime = dir_i->i_ctime = ITIME(je32_to_cpu(rd->mctime)); jffs2_free_raw_dirent(rd); diff --git a/fs/jffs2/erase.c b/fs/jffs2/erase.c index ada5721b730f..6e4327692e8d 100644 --- a/fs/jffs2/erase.c +++ b/fs/jffs2/erase.c @@ -7,16 +7,17 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: erase.c,v 1.45 2002/10/09 08:27:08 dwmw2 Exp $ + * $Id: erase.c,v 1.51 2003/05/11 22:47:36 dwmw2 Exp $ * */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> -#include <linux/interrupt.h> #include <linux/compiler.h> #include <linux/crc32.h> +#include <linux/sched.h> +#include <linux/pagemap.h> #include "nodelist.h" struct erase_priv_struct { @@ -24,7 +25,10 @@ struct erase_priv_struct { 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); @@ -32,16 +36,25 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) { 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; } @@ -65,15 +78,18 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) 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); + c->dirty_size += c->sector_size; + jeb->dirty_size = c->sector_size; + spin_unlock(&c->erase_completion_lock); return; } @@ -82,20 +98,7 @@ void jffs2_erase_block(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) else printk(KERN_WARNING "Erase at 0x%08x failed immediately: errno %d\n", jeb->offset, ret); - /* Note: This is almost identical to jffs2_erase_failed() except - for the fact that we used spin_lock_bh() not spin_lock(). If - we could use spin_lock_bh() from a BH, we could merge them. - Or if we abandon the idea that MTD drivers may call the erase - callback from a BH, I suppose :) - */ - spin_lock_bh(&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_bh(&c->erase_completion_lock); - wake_up(&c->erase_wait); + jffs2_erase_failed(c, jeb); } void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) @@ -104,7 +107,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) down(&c->erase_free_sem); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); while (!list_empty(&c->erase_complete_list) || !list_empty(&c->erase_pending_list)) { @@ -112,7 +115,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) 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_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); jffs2_mark_erased_block(c, jeb); } else if (!list_empty(&c->erase_pending_list)) { @@ -126,7 +129,7 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) 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); @@ -136,10 +139,10 @@ void jffs2_erase_pending_blocks(struct jffs2_sb_info *c) /* Be nice */ cond_resched(); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); } - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_erase_pending_blocks completed\n")); up(&c->erase_free_sem); @@ -156,8 +159,7 @@ static void jffs2_erase_succeeded(struct jffs2_sb_info *c, struct jffs2_eraseblo jffs2_erase_pending_trigger(c); } - -static inline void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +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; @@ -169,6 +171,7 @@ static inline void jffs2_erase_failed(struct jffs2_sb_info *c, struct jffs2_eras wake_up(&c->erase_wait); } +#ifndef __ECOS static void jffs2_erase_callback(struct erase_info *instr) { struct erase_priv_struct *priv = (void *)instr->priv; @@ -181,6 +184,7 @@ static void jffs2_erase_callback(struct erase_info *instr) } kfree(instr); } +#endif /* !__ECOS */ /* Hmmm. Maybe we should accept the extra space it takes and make this a standard doubly-linked list? */ @@ -290,9 +294,9 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb printk(KERN_WARNING "Failed to allocate raw node ref for clean marker\n"); /* Stick it back on the list from whence it came and come back later */ jffs2_erase_pending_trigger(c); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); list_add(&jeb->list, &c->erase_complete_list); - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); return; } } @@ -313,7 +317,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb goto bad; } if (retlen != readlen) { - printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %d\n", ofs, readlen, retlen); + printk(KERN_WARNING "Short read from newly-erased block at 0x%08x. Wanted %d, got %zd\n", ofs, readlen, retlen); goto bad; } for (i=0; i<readlen; i += sizeof(unsigned long)) { @@ -328,13 +332,13 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb jffs2_write_nand_badblock( c ,jeb ); kfree(ebuf); bad2: - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); c->erasing_size -= c->sector_size; c->bad_size += c->sector_size; list_add_tail(&jeb->list, &c->bad_list); c->nr_erasing_blocks--; - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); wake_up(&c->erase_wait); return; } @@ -374,7 +378,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb goto bad2; } if (retlen != je32_to_cpu(marker.totlen)) { - 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, je32_to_cpu(marker.totlen), retlen); goto bad2; } @@ -392,7 +396,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb 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; @@ -403,7 +407,7 @@ static void jffs2_mark_erased_block(struct jffs2_sb_info *c, struct jffs2_eraseb list_add_tail(&jeb->list, &c->free_list); c->nr_erasing_blocks--; c->nr_free_blocks++; - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); wake_up(&c->erase_wait); } diff --git a/fs/jffs2/file.c b/fs/jffs2/file.c index f7fadfa10640..4d241373dc15 100644 --- a/fs/jffs2/file.c +++ b/fs/jffs2/file.c @@ -7,12 +7,11 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: file.c,v 1.81 2002/11/12 09:46:22 dwmw2 Exp $ + * $Id: file.c,v 1.85 2003/05/26 09:50:38 dwmw2 Exp $ * */ #include <linux/kernel.h> -#include <linux/mtd/compatmac.h> /* for min() */ #include <linux/slab.h> #include <linux/fs.h> #include <linux/time.h> @@ -153,17 +152,20 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr) 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_je32(iattr->ia_mode & ~S_ISGID); + ri->mode = cpu_to_jemode(iattr->ia_mode & ~S_ISGID); else - ri->mode = cpu_to_je32(iattr->ia_mode); + ri->mode = cpu_to_jemode(iattr->ia_mode); else - ri->mode = cpu_to_je32(inode->i_mode); + 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((ivalid & ATTR_ATIME)?iattr->ia_atime.tv_sec:inode->i_atime.tv_sec); - ri->mtime = cpu_to_je32((ivalid & ATTR_MTIME)?iattr->ia_mtime.tv_sec:inode->i_mtime.tv_sec); - ri->ctime = cpu_to_je32((ivalid & ATTR_CTIME)?iattr->ia_ctime.tv_sec:inode->i_ctime.tv_sec); + 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 */ @@ -188,13 +190,10 @@ int jffs2_setattr (struct dentry *dentry, struct iattr *iattr) return PTR_ERR(new_metadata); } /* It worked. Update the inode */ - inode->i_atime.tv_sec = je32_to_cpu(ri->atime); - inode->i_ctime.tv_sec = je32_to_cpu(ri->ctime); - inode->i_mtime.tv_sec = je32_to_cpu(ri->mtime); - inode->i_atime.tv_nsec = - inode->i_ctime.tv_nsec = - inode->i_mtime.tv_nsec = 0; - inode->i_mode = je32_to_cpu(ri->mode); + 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); @@ -309,7 +308,7 @@ int jffs2_prepare_write (struct file *filp, struct page *pg, unsigned start, uns ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); - ri.mode = cpu_to_je32(inode->i_mode); + 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)); @@ -390,7 +389,7 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi /* 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_je32(inode->i_mode); + 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); @@ -416,8 +415,7 @@ int jffs2_commit_write (struct file *filp, struct page *pg, unsigned start, unsi inode->i_size = (pg->index << PAGE_CACHE_SHIFT) + start + writtenlen; inode->i_blocks = (inode->i_size + 511) >> 9; - inode->i_ctime.tv_sec = inode->i_mtime.tv_sec = je32_to_cpu(ri->ctime); - inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0; + inode->i_ctime = inode->i_mtime = ITIME(je32_to_cpu(ri->ctime)); } } diff --git a/fs/jffs2/fs.c b/fs/jffs2/fs.c index b785d9a36055..a027504cd82d 100644 --- a/fs/jffs2/fs.c +++ b/fs/jffs2/fs.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: fs.c,v 1.19 2002/11/12 09:53:40 dwmw2 Exp $ + * $Id: fs.c,v 1.24 2003/04/29 09:52:58 dwmw2 Exp $ * */ @@ -16,7 +16,6 @@ #include <linux/sched.h> #include <linux/fs.h> #include <linux/list.h> -#include <linux/interrupt.h> #include <linux/mtd/mtd.h> #include <linux/pagemap.h> #include <linux/slab.h> @@ -35,7 +34,7 @@ int jffs2_statfs(struct super_block *sb, struct statfs *buf) buf->f_ffree = 0; buf->f_namelen = JFFS2_MAX_NAME_LEN; - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); avail = c->dirty_size + c->free_size; if (avail > c->sector_size * JFFS2_RESERVED_BLOCKS_WRITE) @@ -47,7 +46,7 @@ int jffs2_statfs(struct super_block *sb, struct statfs *buf) D1(jffs2_dump_block_lists(c)); - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); return 0; } @@ -87,16 +86,13 @@ void jffs2_read_inode (struct inode *inode) up(&f->sem); return; } - inode->i_mode = je32_to_cpu(latest_node.mode); + 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.tv_sec = je32_to_cpu(latest_node.atime); - inode->i_mtime.tv_sec = je32_to_cpu(latest_node.mtime); - inode->i_ctime.tv_sec = je32_to_cpu(latest_node.ctime); - inode->i_atime.tv_nsec = - inode->i_mtime.tv_nsec = - inode->i_ctime.tv_nsec = 0; + 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; @@ -104,7 +100,7 @@ void jffs2_read_inode (struct inode *inode) inode->i_blocks = (inode->i_size + 511) >> 9; switch (inode->i_mode & S_IFMT) { - unsigned short rdev; + jint16_t rdev; case S_IFLNK: inode->i_op = &jffs2_symlink_inode_operations; @@ -151,7 +147,7 @@ void jffs2_read_inode (struct inode *inode) case S_IFSOCK: case S_IFIFO: inode->i_op = &jffs2_file_inode_operations; - init_special_inode(inode, inode->i_mode, kdev_t_to_nr(mk_kdev(rdev>>8, rdev&0xff))); + init_special_inode(inode, inode->i_mode, kdev_t_to_nr(mk_kdev(je16_to_cpu(rdev)>>8, je16_to_cpu(rdev)&0xff))); break; default: @@ -232,7 +228,7 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i } else { ri->gid = cpu_to_je16(current->fsgid); } - ri->mode = cpu_to_je32(mode); + ri->mode = cpu_to_jemode(mode); ret = jffs2_do_new_inode (c, f, mode, ri); if (ret) { make_bad_inode(inode); @@ -241,12 +237,11 @@ struct inode *jffs2_new_inode (struct inode *dir_i, int mode, struct jffs2_raw_i } inode->i_nlink = 1; inode->i_ino = je32_to_cpu(ri->ino); - inode->i_mode = je32_to_cpu(ri->mode); + 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.tv_nsec = inode->i_ctime.tv_nsec = inode->i_mtime.tv_nsec = 0; - inode->i_atime.tv_sec = inode->i_ctime.tv_sec = inode->i_mtime.tv_sec = get_seconds(); - ri->atime = ri->mtime = ri->ctime = cpu_to_je32(inode->i_mtime.tv_sec); + 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; @@ -263,27 +258,32 @@ 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->sector_size = c->mtd->erasesize; c->flash_size = c->mtd->size; -#if 0 - if (c->sector_size < 0x10000) { - printk(KERN_INFO "jffs2: Erase block size too small (%dKiB). Using 64KiB instead\n", - c->sector_size / 1024); - c->sector_size = 0x10000; - } -#endif + /* + * 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); + 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); - /* Jörn -- stick alignment for weird 8-byte-page flash here */ + /* Joern -- stick alignment for weird 8-byte-page flash here */ if (jffs2_cleanmarker_oob(c)) { /* Cleanmarker is out-of-band, so inline size zero */ diff --git a/fs/jffs2/gc.c b/fs/jffs2/gc.c index 6a36bbbd0e5a..a715475bdec7 100644 --- a/fs/jffs2/gc.c +++ b/fs/jffs2/gc.c @@ -7,19 +7,22 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: gc.c,v 1.88 2002/10/08 16:56:08 dwmw2 Exp $ + * $Id: gc.c,v 1.103 2003/05/22 18:01:02 dwmw2 Exp $ * */ #include <linux/kernel.h> #include <linux/mtd/mtd.h> #include <linux/slab.h> -#include <linux/interrupt.h> #include <linux/pagemap.h> #include <linux/crc32.h> #include <linux/compiler.h> +#include <linux/stat.h> #include "nodelist.h" +static int jffs2_garbage_collect_pristine(struct jffs2_sb_info *c, + struct jffs2_inode_cache *ic, + struct jffs2_raw_node_ref *raw); static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, struct jffs2_inode_info *f, struct jffs2_full_dnode *fd); static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, @@ -32,6 +35,8 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, 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_cache *ic); /* Called with erase_completion_lock held */ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) @@ -107,61 +112,87 @@ static struct jffs2_eraseblock *jffs2_find_gc_block(struct jffs2_sb_info *c) */ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) { + struct jffs2_inode_cache *ic; 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; - uint32_t start = 0, end = 0, nrfrags = 0; uint32_t inum; - struct inode *inode; int ret = 0; 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; - while (c->unchecked_size) { /* We can't start doing GC yet. We haven't finished checking - the node CRCs etc. Do it now and wait for it. */ - struct jffs2_inode_cache *ic; - + 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) + + 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; } - if (ic->state != INO_STATE_UNCHECKED) { - D1(printk(KERN_DEBUG "Skipping check of ino #%d already in state %d\n", - ic->ino, ic->state)); + 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; - } - spin_unlock_bh(&c->erase_completion_lock); + 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(); - D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass() triggering inode scan of ino#%d\n", ic->ino)); - - { - /* XXX: This wants doing more sensibly -- split the core of jffs2_do_read_inode up */ - struct inode *i = iget(OFNI_BS_2SFFJ(c), ic->ino); - if (is_bad_inode(i)) { - printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", ic->ino); - ret = -EIO; - } - iput(i); + 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#%d\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; } @@ -174,7 +205,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 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; } @@ -197,7 +228,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) 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); + spin_unlock(&c->erase_completion_lock); up(&c->alloc_sem); BUG(); } @@ -207,7 +238,7 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) /* Inode-less node. Clean marker, snapshot or something like that */ /* 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_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, raw); up(&c->alloc_sem); goto eraseit_lock; @@ -216,13 +247,136 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) inum = jffs2_raw_ref_to_inum(raw); D1(printk(KERN_DEBUG "Inode number is #%u\n", inum)); - spin_unlock_bh(&c->erase_completion_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), inum)); + + /* 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. + */ + spin_lock(&c->inocache_lock); + ic = jffs2_get_ino_cache(c, inum); + + /* This should never fail unless I'm particularly stupid. + So we don't check before dereferencing it */ + + 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("Ino #%u is absent but node not REF_PRISTINE. Reading.\n", + inum)); + } + break; + + case INO_STATE_PRESENT: + case INO_STATE_UNCHECKED: + /* It's in-core or hasn't been checked. GC must iget() it. */ + break; + + case INO_STATE_CHECKING: + /* Should never happen. We should have finished checking + by the time we actually start doing any GC. */ + BUG(); + + + case INO_STATE_GC: + /* Should never happen. We are holding the alloc_sem, + no other garbage collection can happen. Note that we + do depend on this later when deciding to do a simple + node copy */ + 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", + inum, 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; + + } + + spin_unlock(&c->inocache_lock); + + /* 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) { + ret = jffs2_garbage_collect_pristine(c, ic, raw); + jffs2_set_inocache_state(c, ic, INO_STATE_CHECKEDABSENT); + + if (ret != -EBADFD) + goto release_sem; + + /* Fall through if it wanted us to */ + } + + ret = jffs2_garbage_collect_live(c, jeb, raw, ic); + + release_sem: + up(&c->alloc_sem); + + 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; +} - D1(printk(KERN_DEBUG "jffs2_garbage_collect_pass collecting from block @0x%08x. Node @0x%08x, ino #%u\n", jeb->offset, ref_offset(raw), inum)); - inode = iget(OFNI_BS_2SFFJ(c), inum); +static int jffs2_garbage_collect_live(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb, + struct jffs2_raw_node_ref *raw, struct jffs2_inode_cache *ic) +{ + 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; + struct inode *inode; + int ret = 0; + + inode = iget(OFNI_BS_2SFFJ(c), ic->ino); if (is_bad_inode(inode)) { - printk(KERN_NOTICE "Eep. read_inode() failed for ino #%u\n", inum); + 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. */ up(&c->alloc_sem); iput(inode); @@ -254,16 +408,26 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) end = frag->ofs + frag->size; #if 1 /* Temporary debugging sanity checks, till we're ready to _trust_ the REF_PRISTINE flag stuff */ if (!nrfrags && ref_flags(fn->raw) == REF_PRISTINE) { - if (fn->frags > 1) + if (fn->frags > 1) { printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had %d frags. Tell dwmw2\n", ref_offset(raw), fn->frags); - - if (frag->ofs & (PAGE_CACHE_SIZE-1) && frag_prev(frag) && frag_prev(frag)->node) + mark_ref_normal(raw); + } + /* 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) { printk(KERN_WARNING "REF_PRISTINE node at 0x%08x had a previous non-hole frag in the same page. Tell dwmw2\n", ref_offset(raw)); + mark_ref_normal(raw); + } - if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->node) + if ((frag->ofs+frag->size) & (PAGE_CACHE_SIZE-1) && frag_next(frag) && frag_next(frag)->size < PAGE_CACHE_SIZE) { 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(raw), frag->ofs, frag->ofs+frag->size); + mark_ref_normal(raw); + } } #endif if (!nrfrags++) @@ -273,6 +437,15 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) } } if (fn) { + if (ref_flags(raw) == REF_PRISTINE) { + ret = jffs2_garbage_collect_pristine(c, ic, raw); + if (!ret) { + /* Urgh. Return it sensibly. */ + frag->node->raw = ic->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. */ @@ -305,25 +478,145 @@ int jffs2_garbage_collect_pass(struct jffs2_sb_info *c) } 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; + + D1(printk(KERN_DEBUG "Going to GC REF_PRISTINE node at 0x%08x\n", ref_offset(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, raw->totlen), + &phys_ofs, &alloclen); + if (ret) + return ret; + + if (alloclen < raw->totlen) { + /* Doesn't fit untouched. We'll go the old route and split it */ + return -EBADFD; + } + + node = kmalloc(raw->totlen, GFP_KERNEL); + if (!node) + return -ENOMEM; + + ret = jffs2_flash_read(c, ref_offset(raw), raw->totlen, &retlen, (char *)node); + if (!ret && retlen != raw->totlen) + 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; + } + } + 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; } - spin_unlock_bh(&c->erase_completion_lock); + nraw = jffs2_alloc_raw_node_ref(); + if (!nraw) { + ret = -ENOMEM; + goto out_node; + } + nraw->flash_offset = phys_ofs; + nraw->totlen = raw->totlen; + nraw->next_phys = NULL; + + /* OK, all the CRCs are good; this node can just be copied as-is. */ + + ret = jffs2_flash_write(c, phys_ofs, raw->totlen, &retlen, (char *)node); + if (ret || (retlen != raw->totlen)) { + printk(KERN_NOTICE "Write of %d bytes at 0x%08x failed. returned %d, retlen %zd\n", + raw->totlen, 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(raw); + } + 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, @@ -331,7 +624,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ { struct jffs2_full_dnode *new_fn; struct jffs2_raw_inode ri; - unsigned short dev; + jint16_t dev; char *mdata = NULL, mdatalen = 0; uint32_t alloclen, phys_ofs; int ret; @@ -340,8 +633,8 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ S_ISCHR(JFFS2_F_I_MODE(f)) ) { /* For these, we don't actually need to read the old node */ /* FIXME: for minor or major > 255. */ - dev = ((JFFS2_F_I_RDEV_MAJ(f) << 8) | - JFFS2_F_I_RDEV_MIN(f)); + 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)); @@ -364,7 +657,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ 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; } @@ -377,7 +670,7 @@ static int jffs2_garbage_collect_metadata(struct jffs2_sb_info *c, struct jffs2_ ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); - ri.mode = cpu_to_je32(JFFS2_F_I_MODE(f)); + 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)); @@ -431,7 +724,7 @@ static int jffs2_garbage_collect_dirent(struct jffs2_sb_info *c, struct jffs2_er 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; } @@ -489,7 +782,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct continue; } if (retlen != sizeof(struct jffs2_unknown_node)) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading header from obsolete node at %08x\n", + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading header from obsolete node at %08x\n", retlen, sizeof(struct jffs2_unknown_node), ref_offset(raw)); continue; } @@ -506,7 +799,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct continue; } if (retlen != sizeof(rd)) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading from obsolete node at %08x\n", + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %zd) reading from obsolete node at %08x\n", retlen, sizeof(rd), ref_offset(raw)); continue; } @@ -534,7 +827,7 @@ static int jffs2_garbage_collect_deletion_dirent(struct jffs2_sb_info *c, struct continue; } if (retlen != name_len+1) { - printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%d not %d) reading name from obsolete node at %08x\n", + printk(KERN_WARNING "jffs2_g_c_deletion_dirent(): Short read (%zd not %d) reading name from obsolete node at %08x\n", retlen, name_len+1, ref_offset(raw)); continue; } @@ -597,7 +890,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras write it out again with the _same_ version as before */ 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 hole 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 (je16_to_cpu(ri.nodetype) != JFFS2_NODETYPE_INODE) { @@ -607,7 +900,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras return -EIO; } 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%x\n", + 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; @@ -642,7 +935,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras ri.csize = cpu_to_je32(0); ri.compr = JFFS2_COMPR_ZERO; } - ri.mode = cpu_to_je32(JFFS2_F_I_MODE(f)); + 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)); @@ -654,7 +947,7 @@ static int jffs2_garbage_collect_hole(struct jffs2_sb_info *c, struct jffs2_eras 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; } @@ -762,8 +1055,11 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era * page OK. We'll actually write it out again in commit_write, which is a little * suboptimal, but at least we're correct. */ +#ifdef __ECOS + pg = read_cache_page(start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); +#else pg = read_cache_page(inode->i_mapping, start >> PAGE_CACHE_SHIFT, (void *)jffs2_do_readpage_unlock, inode); - +#endif if (IS_ERR(pg)) { printk(KERN_WARNING "read_cache_page() returned error: %ld\n", PTR_ERR(pg)); return PTR_ERR(pg); @@ -780,11 +1076,11 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era 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)); @@ -804,7 +1100,7 @@ static int jffs2_garbage_collect_dnode(struct jffs2_sb_info *c, struct jffs2_era ri.ino = cpu_to_je32(f->inocache->ino); ri.version = cpu_to_je32(++f->highest_version); - ri.mode = cpu_to_je32(JFFS2_F_I_MODE(f)); + 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)); diff --git a/fs/jffs2/malloc.c b/fs/jffs2/malloc.c index f8b7ba5a46d8..4346f9874a2f 100644 --- a/fs/jffs2/malloc.c +++ b/fs/jffs2/malloc.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: malloc.c,v 1.22 2002/05/20 14:56:38 dwmw2 Exp $ + * $Id: malloc.c,v 1.24 2003/03/11 17:30:29 gleixner Exp $ * */ @@ -23,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; @@ -73,8 +76,7 @@ int __init jffs2_create_slab_caches(void) inode_cache_slab = kmem_cache_create("jffs2_inode_cache", sizeof(struct jffs2_inode_cache), - 0, JFFS2_SLAB_POISON|SLAB_RECLAIM_ACCOUNT, - NULL, NULL); + 0, JFFS2_SLAB_POISON, NULL, NULL); if (inode_cache_slab) return 0; err: @@ -112,75 +114,92 @@ void jffs2_free_full_dirent(struct jffs2_full_dirent *x) 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 --git a/fs/jffs2/nodelist.c b/fs/jffs2/nodelist.c index 4bd3c3d65960..294854d372f7 100644 --- a/fs/jffs2/nodelist.c +++ b/fs/jffs2/nodelist.c @@ -7,17 +7,18 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.c,v 1.65 2002/11/12 09:50:13 dwmw2 Exp $ + * $Id: nodelist.c,v 1.79 2003/04/08 08:20:01 dwmw2 Exp $ * */ #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/fs.h> #include <linux/mtd/mtd.h> -#include <linux/interrupt.h> #include <linux/rbtree.h> #include <linux/crc32.h> #include <linux/slab.h> +#include <linux/pagemap.h> #include "nodelist.h" void jffs2_add_fd_to_list(struct jffs2_sb_info *c, struct jffs2_full_dirent *new, struct jffs2_full_dirent **list) @@ -112,10 +113,10 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode 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_bh(&c->erase_completion_lock); + 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 */ @@ -127,12 +128,12 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode /* 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_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); cond_resched(); /* FIXME: point() */ - err = jffs2_flash_read(c, (ref_offset(ref)), min(ref->totlen, sizeof(node)), &retlen, (void *)&node); + err = jffs2_flash_read(c, (ref_offset(ref)), min_t(uint32_t, ref->totlen, sizeof(node)), &retlen, (void *)&node); if (err) { printk(KERN_WARNING "error %d reading node at 0x%08x in get_inode_nodes()\n", err, ref_offset(ref)); goto free_out; @@ -140,7 +141,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode /* 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, sizeof(node.u))) { printk(KERN_WARNING "short read in get_inode_nodes()\n"); err = -EIO; goto free_out; @@ -158,6 +159,14 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode err = -EIO; goto free_out; } + /* 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)) { @@ -166,6 +175,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode ref_offset(ref)); BUG(); } + fd = jffs2_alloc_full_dirent(node.d.nsize+1); if (!fd) { err = -ENOMEM; @@ -187,7 +197,7 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode dirent we've already read from the flash */ if (retlen > sizeof(struct jffs2_raw_dirent)) - memcpy(&fd->name[0], &node.d.name[0], min((uint32_t)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? @@ -244,46 +254,95 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode 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_bh(&c->erase_completion_lock); + 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)) { - /* FIXME: point() */ - char *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; + 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)); - kfree(buf); + 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_bh(&c->erase_completion_lock); + 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]; jeb->used_size += ref->totlen; jeb->unchecked_size -= ref->totlen; c->used_size += ref->totlen; c->unchecked_size -= ref->totlen; - mark_ref_normal(ref); + /* 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(); @@ -323,13 +382,15 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode 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]; jeb->used_size += ref->totlen; jeb->unchecked_size -= ref->totlen; c->used_size += ref->totlen; c->unchecked_size -= ref->totlen; - + 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)) { @@ -361,10 +422,10 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode } } - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); } - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); *tnp = ret_tn; *fdp = ret_fd; @@ -376,12 +437,24 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode return err; } -struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino) +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) { @@ -391,8 +464,6 @@ struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino) if (ret && ret->ino != ino) ret = NULL; - spin_unlock(&c->inocache_lock); - D2(printk(KERN_DEBUG "jffs2_get_ino_cache found %p for ino %u\n", ret, ino)); return ret; } diff --git a/fs/jffs2/nodelist.h b/fs/jffs2/nodelist.h index 5ead344453d1..0cfad439f844 100644 --- a/fs/jffs2/nodelist.h +++ b/fs/jffs2/nodelist.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodelist.h,v 1.87 2002/11/12 13:36:18 dwmw2 Exp $ + * $Id: nodelist.h,v 1.93 2003/02/24 21:47:28 dwmw2 Exp $ * */ @@ -16,12 +16,17 @@ #include <linux/config.h> #include <linux/fs.h> - -#include <linux/mtd/compatmac.h> /* For min/max in older kernels */ +#include <linux/types.h> #include <linux/jffs2.h> #include <linux/jffs2_fs_sb.h> #include <linux/jffs2_fs_i.h> + +#ifdef __ECOS +#include "os-ecos.h" +#else +#include <linux/mtd/compatmac.h> /* For min/max in older kernels */ #include "os-linux.h" +#endif #ifndef CONFIG_JFFS2_FS_DEBUG #define CONFIG_JFFS2_FS_DEBUG 2 @@ -98,13 +103,18 @@ struct jffs2_inode_cache { uint32_t ino; int nlink; int state; -#define INO_STATE_UNCHECKED 0 -#define INO_STATE_CHECKING 1 -#define INO_STATE_CHECKEDABSENT 2 -#define INO_STATE_READINGINODE 3 -#define INO_STATE_PRESENT 5 }; +/* 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 { @@ -281,7 +291,8 @@ int jffs2_get_inode_nodes(struct jffs2_sb_info *c, ino_t ino, struct jffs2_inode struct jffs2_tmp_dnode_info **tnp, struct jffs2_full_dirent **fdp, uint32_t *highest_version, uint32_t *latest_mctime, uint32_t *mctime_ver); -struct jffs2_inode_cache *jffs2_get_ino_cache(struct jffs2_sb_info *c, int ino); +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); @@ -315,10 +326,10 @@ int jffs2_do_link (struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, uint /* readinode.c */ void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uint32_t size); -int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_full_dnode *fn); int jffs2_add_full_dnode_to_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, struct jffs2_full_dnode *fn); 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 */ diff --git a/fs/jffs2/nodemgmt.c b/fs/jffs2/nodemgmt.c index 8baac2d30906..62ab0469ac03 100644 --- a/fs/jffs2/nodemgmt.c +++ b/fs/jffs2/nodemgmt.c @@ -7,14 +7,15 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: nodemgmt.c,v 1.84 2002/11/12 11:17:29 dwmw2 Exp $ + * $Id: nodemgmt.c,v 1.94 2003/02/19 17:50:26 gleixner Exp $ * */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> -#include <linux/interrupt.h> +#include <linux/compiler.h> +#include <linux/sched.h> /* For cond_resched() */ #include "nodelist.h" /** @@ -54,26 +55,57 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs D1(printk(KERN_DEBUG "jffs2_reserve_space(): alloc sem got\n")); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); - /* this needs a little more thought */ + /* this needs a little more thought (true <tglx> :)) */ while(ret == -EAGAIN) { while(c->nr_free_blocks + c->nr_erasing_blocks < blocksneeded) { int ret; + uint32_t dirty, avail; up(&c->alloc_sem); - if (c->dirty_size + c->unchecked_size < c->sector_size) { + /* 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->sector_size) { D1(printk(KERN_DEBUG "dirty size 0x%08x + unchecked_size 0x%08x < sector size 0x%08x, returning -ENOSPC\n", - c->dirty_size, c->unchecked_size, c->sector_size)); - spin_unlock_bh(&c->erase_completion_lock); + dirty, c->unchecked_size, c->sector_size)); + spin_unlock(&c->erase_completion_lock); + 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) { + 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); 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, 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_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); ret = jffs2_garbage_collect_pass(c); if (ret) @@ -85,7 +117,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs 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); @@ -93,7 +125,7 @@ int jffs2_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, uint32_t *ofs 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; @@ -106,14 +138,14 @@ int jffs2_reserve_space_gc(struct jffs2_sb_info *c, uint32_t minsize, uint32_t * 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; } @@ -127,10 +159,10 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui /* Skip the end of this block and file it as having some dirty space */ /* If there's a pending write to it, flush now */ if (c->wbuf_len) { - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); D1(printk(KERN_DEBUG "jffs2_do_reserve_space: Flushing write buffer\n")); jffs2_flush_wbuf(c, 1); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); /* We know nobody's going to have changed nextblock. Just continue */ } c->wasted_size += jeb->free_size; @@ -186,9 +218,9 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui !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_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf(c, 1); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); /* Have another go. It'll be on the erasable_list now */ return -EAGAIN; } @@ -203,6 +235,11 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui } /* Make sure this can't deadlock. Someone has to start the erases of erase_pending blocks */ +#ifdef __ECOS + /* In eCos, we don't have a handy kernel thread doing the erases for + us. We do them ourselves right now. */ + jffs2_erase_pending_blocks(c); +#else 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. (erasableempty: %s, erasingempty: %s, erasependingempty: %s)\n", @@ -212,13 +249,14 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui D1(printk(KERN_DEBUG "Triggering pending erases\n")); jffs2_erase_pending_trigger(c); } - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); schedule(); remove_wait_queue(&c->erase_wait, &wait); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); if (signal_pending(current)) { return -EINTR; } +#endif /* An erase may have failed, decreasing the amount of free space available. So we must restart from the beginning */ @@ -248,9 +286,9 @@ static int jffs2_do_reserve_space(struct jffs2_sb_info *c, uint32_t minsize, ui already set c->nextblock so that jffs2_mark_node_obsolete() won't try to refile it to the dirty_list. */ - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); jffs2_mark_node_obsolete(c, jeb->first_node); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); } D1(printk(KERN_DEBUG "jffs2_do_reserve_space(): Giving 0x%x bytes at 0x%x\n", *len, *ofs)); @@ -276,7 +314,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r uint32_t len = new->totlen; jeb = &c->blocks[new->flash_offset / c->sector_size]; - D1(printk(KERN_DEBUG "jffs2_add_physical_node_ref(): Node at 0x%x, size 0x%x\n", ref_offset(new), 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 || (ref_offset(new)) != jeb->offset + (c->sector_size - jeb->free_size)) { printk(KERN_WARNING "argh. node added in wrong place\n"); @@ -284,7 +322,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r return -EINVAL; } #endif - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); if (!jeb->first_node) jeb->first_node = new; @@ -308,9 +346,9 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); if (c->wbuf_len) { /* Flush the last write in the block if it's outstanding */ - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); jffs2_flush_wbuf(c, 1); - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); } list_add_tail(&jeb->list, &c->clean_list); @@ -319,7 +357,7 @@ int jffs2_add_physical_node_ref(struct jffs2_sb_info *c, struct jffs2_raw_node_r ACCT_SANITY_CHECK(c,jeb); D1(ACCT_PARANOIA_CHECK(jeb)); - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); return 0; } @@ -337,7 +375,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref struct jffs2_eraseblock *jeb; int blocknr; struct jffs2_unknown_node n; - int ret; + int ret, addedsize; size_t retlen; if(!ref) { @@ -355,7 +393,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref } jeb = &c->blocks[blocknr]; - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); if (ref_flags(ref) == REF_UNCHECKED) { D1(if (unlikely(jeb->unchecked_size < ref->totlen)) { @@ -377,14 +415,17 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref c->used_size -= ref->totlen; } + // Take care, that wasted size is taken into concern if ((jeb->dirty_size || ISDIRTY(jeb->wasted_size + ref->totlen)) && jeb != c->nextblock) { D1(printk("Dirtying\n")); - jeb->dirty_size += ref->totlen + jeb->wasted_size; - c->dirty_size += ref->totlen + jeb->wasted_size; + addedsize = ref->totlen + jeb->wasted_size; + jeb->dirty_size += addedsize; + c->dirty_size += addedsize; c->wasted_size -= jeb->wasted_size; jeb->wasted_size = 0; } else { D1(printk("Wasting\n")); + addedsize = 0; jeb->wasted_size += ref->totlen; c->wasted_size += ref->totlen; } @@ -400,7 +441,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref 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; } @@ -417,6 +458,10 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref if (c->wbuf_len) { D1(printk(KERN_DEBUG "...and adding to erasable_pending_wbuf_list\n")); list_add_tail(&jeb->list, &c->erasable_pending_wbuf_list); +#if 0 /* This check was added to allow us to find places where we added nodes to the lists + after dropping the alloc_sem, and it did that just fine. But it also caused us to + lock the alloc_sem in other places, like clear_inode(), when we wouldn't otherwise + have needed to. So I suspect it's outlived its usefulness. Thomas? */ /* We've changed the rules slightly. After writing a node you now mustn't drop the @@ -436,6 +481,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref printk(KERN_CRIT "jffs2_mark_node_obsolete() called with wbuf active but alloc_sem not locked!\n"); BUG(); } +#endif } else { if (jiffies & 127) { /* Most of the time, we just erase it immediately. Otherwise we @@ -454,7 +500,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref D1(printk(KERN_DEBUG "Done OK\n")); } 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 - ref->totlen)) { + } 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")); @@ -470,7 +516,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref jeb->offset, jeb->free_size, jeb->dirty_size, jeb->used_size)); } - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); if (!jffs2_can_mark_obsolete(c)) return; @@ -484,7 +530,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref return; } if (retlen != sizeof(n)) { - printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %d\n", ref_offset(ref), retlen); + printk(KERN_WARNING "Short read from obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); return; } if (PAD(je32_to_cpu(n.totlen)) != PAD(ref->totlen)) { @@ -503,7 +549,7 @@ void jffs2_mark_node_obsolete(struct jffs2_sb_info *c, struct jffs2_raw_node_ref return; } if (retlen != sizeof(n)) { - printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %d\n", ref_offset(ref), retlen); + printk(KERN_WARNING "Short write in obliterating obsoleted node at 0x%08x: %zd\n", ref_offset(ref), retlen); return; } } diff --git a/fs/jffs2/os-linux.h b/fs/jffs2/os-linux.h index a5c35fdb51c8..d08ca2e3a152 100644 --- a/fs/jffs2/os-linux.h +++ b/fs/jffs2/os-linux.h @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: os-linux.h,v 1.21 2002/11/12 09:44:30 dwmw2 Exp $ + * $Id: os-linux.h,v 1.26 2003/05/16 18:45:25 dwmw2 Exp $ * */ @@ -15,6 +15,11 @@ #define __JFFS2_OS_LINUX_H__ #include <linux/version.h> +/* JFFS2 uses Linux mode bits natively -- no need for conversion */ +#define os_to_jffs2_mode(x) (x) +#define jffs2_to_os_mode(x) (x) + + #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,2) #define JFFS2_INODE_INFO(i) (list_entry(i, struct jffs2_inode_info, vfs_inode)) #define OFNI_EDONI_2SFFJ(f) (&(f)->vfs_inode) @@ -37,9 +42,6 @@ #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) -#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) #if LINUX_VERSION_CODE > KERNEL_VERSION(2,5,1) #define JFFS2_F_I_RDEV_MIN(f) (minor(OFNI_EDONI_2SFFJ(f)->i_rdev)) @@ -49,6 +51,21 @@ #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 + /* Hmmm. P'raps generic code should only ever see versions of signal functions which do the locking automatically? */ #if LINUX_VERSION_CODE < KERNEL_VERSION(2,5,40) @@ -57,6 +74,16 @@ #define current_sig_lock current->sighand->siglock #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) diff --git a/fs/jffs2/read.c b/fs/jffs2/read.c index 4e8b3a1c624c..7216eb97cd46 100644 --- a/fs/jffs2/read.c +++ b/fs/jffs2/read.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: read.c,v 1.29 2002/11/12 09:51:22 dwmw2 Exp $ + * $Id: read.c,v 1.31 2003/01/14 14:06:22 dwmw2 Exp $ * */ @@ -39,7 +39,7 @@ int jffs2_read_dnode(struct jffs2_sb_info *c, struct jffs2_full_dnode *fd, unsig } 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", + 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; } @@ -197,8 +197,9 @@ int jffs2_read_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 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, frag->ofs+fragofs+readlen, - ref_offset(frag->node->raw))); + 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) { diff --git a/fs/jffs2/readinode.c b/fs/jffs2/readinode.c index 92ba6f553d2d..4f935bfa2fd0 100644 --- a/fs/jffs2/readinode.c +++ b/fs/jffs2/readinode.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: readinode.c,v 1.95 2002/11/12 11:17:29 dwmw2 Exp $ + * $Id: readinode.c,v 1.106 2003/05/14 06:53:26 dwmw2 Exp $ * */ @@ -17,11 +17,13 @@ #include <linux/crc32.h> #include <linux/pagemap.h> #include <linux/mtd/mtd.h> -#include <linux/interrupt.h> +#include <linux/compiler.h> #include "nodelist.h" +static int jffs2_add_frag_to_fragtree(struct jffs2_sb_info *c, struct rb_root *list, struct jffs2_node_frag *newfrag); -D1(static void jffs2_print_fragtree(struct rb_root *list, int permitbug) +#if CONFIG_JFFS2_FS_DEBUG >= 1 +static void jffs2_print_fragtree(struct rb_root *list, int permitbug) { struct jffs2_node_frag *this = frag_first(list); uint32_t lastofs = 0; @@ -44,31 +46,17 @@ D1(static void jffs2_print_fragtree(struct rb_root *list, int permitbug) printk(KERN_CRIT "Frag tree got a hole in it\n"); BUG(); } -}) +} -D1(void jffs2_print_frag_list(struct jffs2_inode_info *f) +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", ref_offset(f->metadata->raw)); } -}) - - -/* 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; - D1(printk(KERN_DEBUG "jffs2_add_full_dnode_to_inode(ino #%u, f %p, fn %p)\n", f->inocache->ino, f, fn)); - - ret = jffs2_add_full_dnode_to_fraglist(c, &f->fragtree, fn); - - D2(jffs2_print_frag_list(f)); - return ret; } +#endif /* D1 */ static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_frag *this) { @@ -91,26 +79,24 @@ static void jffs2_obsolete_node_frag(struct jffs2_sb_info *c, struct jffs2_node_ jffs2_free_node_frag(this); } -/* Doesn't set inode->i_size */ -int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *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) { - struct jffs2_node_frag *this; + int ret; struct jffs2_node_frag *newfrag; - uint32_t lastend; + + 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; - } - - if (!fn->raw) { - printk(KERN_WARNING "dwmw2 is stupid. j_a_f_d_t_f should never happen with ->raw == NULL\n"); - BUG(); - } - 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)); + 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; } @@ -120,8 +106,42 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *li newfrag->node = fn; newfrag->node->frags = 1; + 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(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 */ - this = jffs2_lookup_node_frag(list, fn->ofs); + this = jffs2_lookup_node_frag(list, newfrag->node->ofs); if (this) { D2(printk(KERN_DEBUG "j_a_f_d_t_f: Lookup gave frag 0x%04x-0x%04x; phys 0x%08x (*%p)\n", @@ -143,16 +163,18 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *li if ((lastend-1) >> PAGE_CACHE_SHIFT == newfrag->ofs >> PAGE_CACHE_SHIFT) { if (this->node) mark_ref_normal(this->node->raw); - mark_ref_normal(fn->raw); + mark_ref_normal(newfrag->node->raw); } - if (lastend < fn->ofs) { + 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->size = newfrag->node->ofs - lastend; holefrag->node = NULL; if (this) { /* By definition, the 'this' node has no right-hand child, @@ -190,9 +212,9 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *li if (newfrag->ofs > this->ofs) { /* This node isn't completely obsoleted. The start of it remains valid */ - /* Mark the new node and the partially covered node REF_NORMAL -- let + /* Mark the new node and the partially covered node REF_NORMAL -- let the GC take a look at them */ - mark_ref_normal(fn->raw); + mark_ref_normal(newfrag->node->raw); if (this->node) mark_ref_normal(this->node->raw); @@ -283,7 +305,7 @@ int jffs2_add_full_dnode_to_fraglist(struct jffs2_sb_info *c, struct rb_root *li /* 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(fn->raw); + mark_ref_normal(newfrag->node->raw); return 0; } @@ -314,24 +336,54 @@ void jffs2_truncate_fraglist (struct jffs2_sb_info *c, struct rb_root *list, uin /* Scan the list of all nodes present for this ino, build map of versions, etc. */ +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_full_dnode *fn = NULL; - uint32_t crc; - uint32_t latest_mctime, mctime_ver; - uint32_t mdata_ver = 0; - size_t retlen; - int ret; - D2(printk(KERN_DEBUG "jffs2_do_read_inode(): getting inocache\n")); + 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; + + 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; + + default: + BUG(); + } + } + spin_unlock(&c->inocache_lock); if (!f->inocache && ino == 1) { /* Special case - no root inode on medium */ f->inocache = jffs2_alloc_inode_cache(); @@ -343,19 +395,61 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 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_do_read_inode() on nonexistent ino %u\n", ino); return -ENOENT; } - D1(printk(KERN_DEBUG "jffs2_do_read_inode(): ino #%u nlink is %d\n", ino, 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, 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 %u returned %d\n", ino, ret); + 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; @@ -365,13 +459,21 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, fn = tn->fn; - if (f->metadata && 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; + if (f->metadata) { + if (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 { + D1(printk(KERN_DEBUG "Er. New metadata at 0x%08x with ver %d is actually older than previous %d\n", + ref_offset(f->metadata->raw), tn->version, mdata_ver)); + jffs2_mark_node_obsolete(c, fn->raw); + jffs2_free_full_dnode(fn); + goto next_tn; + } } if (fn->size) { @@ -382,31 +484,36 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, f->metadata = fn; mdata_ver = tn->version; } + next_tn: tn_list = tn->next; jffs2_free_tmp_dnode_info(tn); } if (!fn) { /* No data nodes for this inode. */ - if (ino != 1) { - printk(KERN_WARNING "jffs2_do_read_inode(): No data nodes found for ino #%u\n", 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) { + 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_je32(S_IFDIR|S_IRUGO|S_IWUSR|S_IXUGO); + 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; } 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, %ld of %d bytes read\n", - ret, (long)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); @@ -415,13 +522,13 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, 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", ino, ref_offset(fn->raw)); + 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(je32_to_cpu(latest_node->mode) & S_IFMT) { + switch(jemode_to_cpu(latest_node->mode) & S_IFMT) { case S_IFDIR: if (mctime_ver > je32_to_cpu(latest_node->version)) { /* The times in the latest_node are actually older than @@ -447,23 +554,26 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, case S_IFBLK: case S_IFCHR: - /* Xertain inode types should have only one data node, and it's + /* 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", ino, je32_to_cpu(latest_node->mode)); + 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; } if (!frag_first(&f->fragtree)) { - printk(KERN_WARNING "Argh. Special inode #%u with mode 0%o has no fragments\n", ino, je32_to_cpu(latest_node->mode)); + 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 0%o had more than one node\n", ino, je32_to_cpu(latest_node->mode)); + 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); @@ -475,7 +585,8 @@ int jffs2_do_read_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, f->fragtree = RB_ROOT; break; } - f->inocache->state = INO_STATE_PRESENT; + if (f->inocache->state == INO_STATE_READING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_PRESENT); return 0; } @@ -494,7 +605,7 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) the nodes are marked obsolete, and jffs2_g_c_pass() won't call iget() for the inode in question. - We also do this to keep the (maybe temporary) BUG() in + We also used to do this to keep the temporary BUG() in jffs2_mark_node_obsolete() from triggering. */ if(deleted) @@ -518,8 +629,8 @@ void jffs2_do_clear_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f) jffs2_free_full_dirent(fd); } - if (f->inocache) - f->inocache->state = INO_STATE_CHECKEDABSENT; + if (f->inocache && f->inocache->state != INO_STATE_CHECKING) + jffs2_set_inocache_state(c, f->inocache, INO_STATE_CHECKEDABSENT); up(&f->sem); diff --git a/fs/jffs2/scan.c b/fs/jffs2/scan.c index 889f055133b4..8ed8212bb5eb 100644 --- a/fs/jffs2/scan.c +++ b/fs/jffs2/scan.c @@ -7,10 +7,11 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: scan.c,v 1.92 2002/09/09 16:29:08 dwmw2 Exp $ + * $Id: scan.c,v 1.99 2003/04/28 10:17:17 dwmw2 Exp $ * */ #include <linux/kernel.h> +#include <linux/sched.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> #include <linux/pagemap.h> @@ -70,23 +71,21 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) uint32_t empty_blocks = 0, bad_blocks = 0; unsigned char *flashbuf = NULL; uint32_t buf_size = 0; +#ifndef __ECOS size_t pointlen; - if (!c->blocks) { - printk(KERN_WARNING "EEEK! c->blocks is NULL!\n"); - return -EINVAL; - } 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%x\n", pointlen)); - c->mtd->unpoint(c->mtd, flashbuf); + 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 */ @@ -237,9 +236,10 @@ int jffs2_scan_medium(struct jffs2_sb_info *c) } if (buf_size) kfree(flashbuf); +#ifndef __ECOS else - c->mtd->unpoint(c->mtd, flashbuf); - + c->mtd->unpoint(c->mtd, flashbuf, 0, c->mtd->size); +#endif return 0; } @@ -255,7 +255,7 @@ static int jffs2_fill_scan_buf (struct jffs2_sb_info *c, unsigned char *buf, return ret; } if (retlen < len) { - D1(printk(KERN_WARNING "Read at 0x%x gave only 0x%x bytes\n", ofs, retlen)); + 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)); @@ -366,7 +366,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo prevofs = ofs; if (jeb->offset + c->sector_size < ofs + sizeof(*node)) { - D1(printk(KERN_DEBUG "Fewer than %d bytes left to end of block. (%x+%x<%x+%x) Not reading\n", sizeof(struct jffs2_unknown_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; @@ -374,7 +374,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo 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 %d bytes (node header) left to end of buf. Reading 0x%x at 0x%08x\n", + 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) @@ -410,8 +410,8 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo /* Ran off end. */ D1(printk(KERN_DEBUG "Empty flash ends normally at 0x%08x\n", ofs)); - if (buf_ofs == jeb->offset && jeb->used_size == PAD(c->cleanmarker_size) && - !jeb->first_node->next_in_ino && !jeb->dirty_size) + 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; @@ -430,7 +430,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo continue; } if (je16_to_cpu(node->magic) == JFFS2_DIRTY_BITMASK) { - D1(printk(KERN_DEBUG "Empty bitmask at 0x%08x\n", ofs)); + D1(printk(KERN_DEBUG "Dirty bitmask at 0x%08x\n", ofs)); DIRTY_SPACE(4); ofs += 4; continue; @@ -492,7 +492,7 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo case JFFS2_NODETYPE_INODE: 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 %d bytes (inode node) left to end of buf. Reading 0x%x at 0x%08x\n", + 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) @@ -585,8 +585,8 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo } - 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)); + 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) { @@ -596,9 +596,10 @@ static int jffs2_scan_eraseblock (struct jffs2_sb_info *c, struct jffs2_eraseblo jeb->wasted_size = 0; } - if ((jeb->used_size + jeb->unchecked_size) == PAD(c->cleanmarker_size) && - !jeb->first_node->next_in_ino && !jeb->dirty_size) + 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; diff --git a/fs/jffs2/super.c b/fs/jffs2/super.c index e87cdaececb6..8bc53c400875 100644 --- a/fs/jffs2/super.c +++ b/fs/jffs2/super.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: super.c,v 1.74 2002/11/12 09:37:39 dwmw2 Exp $ + * $Id: super.c,v 1.79 2003/05/27 22:35:42 dwmw2 Exp $ * */ @@ -23,7 +23,6 @@ #include <linux/jffs2.h> #include <linux/pagemap.h> #include <linux/mtd/mtd.h> -#include <linux/interrupt.h> #include <linux/ctype.h> #include <linux/namei.h> #include "nodelist.h" @@ -53,7 +52,7 @@ static void jffs2_i_init_once(void * foo, kmem_cache_t * cachep, unsigned long f if ((flags & (SLAB_CTOR_VERIFY|SLAB_CTOR_CONSTRUCTOR)) == SLAB_CTOR_CONSTRUCTOR) { - init_MUTEX(&ei->sem); + init_MUTEX_LOCKED(&ei->sem); inode_init_once(&ei->vfs_inode); } } @@ -101,9 +100,9 @@ static int jffs2_sb_set(struct super_block *sb, void *data) return 0; } -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) +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; @@ -153,9 +152,9 @@ jffs2_get_sb_mtd(struct file_system_type *fs_type, int flags, return sb; } -static struct super_block * -jffs2_get_sb_mtdnr(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data, int mtdnr) +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 mtd_info *mtd; @@ -168,9 +167,9 @@ jffs2_get_sb_mtdnr(struct file_system_type *fs_type, int flags, return jffs2_get_sb_mtd(fs_type, flags, dev_name, data, mtd); } -static struct super_block * -jffs2_get_sb(struct file_system_type *fs_type, int flags, - const char *dev_name, void *data) +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; diff --git a/fs/jffs2/wbuf.c b/fs/jffs2/wbuf.c index b63b05425e55..2ba43b76297a 100644 --- a/fs/jffs2/wbuf.c +++ b/fs/jffs2/wbuf.c @@ -7,39 +7,40 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: wbuf.c,v 1.20 2002/11/12 11:33:02 dwmw2 Exp $ - * + some of the dependencies on later MTD NAND code temporarily reverted. + * $Id: wbuf.c,v 1.30 2003/02/19 17:48:49 gleixner Exp $ * */ #include <linux/kernel.h> #include <linux/slab.h> #include <linux/mtd/mtd.h> -#include <linux/interrupt.h> #include <linux/crc32.h> #include <linux/mtd/nand.h> #include "nodelist.h" -/* FIXME duplicated defines in wbuf.c and nand.c - * Constants for out of band layout - */ -#ifndef NAND_BADBLOCK_POS -#define NAND_BADBLOCK_POS 5 -#endif -#ifndef NAND_JFFS2_OOB_BADBPOS -#define NAND_JFFS2_OOB_BADBPOS 5 -#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 - /* 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 +#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 + +struct nand_oobinfo jffs2_oobinfo = { + useecc: 1, + eccpos: {JFFS2_OOB_ECCPOS0, JFFS2_OOB_ECCPOS1, JFFS2_OOB_ECCPOS2, JFFS2_OOB_ECCPOS3, JFFS2_OOB_ECCPOS4, JFFS2_OOB_ECCPOS5} +}; + static inline void jffs2_refile_wbuf_blocks(struct jffs2_sb_info *c) { struct list_head *this, *next; @@ -178,13 +179,14 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) /* else jffs2_flash_writev has actually filled in the rest of the buffer for us, and will deal with the node refs etc. later. */ - ret = c->mtd->write(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf); + ret = c->mtd->write_ecc(c->mtd, c->wbuf_ofs, c->wbuf_pagesize, &retlen, c->wbuf, NULL, &jffs2_oobinfo); if (ret || retlen != c->wbuf_pagesize) { if (ret) printk(KERN_CRIT "jffs2_flush_wbuf(): Write failed with %d\n",ret); else - printk(KERN_CRIT "jffs2_flush_wbuf(): Write was short %d instead of %d\n",retlen,c->wbuf_pagesize); + printk(KERN_CRIT "jffs2_flush_wbuf(): Write was short: %zd instead of %d\n", + retlen, c->wbuf_pagesize); ret = -EIO; /* CHECKME NAND @@ -205,7 +207,7 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) /* 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_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); if (!c->nextblock) BUG(); /* wbuf_pagesize - wbuf_len is the amount of space that's to be @@ -222,13 +224,13 @@ int jffs2_flush_wbuf(struct jffs2_sb_info *c, int pad) 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_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); } /* Stick any now-obsoleted blocks on the erase_pending_list */ - spin_lock_bh(&c->erase_completion_lock); + spin_lock(&c->erase_completion_lock); jffs2_refile_wbuf_blocks(c); - spin_unlock_bh(&c->erase_completion_lock); + spin_unlock(&c->erase_completion_lock); memset(c->wbuf,0xff,c->wbuf_pagesize); /* adjust write buffer offset, else we get a non contigous write bug */ @@ -394,7 +396,7 @@ int jffs2_flash_writev(struct jffs2_sb_info *c, const struct iovec *invecs, unsi outvecs[splitvec].iov_len = split_ofs; /* We did cross a page boundary, so we write some now */ - ret = jffs2_flash_direct_writev(c, outvecs, splitvec+1, outvec_to, &wbuf_retlen); + ret = c->mtd->writev_ecc(c->mtd, outvecs, splitvec+1, outvec_to, &wbuf_retlen, NULL, &jffs2_oobinfo); if (ret < 0 || wbuf_retlen != PAGE_DIV(totlen)) { /* At this point we have no problem, c->wbuf is empty. @@ -442,11 +444,19 @@ alldone: } /* - This is the entry for NOR-Flash. We use it also for NAND to flush wbuf + * 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) { - return c->mtd->write(c->mtd, ofs, len, retlen, 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); } /* @@ -459,10 +469,11 @@ int jffs2_flash_read(struct jffs2_sb_info *c, loff_t ofs, size_t len, size_t *re /* Read flash */ if (!jffs2_can_mark_obsolete(c)) { - ret = c->mtd->read(c->mtd, ofs, len, retlen, buf); + ret = c->mtd->read_ecc(c->mtd, ofs, len, retlen, buf, NULL, &jffs2_oobinfo); if ( (ret == -EIO) && (*retlen == len) ) { - printk(KERN_WARNING "mtd->read(0x%x bytes from 0x%llx) returned ECC error\n", len, ofs); + 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. @@ -549,7 +560,7 @@ int jffs2_check_oob_empty( struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb if (retlen < len) { D1(printk(KERN_WARNING "jffs2_check_oob_empty(): Read OOB return short read " - "(%d bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); + "(%zd bytes not %d) for block at %08x\n", retlen, len, jeb->offset)); ret = -EIO; goto out; } @@ -593,69 +604,83 @@ out: return ret; } -int jffs2_check_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) +/* +* 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; - size_t retlen; - int fsdata_pos,fsdata_len, oob_size, badblock_pos; + int ret, i, cnt, retval = 0; + size_t retlen, offset; + int fsdata_pos, fsdata_len, oob_size, badblock_pos; + offset = jeb->offset; oob_size = c->mtd->oobsize; - switch(c->mtd->ecctype) { - case MTD_ECC_SW: + switch (c->mtd->ecctype) { + case MTD_ECC_SW: fsdata_pos = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDAPOS : NAND_JFFS2_OOB16_FSDAPOS; fsdata_len = (c->wbuf_pagesize == 256) ? NAND_JFFS2_OOB8_FSDALEN : NAND_JFFS2_OOB16_FSDALEN; badblock_pos = NAND_BADBLOCK_POS; break; default: - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Invalid ECC type\n")); + D1 (printk (KERN_WARNING "jffs2_write_nand_cleanmarker(): Invalid ECC type\n")); return -EINVAL; - } - - /* - * We read oob data from page 0 and 1 of the block. - * page 0 contains cleanmarker and badblock info - * page 2 contains failure count of this block - */ - ret = c->mtd->read_oob(c->mtd, jeb->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 (%d bytes not %d) for block at %08x\n", retlen, oob_size << 1 , jeb->offset)); - return -EIO; } - /* Check for bad block marker */ - if (buf[badblock_pos] != 0xff) { - D1(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n",jeb->offset)); - return 2; - } - /* Check for failure counter in the second page */ - if (buf[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[badblock_pos+oob_size])); - return 3; - } + /* 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); - 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 < fsdata_len; i++) { - if (buf[fsdata_pos+i] != p[i]) { - D2(printk(KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset)); - return 1; + 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[badblock_pos] != 0xff) { + D1 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Bad block at %08x\n", jeb->offset)); + return 2; + } + + /* Check for failure counter in the second page */ + if (buf[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[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 < fsdata_len; i++) { + if (buf[fsdata_pos + i] != p[i]) { + D2 (printk (KERN_WARNING "jffs2_check_nand_cleanmarker(): Cleanmarker node not detected in block at %08x\n", jeb->offset)); + retval = 1; + } + } + } + offset += c->mtd->erasesize; } - - return 0; + return retval; } int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_eraseblock *jeb) @@ -686,7 +711,7 @@ int jffs2_write_nand_cleanmarker(struct jffs2_sb_info *c, struct jffs2_erasebloc return ret; } if (retlen != fsdata_len) { - D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %d not %d\n", jeb->offset, retlen, fsdata_len)); + D1(printk(KERN_WARNING "jffs2_write_nand_cleanmarker(): Short write for block at %08x: %zd not %d\n", jeb->offset, retlen, fsdata_len)); return ret; } return 0; @@ -721,7 +746,7 @@ int jffs2_nand_read_failcnt(struct jffs2_sb_info *c, struct jffs2_eraseblock *je } if (retlen < oob_size) { - D1(printk(KERN_WARNING "jffs2_nand_read_failcnt(): Read OOB return short read (%d bytes not %d) for block at %08x\n", retlen, oob_size, jeb->offset)); + 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; } @@ -767,9 +792,8 @@ int jffs2_write_nand_badblock(struct jffs2_sb_info *c, struct jffs2_eraseblock * return ret; } if (retlen != 1) { - D1(printk(KERN_WARNING "jffs2_write_nand_badblock(): Short write for block at %08x: %d not 1\n", jeb->offset, retlen)); + 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; } - diff --git a/fs/jffs2/write.c b/fs/jffs2/write.c index 8a4a8d9890f1..cc50ffbaea3d 100644 --- a/fs/jffs2/write.c +++ b/fs/jffs2/write.c @@ -7,7 +7,7 @@ * * For licensing information, see the file 'LICENCE' in this directory. * - * $Id: write.c,v 1.60 2002/09/09 16:29:08 dwmw2 Exp $ + * $Id: write.c,v 1.65 2003/01/21 18:11:29 dwmw2 Exp $ * */ @@ -47,7 +47,7 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint 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_je32(mode); + ri->mode = cpu_to_jemode(mode); f->highest_version = 1; ri->version = cpu_to_je32(f->highest_version); @@ -55,6 +55,7 @@ int jffs2_do_new_inode(struct jffs2_sb_info *c, struct jffs2_inode_info *f, uint return 0; } +#if CONFIG_JFFS2_FS_DEBUG > 0 static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) { unsigned char buf[16]; @@ -63,7 +64,7 @@ static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) 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 %d\n", ret, retlen)); + D1(printk(KERN_DEBUG "read failed or short in writecheck(). ret %d, retlen %zd\n", ret, retlen)); return; } ret = 0; @@ -79,7 +80,7 @@ static void writecheck(struct jffs2_sb_info *c, uint32_t ofs) 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, @@ -105,10 +106,10 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 vecs[1].iov_base = (unsigned char *)data; vecs[1].iov_len = datalen; - writecheck(c, flash_ofs); + D1(writecheck(c, flash_ofs)); if (je32_to_cpu(ri->totlen) != sizeof(*ri) + datalen) { - printk(KERN_WARNING "jffs2_write_dnode: ri->totlen (0x%08x) != sizeof(*ri) (0x%08x) + datalen (0x%08x)\n", 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) @@ -135,7 +136,7 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 ret = jffs2_flash_writev(c, vecs, cnt, flash_ofs, &retlen); 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) { @@ -162,20 +163,27 @@ struct jffs2_full_dnode *jffs2_write_dnode(struct jffs2_sb_info *c, struct jffs2 return ERR_PTR(ret?ret:-EIO); } /* Mark the space used */ - if (datalen == PAGE_CACHE_SIZE) + /* 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 + } 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, 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))); + 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 (writelen) *writelen = retlen; @@ -194,7 +202,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff 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))); - writecheck(c, flash_ofs); + D1(writecheck(c, flash_ofs)); 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"); @@ -233,7 +241,7 @@ struct jffs2_full_dirent *jffs2_write_dirent(struct jffs2_sb_info *c, struct jff ret = jffs2_flash_writev(c, vecs, 2, flash_ofs, &retlen); 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) { @@ -290,7 +298,7 @@ int jffs2_write_inode_range(struct jffs2_sb_info *c, struct jffs2_inode_info *f, } down(&f->sem); datalen = writelen; - cdatalen = min(alloclen - sizeof(*ri), writelen); + cdatalen = min_t(uint32_t, alloclen - sizeof(*ri), writelen); comprbuf = kmalloc(cdatalen, GFP_KERNEL); if (comprbuf) { @@ -392,7 +400,7 @@ int jffs2_do_create(struct jffs2_sb_info *c, struct jffs2_inode_info *dir_f, str fn = jffs2_write_dnode(c, f, ri, NULL, 0, phys_ofs, &writtenlen); D1(printk(KERN_DEBUG "jffs2_do_create created file with mode 0x%x\n", - je32_to_cpu(ri->mode))); + jemode_to_cpu(ri->mode))); if (IS_ERR(fn)) { D1(printk(KERN_DEBUG "jffs2_write_dnode() failed\n")); diff --git a/include/linux/jffs2.h b/include/linux/jffs2.h index 958cab16d8a7..2e9a2c8e4fdd 100644 --- a/include/linux/jffs2.h +++ b/include/linux/jffs2.h @@ -8,19 +8,23 @@ * For licensing information, see the file 'LICENCE' in the * jffs2 directory. * - * $Id: jffs2.h,v 1.25 2002/08/20 21:37:27 dwmw2 Exp $ + * $Id: jffs2.h,v 1.30 2003/02/15 00:15:22 dwmw2 Exp $ * */ #ifndef __LINUX_JFFS2_H__ #define __LINUX_JFFS2_H__ +/* You must include something which defines the C99 uintXX_t types. + We don't do it from here because this file is used in too many + different environments. */ + #define JFFS2_SUPER_MAGIC 0x72b6 /* Values we may expect to find in the 'magic' field */ #define JFFS2_OLD_MAGIC_BITMASK 0x1984 #define JFFS2_MAGIC_BITMASK 0x1985 -#define KSAMTIB_CIGAM_2SFFJ 0x5981 /* For detecting wrong-endian fs */ +#define KSAMTIB_CIGAM_2SFFJ 0x8519 /* For detecting wrong-endian fs */ #define JFFS2_EMPTY_BITMASK 0xffff #define JFFS2_DIRTY_BITMASK 0x0000 @@ -76,29 +80,42 @@ typedef struct { } __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 @@ -144,7 +161,7 @@ struct jffs2_raw_inode jint32_t hdr_crc; jint32_t ino; /* Inode number. */ jint32_t version; /* Version number. */ - jint32_t mode; /* The file's type or mode. */ + 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) */ @@ -159,7 +176,7 @@ struct jffs2_raw_inode 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[dsize]; + uint8_t data[0]; } __attribute__((packed)); union jffs2_node_union { diff --git a/include/linux/jffs2_fs_sb.h b/include/linux/jffs2_fs_sb.h index 7cfabbd614cb..246ff237325c 100644 --- a/include/linux/jffs2_fs_sb.h +++ b/include/linux/jffs2_fs_sb.h @@ -1,4 +1,4 @@ -/* $Id: jffs2_fs_sb.h,v 1.35 2002/11/12 09:42:18 dwmw2 Exp $ */ +/* $Id: jffs2_fs_sb.h,v 1.37 2003/01/17 16:04:44 dwmw2 Exp $ */ #ifndef _JFFS2_FS_SB #define _JFFS2_FS_SB @@ -8,6 +8,8 @@ #include <linux/workqueue.h> #include <linux/completion.h> #include <asm/semaphore.h> +#include <linux/timer.h> +#include <linux/wait.h> #include <linux/list.h> #define JFFS2_SB_FLAG_RO 1 @@ -73,6 +75,7 @@ struct jffs2_sb_info { against erase completion handler */ wait_queue_head_t erase_wait; /* For waiting for erases to complete */ + wait_queue_head_t inocache_wq; struct jffs2_inode_cache **inocache_list; spinlock_t inocache_lock; diff --git a/include/linux/mtd/blktrans.h b/include/linux/mtd/blktrans.h new file mode 100644 index 000000000000..ab9639b36e25 --- /dev/null +++ b/include/linux/mtd/blktrans.h @@ -0,0 +1,75 @@ +/* + * $Id: blktrans.h,v 1.4 2003/05/21 01:01:32 dwmw2 Exp $ + * + * (C) 2003 David Woodhouse <dwmw2@infradead.org> + * + * Interface to Linux block layer for MTD 'translation layers'. + * + */ + +#ifndef __MTD_TRANS_H__ +#define __MTD_TRANS_H__ + +#include <asm/semaphore.h> + +struct 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); + + /* HDIO_GETGEO and HDIO_GETGEO_BIG are the only non-private + ioctls which are expected to be passed through */ + int (*ioctl)(struct mtd_blktrans_dev *dev, + struct inode * inode, struct file * file, + unsigned int cmd, unsigned long arg); + + /* Called with mtd_table_mutex held; no race with add/remove */ + int (*open)(struct mtd_blktrans_dev *dev, + struct inode *i, struct file *f); + int (*release)(struct mtd_blktrans_dev *dev, + struct inode *i, struct file *f); + + /* 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 --git a/include/linux/mtd/cfi.h b/include/linux/mtd/cfi.h index 0ad3d186597d..c72e6c789b82 100644 --- a/include/linux/mtd/cfi.h +++ b/include/linux/mtd/cfi.h @@ -1,7 +1,7 @@ /* Common Flash Interface structures * See http://support.intel.com/design/flash/technote/index.htm - * $Id: cfi.h,v 1.25 2001/09/04 07:06:21 dwmw2 Exp $ + * $Id: cfi.h,v 1.34 2003/05/16 18:55:44 dwmw2 Exp $ */ #ifndef __MTD_CFI_H__ @@ -18,13 +18,16 @@ * You can optimize the code size and performance by defining only * the geometry(ies) available on your hardware. * CFIDEV_INTERLEAVE_n, where represents the interleave (number of chips to fill the bus width) - * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2 or 4 bytes) + * CFIDEV_BUSWIDTH_n, where n is the bus width in bytes (1, 2, 4 or 8 bytes) * * By default, all (known) geometries are supported. */ #ifndef CONFIG_MTD_CFI_GEOMETRY +/* The default case - support all but 64-bit, which has + a performance penalty */ + #define CFIDEV_INTERLEAVE_1 (1) #define CFIDEV_INTERLEAVE_2 (2) #define CFIDEV_INTERLEAVE_4 (4) @@ -33,8 +36,12 @@ #define CFIDEV_BUSWIDTH_2 (2) #define CFIDEV_BUSWIDTH_4 (4) +typedef __u32 cfi_word; + #else +/* Explicitly configured buswidth/interleave support */ + #ifdef CONFIG_MTD_CFI_I1 #define CFIDEV_INTERLEAVE_1 (1) #endif @@ -44,6 +51,9 @@ #ifdef CONFIG_MTD_CFI_I4 #define CFIDEV_INTERLEAVE_4 (4) #endif +#ifdef CONFIG_MTD_CFI_I8 +#define CFIDEV_INTERLEAVE_8 (8) +#endif #ifdef CONFIG_MTD_CFI_B1 #define CFIDEV_BUSWIDTH_1 (1) @@ -54,6 +64,27 @@ #ifdef CONFIG_MTD_CFI_B4 #define CFIDEV_BUSWIDTH_4 (4) #endif +#ifdef CONFIG_MTD_CFI_B8 +#define CFIDEV_BUSWIDTH_8 (8) +#endif + +/* pick the largest necessary */ +#ifdef CONFIG_MTD_CFI_B8 +typedef __u64 cfi_word; + +/* This only works if asm/io.h is included first */ +#ifndef __raw_readll +#define __raw_readll(addr) (*(volatile __u64 *)(addr)) +#endif +#ifndef __raw_writell +#define __raw_writell(v, addr) (*(volatile __u64 *)(addr) = (v)) +#endif +#define CFI_WORD_64 +#else /* CONFIG_MTD_CFI_B8 */ +/* All others can use 32-bits. It's probably more efficient than + the smaller types anyway */ +typedef __u32 cfi_word; +#endif /* CONFIG_MTD_CFI_B8 */ #endif @@ -61,12 +92,15 @@ * The following macros are used to select the code to execute: * cfi_buswidth_is_*() * cfi_interleave_is_*() - * [where * is either 1, 2 or 4] + * [where * is either 1, 2, 4, or 8] * Those macros should be used with 'if' statements. If only one of few * geometry arrangements are selected, they expand to constants thus allowing * the compiler (most of them being 0) to optimize away all the unneeded code, * while still validating the syntax (which is not possible with embedded * #if ... #endif constructs). + * The exception to this is the 64-bit versions, which need an extension + * to the cfi_word type, and cause compiler warnings about shifts being + * out of range. */ #ifdef CFIDEV_INTERLEAVE_1 @@ -105,6 +139,18 @@ # define cfi_interleave_is_4() (0) #endif +#ifdef CFIDEV_INTERLEAVE_8 +# ifdef CFIDEV_INTERLEAVE +# undef CFIDEV_INTERLEAVE +# define CFIDEV_INTERLEAVE (cfi->interleave) +# else +# define CFIDEV_INTERLEAVE CFIDEV_INTERLEAVE_8 +# endif +# define cfi_interleave_is_8() (CFIDEV_INTERLEAVE == CFIDEV_INTERLEAVE_8) +#else +# define cfi_interleave_is_8() (0) +#endif + #ifndef CFIDEV_INTERLEAVE #error You must define at least one interleave to support! #endif @@ -145,6 +191,18 @@ # define cfi_buswidth_is_4() (0) #endif +#ifdef CFIDEV_BUSWIDTH_8 +# ifdef CFIDEV_BUSWIDTH +# undef CFIDEV_BUSWIDTH +# define CFIDEV_BUSWIDTH (map->buswidth) +# else +# define CFIDEV_BUSWIDTH CFIDEV_BUSWIDTH_8 +# endif +# define cfi_buswidth_is_8() (CFIDEV_BUSWIDTH == CFIDEV_BUSWIDTH_8) +#else +# define cfi_buswidth_is_8() (0) +#endif + #ifndef CFIDEV_BUSWIDTH #error You must define at least one bus width to support! #endif @@ -156,6 +214,7 @@ #define CFI_DEVICETYPE_X8 (8 / 8) #define CFI_DEVICETYPE_X16 (16 / 8) #define CFI_DEVICETYPE_X32 (32 / 8) +#define CFI_DEVICETYPE_X64 (64 / 8) /* NB: We keep these structures in memory in HOST byteorder, except * where individually noted. @@ -206,6 +265,10 @@ struct cfi_pri_intelext { __u16 BlkStatusRegMask; __u8 VccOptimal; __u8 VppOptimal; + __u8 NumProtectionFields; + __u16 ProtRegAddr; + __u8 FactProtRegSize; + __u8 UserProtRegSize; } __attribute__((packed)); struct cfi_pri_query { @@ -229,8 +292,8 @@ struct cfi_bri_query { #define P_ID_RESERVED 65535 -#define CFI_MODE_CFI 0 -#define CFI_MODE_JEDEC 1 +#define CFI_MODE_CFI 1 +#define CFI_MODE_JEDEC 0 struct cfi_private { __u16 cmdset; @@ -264,9 +327,9 @@ static inline __u32 cfi_build_cmd_addr(__u32 cmd_ofs, int interleave, int type) /* * Transforms the CFI command for the given geometry (bus width & interleave. */ -static inline __u32 cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi) +static inline cfi_word cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_private *cfi) { - __u32 val = 0; + cfi_word val = 0; if (cfi_buswidth_is_1()) { /* 1 x8 device */ @@ -291,6 +354,27 @@ static inline __u32 cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_p val = (cmd << 16) | cmd; val = cpu_to_cfi32((val << 8) | val); } +#ifdef CFI_WORD_64 + } else if (cfi_buswidth_is_8()) { + if (cfi_interleave_is_1()) { + /* 1 x64 device in x64 mode */ + val = cpu_to_cfi64(cmd); + } else if (cfi_interleave_is_2()) { + /* 2 x32 device in x32 mode */ + val = cmd; + val = cpu_to_cfi64((val << 32) | val); + } else if (cfi_interleave_is_4()) { + /* 4 (x16, x32 or x64) devices in x16 mode */ + val = (cmd << 16) | cmd; + val = cpu_to_cfi64((val << 32) | val); + } else if (cfi_interleave_is_8()) { + /* 8 (x8, x16 or x32) devices in x8 mode */ + val = (cmd << 8) | cmd; + val = (val << 16) | val; + val = (val << 32) | val; + val = cpu_to_cfi64(val); + } +#endif /* CFI_WORD_64 */ } return val; } @@ -300,14 +384,16 @@ static inline __u32 cfi_build_cmd(u_char cmd, struct map_info *map, struct cfi_p * Read a value according to the bus width. */ -static inline __u32 cfi_read(struct map_info *map, __u32 addr) +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); } else { return 0; } @@ -317,14 +403,16 @@ static inline __u32 cfi_read(struct map_info *map, __u32 addr) * Write a value according to the bus width. */ -static inline void cfi_write(struct map_info *map, __u32 val, __u32 addr) +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); } } @@ -337,9 +425,9 @@ static inline void cfi_write(struct map_info *map, __u32 val, __u32 addr) */ static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base, struct map_info *map, struct cfi_private *cfi, - int type, __u32 *prev_val) + int type, cfi_word *prev_val) { - __u32 val; + cfi_word val; __u32 addr = base + cfi_build_cmd_addr(cmd_addr, CFIDEV_INTERLEAVE, type); val = cfi_build_cmd(cmd, map, cfi); @@ -355,11 +443,13 @@ static inline __u32 cfi_send_gen_cmd(u_char cmd, __u32 cmd_addr, __u32 base, 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)); } else { return 0; } @@ -368,17 +458,17 @@ static inline __u8 cfi_read_query(struct map_info *map, __u32 addr) static inline void cfi_udelay(int us) { #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0) - if (need_resched()) { - unsigned long t = us * HZ / 1000000; - if (t < 1) - t = 1; + unsigned long t = us * HZ / 1000000; + if (t) { set_current_state(TASK_UNINTERRUPTIBLE); schedule_timeout(t); + return; } - else #endif - udelay(us); + udelay(us); + cond_resched(); } + static inline void cfi_spin_lock(spinlock_t *mutex) { spin_lock_bh(mutex); @@ -389,5 +479,4 @@ static inline void cfi_spin_unlock(spinlock_t *mutex) spin_unlock_bh(mutex); } - #endif /* __MTD_CFI_H__ */ diff --git a/include/linux/mtd/cfi_endian.h b/include/linux/mtd/cfi_endian.h index 9f3b041c4092..25724f7d3867 100644 --- a/include/linux/mtd/cfi_endian.h +++ b/include/linux/mtd/cfi_endian.h @@ -1,5 +1,5 @@ /* - * $Id: cfi_endian.h,v 1.10 2001/06/18 11:00:46 abz Exp $ + * $Id: cfi_endian.h,v 1.11 2002/01/30 23:20:48 awozniak Exp $ * */ @@ -30,22 +30,28 @@ #define cfi8_to_cpu(x) (x) #define cpu_to_cfi16(x) cpu_to_le16(x) #define cpu_to_cfi32(x) cpu_to_le32(x) +#define cpu_to_cfi64(x) cpu_to_le64(x) #define cfi16_to_cpu(x) le16_to_cpu(x) #define cfi32_to_cpu(x) le32_to_cpu(x) +#define cfi64_to_cpu(x) le64_to_cpu(x) #elif defined (CFI_BIG_ENDIAN) #define cpu_to_cfi8(x) (x) #define cfi8_to_cpu(x) (x) #define cpu_to_cfi16(x) cpu_to_be16(x) #define cpu_to_cfi32(x) cpu_to_be32(x) +#define cpu_to_cfi64(x) cpu_to_be64(x) #define cfi16_to_cpu(x) be16_to_cpu(x) #define cfi32_to_cpu(x) be32_to_cpu(x) +#define cfi64_to_cpu(x) be64_to_cpu(x) #elif defined (CFI_HOST_ENDIAN) #define cpu_to_cfi8(x) (x) #define cfi8_to_cpu(x) (x) #define cpu_to_cfi16(x) (x) #define cpu_to_cfi32(x) (x) +#define cpu_to_cfi64(x) (x) #define cfi16_to_cpu(x) (x) #define cfi32_to_cpu(x) (x) +#define cfi64_to_cpu(x) (x) #else #error No CFI endianness defined #endif diff --git a/include/linux/mtd/compatmac.h b/include/linux/mtd/compatmac.h index 8b243674cf22..7d1300d9bd51 100644 --- a/include/linux/mtd/compatmac.h +++ b/include/linux/mtd/compatmac.h @@ -1,201 +1,10 @@ -/* - * mtd/include/compatmac.h - * - * $Id: compatmac.h,v 1.4 2000/07/03 10:01:38 dwmw2 Exp $ - * - * Extensions and omissions from the normal 'linux/compatmac.h' - * files. hopefully this will end up empty as the 'real' one - * becomes fully-featured. - */ - - -/* First, include the parts which the kernel is good enough to provide - * to us - */ - #ifndef __LINUX_MTD_COMPATMAC_H__ #define __LINUX_MTD_COMPATMAC_H__ -#include <linux/types.h> /* used later in this header */ -#include <linux/module.h> -#ifndef LINUX_VERSION_CODE -#include <linux/version.h> -#endif - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) -#include <linux/vmalloc.h> -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,0,0) -# error "This kernel is too old: not supported by this file" -#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 */ -#endif - -/* - * "select" changed in 2.1.23. The implementation is twin, but this - * header is new - */ -#if LINUX_VERSION_CODE > KERNEL_VERSION(2,1,22) -# include <linux/poll.h> -#else -# define __USE_OLD_SELECT__ -#endif - -/* 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 - -/* changed the prototype of read/write */ - -#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,1,0) || defined(__alpha__) -# define count_t unsigned long -# define read_write_t long -#else -# define count_t int -# define read_write_t int -#endif - - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,1,31) -# define release_t void -# define release_return(x) return -#else -# define release_t int -# define release_return(x) return (x) -#endif - -#if LINUX_VERSION_CODE < 0x20300 -#define __exit -#endif -#if LINUX_VERSION_CODE < 0x20200 -#define __init -#else -#include <linux/init.h> -#endif - -#if LINUX_VERSION_CODE < 0x20300 -#define init_MUTEX(x) do {*(x) = MUTEX;} while (0) -#define RQFUNC_ARG void -#define blkdev_dequeue_request(req) do {CURRENT = req->next;} while (0) -#else -#define RQFUNC_ARG request_queue_t *q -#endif - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,2,0) -#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) -#endif - - - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) - -#define DECLARE_WAIT_QUEUE_HEAD(x) struct wait_queue *x = NULL -#define init_waitqueue_head init_waitqueue - -static inline int try_inc_mod_count(struct module *mod) -{ - if (mod) - __MOD_INC_USE_COUNT(mod); - return 1; -} -#endif - - -/* Yes, I'm aware that it's a fairly ugly hack. - Until the __constant_* macros appear in Linus' own kernels, this is - the way it has to be done. - DW 19/1/00 - */ - -#include <asm/byteorder.h> - -#ifndef __constant_cpu_to_le16 - -#ifdef __BIG_ENDIAN -#define __constant_cpu_to_le64(x) ___swab64((x)) -#define __constant_le64_to_cpu(x) ___swab64((x)) -#define __constant_cpu_to_le32(x) ___swab32((x)) -#define __constant_le32_to_cpu(x) ___swab32((x)) -#define __constant_cpu_to_le16(x) ___swab16((x)) -#define __constant_le16_to_cpu(x) ___swab16((x)) -#define __constant_cpu_to_be64(x) ((__u64)(x)) -#define __constant_be64_to_cpu(x) ((__u64)(x)) -#define __constant_cpu_to_be32(x) ((__u32)(x)) -#define __constant_be32_to_cpu(x) ((__u32)(x)) -#define __constant_cpu_to_be16(x) ((__u16)(x)) -#define __constant_be16_to_cpu(x) ((__u16)(x)) -#else -#ifdef __LITTLE_ENDIAN -#define __constant_cpu_to_le64(x) ((__u64)(x)) -#define __constant_le64_to_cpu(x) ((__u64)(x)) -#define __constant_cpu_to_le32(x) ((__u32)(x)) -#define __constant_le32_to_cpu(x) ((__u32)(x)) -#define __constant_cpu_to_le16(x) ((__u16)(x)) -#define __constant_le16_to_cpu(x) ((__u16)(x)) -#define __constant_cpu_to_be64(x) ___swab64((x)) -#define __constant_be64_to_cpu(x) ___swab64((x)) -#define __constant_cpu_to_be32(x) ___swab32((x)) -#define __constant_be32_to_cpu(x) ___swab32((x)) -#define __constant_cpu_to_be16(x) ___swab16((x)) -#define __constant_be16_to_cpu(x) ___swab16((x)) -#else -#error No (recognised) endianness defined (unless it,s PDP) -#endif /* __LITTLE_ENDIAN */ -#endif /* __BIG_ENDIAN */ - -#endif /* ifndef __constant_cpu_to_le16 */ - -#if LINUX_VERSION_CODE < KERNEL_VERSION(2,3,0) - #define mod_init_t int __init - #define mod_exit_t void -#else - #define mod_init_t static int __init - #define mod_exit_t static void __exit -#endif - -#ifndef THIS_MODULE -#ifdef MODULE -#define THIS_MODULE (&__this_module) -#else -#define THIS_MODULE (NULL) -#endif -#endif - -#if LINUX_VERSION_CODE < 0x20300 -#include <linux/interrupt.h> -#define spin_lock_bh(lock) do {start_bh_atomic();spin_lock(lock);} while(0) -#define spin_unlock_bh(lock) do {spin_unlock(lock);end_bh_atomic();} while(0) -#else -#include <linux/interrupt.h> -#include <linux/spinlock.h> -#endif +/* Nothing to see here. We write 2.5-compatible code and this + file makes it all OK in older kernels, but it's empty in _current_ + kernels. Include guard just to make GCC ignore it in future inclusions + anyway... */ #endif /* __LINUX_MTD_COMPATMAC_H__ */ - - diff --git a/include/linux/mtd/doc2000.h b/include/linux/mtd/doc2000.h index 6161f2c0ed3b..c224985990b9 100644 --- a/include/linux/mtd/doc2000.h +++ b/include/linux/mtd/doc2000.h @@ -2,7 +2,7 @@ /* Linux driver for Disk-On-Chip 2000 */ /* (c) 1999 Machine Vision Holdings, Inc. */ /* Author: David Woodhouse <dwmw2@mvhi.com> */ -/* $Id: doc2000.h,v 1.15 2001/09/19 00:22:15 dwmw2 Exp $ */ +/* $Id: doc2000.h,v 1.16 2003/05/23 11:29:33 dwmw2 Exp $ */ #ifndef __MTD_DOC2000_H__ #define __MTD_DOC2000_H__ @@ -38,6 +38,35 @@ #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 0x1002 +#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. @@ -71,13 +100,20 @@ #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_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 +129,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) @@ -110,6 +150,9 @@ struct Nand { #define MAX_FLOORS_MIL 4 #define MAX_CHIPS_MIL 1 +#define MAX_FLOORS_MPLUS 1 +#define MAX_CHIPS_MPLUS 1 + #define ADDR_COLUMN 1 #define ADDR_PAGE 2 #define ADDR_COLUMN_PAGE 3 @@ -126,6 +169,7 @@ struct DiskOnChip { int chipshift; char page256; char pageadrlen; + char interleave; /* Internal interleaving - Millennium Plus style */ unsigned long erasesize; int curfloor; diff --git a/include/linux/mtd/flashchip.h b/include/linux/mtd/flashchip.h index 4cdccad20abe..7e042bf5fd0b 100644 --- a/include/linux/mtd/flashchip.h +++ b/include/linux/mtd/flashchip.h @@ -6,7 +6,7 @@ * * (C) 2000 Red Hat. GPLd. * - * $Id: flashchip.h,v 1.7 2001/01/18 03:52:36 nico Exp $ + * $Id: flashchip.h,v 1.9 2003/04/30 11:15:22 dwmw2 Exp $ * */ @@ -36,6 +36,7 @@ typedef enum { FL_UNLOADING, FL_LOCKING, FL_UNLOCKING, + FL_POINT, FL_UNKNOWN } flstate_t; @@ -54,8 +55,13 @@ struct flchip { a given offset, and we'll want to add the per-chip length field back in. */ + int ref_point_counter; flstate_t state; flstate_t oldstate; + + int write_suspended:1; + int erase_suspended:1; + 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 --git a/include/linux/mtd/inftl.h b/include/linux/mtd/inftl.h new file mode 100644 index 000000000000..5cc052da9597 --- /dev/null +++ b/include/linux/mtd/inftl.h @@ -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.3 2003/05/23 11:35:34 dwmw2 Exp $ + */ + +#ifndef __MTD_INFTL_H__ +#define __MTD_INFTL_H__ + +#include <linux/mtd/blktrans.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/nftl.h> + +#define OSAK_VERSION 0x5120 +#define PERCENTUSED 98 + +#define SECTORSIZE 512 + +#ifndef INFTL_MAJOR +#define INFTL_MAJOR 93 /* FIXME */ +#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 --git a/include/linux/mtd/jedec.h b/include/linux/mtd/jedec.h index 75271b8a70ba..2ba0f700ddbc 100644 --- a/include/linux/mtd/jedec.h +++ b/include/linux/mtd/jedec.h @@ -7,14 +7,13 @@ * * See the AMD flash databook for information on how to operate the interface. * - * $Id: jedec.h,v 1.2 2001/11/06 14:37:36 dwmw2 Exp $ + * $Id: jedec.h,v 1.3 2003/05/21 11:51:01 dwmw2 Exp $ */ #ifndef __LINUX_MTD_JEDEC_H__ #define __LINUX_MTD_JEDEC_H__ #include <linux/types.h> -#include <linux/mtd/map.h> #define MAX_JEDEC_CHIPS 16 diff --git a/include/linux/mtd/map.h b/include/linux/mtd/map.h index ad8dfcbda17d..0c933f9bf1fe 100644 --- a/include/linux/mtd/map.h +++ b/include/linux/mtd/map.h @@ -1,14 +1,15 @@ /* Overhauled routines for dealing with different mmap regions of flash */ -/* $Id: map.h,v 1.25 2001/09/09 15:04:17 dwmw2 Exp $ */ +/* $Id: map.h,v 1.34 2003/05/28 12:42:22 dwmw2 Exp $ */ #ifndef __LINUX_MTD_MAP_H__ #define __LINUX_MTD_MAP_H__ #include <linux/config.h> #include <linux/types.h> -#include <linux/mtd/mtd.h> -#include <linux/slab.h> +#include <linux/list.h> +#include <asm/system.h> +#include <asm/io.h> /* The map stuff is very simple. You fill in your struct map_info with a handful of routines for accessing the device, making sure they handle @@ -29,34 +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); + +#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 (*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); + /* 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 *); @@ -68,24 +79,94 @@ struct mtd_chip_driver { void register_mtd_chip_driver(struct mtd_chip_driver *); void unregister_mtd_chip_driver(struct mtd_chip_driver *); -struct mtd_info *do_map_probe(char *name, struct map_info *map); +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) -/* - * 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) +extern void simple_map_init(struct map_info *); +#define map_is_linear(map) (map->phys != NO_XIP) + +#else +static inline u8 map_read8(struct map_info *map, unsigned long ofs) { - struct map_info *map = mtd->priv; + return __raw_readb(map->virt + ofs); +} - if (map->fldrv->destroy) - map->fldrv->destroy(mtd); - module_put(map->fldrv->module); - kfree(mtd); +static inline u16 map_read16(struct map_info *map, unsigned long ofs) +{ + return __raw_readw(map->virt + ofs); } -#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 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 +} + +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 --git a/include/linux/mtd/mtd.h b/include/linux/mtd/mtd.h index 6a57af9f46a3..9ac94aaabd3f 100644 --- a/include/linux/mtd/mtd.h +++ b/include/linux/mtd/mtd.h @@ -1,5 +1,5 @@ -/* $Id: mtd.h,v 1.33 2001/06/09 00:08:59 dwmw2 Exp $ */ +/* $Id: mtd.h,v 1.45 2003/05/20 21:56:40 dwmw2 Exp $ */ #ifndef __MTD_MTD_H__ #define __MTD_MTD_H__ @@ -9,7 +9,6 @@ #include <linux/config.h> #include <linux/version.h> #include <linux/types.h> -#include <linux/mtd/compatmac.h> #include <linux/module.h> #include <linux/uio.h> @@ -26,7 +25,6 @@ struct mtd_oob_buf { unsigned char *ptr; }; - #define MTD_CHAR_MAJOR 90 #define MTD_BLOCK_MAJOR 31 #define MAX_MTD_DEVICES 16 @@ -93,16 +91,23 @@ struct region_info_user { #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 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__ */ @@ -147,11 +152,15 @@ struct mtd_info { u_int32_t oobsize; // Amount of OOB data per block (e.g. 16) 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. */ @@ -161,32 +170,47 @@ struct mtd_info { /* 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 */ int (*point) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); /* We probably shouldn't allow XIP if the unpoint isn't a NULL */ - void (*unpoint) (struct mtd_info *mtd, u_char * addr); + void (*unpoint) (struct mtd_info *mtd, u_char * addr, loff_t from, size_t len); 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 (*write_ecc) (struct mtd_info *mtd, loff_t to, size_t len, size_t *retlen, const u_char *buf, u_char *eccbuf); + 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); + /* + * Methods to access the protection register area, present in some + * flash devices. The user data is one time programmable but the + * factory data is read only. + */ + int (*read_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + + int (*read_fact_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + + /* This function is not yet implemented */ + int (*write_user_prot_reg) (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char *buf); + /* iovec-based read/write methods. We need these especially for NAND flash, with its limited number of write cycles per erase. NB: The 'count' parameter is the number of _vectors_, each of which contains an (ofs, len) tuple. */ 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, 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, struct nand_oobinfo *oobsel); /* Sync */ void (*sync) (struct mtd_info *mtd); @@ -200,6 +224,9 @@ struct mtd_info { void (*resume) (struct mtd_info *mtd); void *priv; + + struct module *owner; + int usecount; }; @@ -208,35 +235,27 @@ struct mtd_info { extern int add_mtd_device(struct mtd_info *mtd); extern int del_mtd_device (struct mtd_info *mtd); -extern struct mtd_info *__get_mtd_device(struct mtd_info *mtd, int num); +extern 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; - - ret = __get_mtd_device(mtd, num); - if (ret && !try_module_get(ret->module)) - return NULL; - return ret; -} +extern void put_mtd_device(struct mtd_info *mtd); -static inline void put_mtd_device(struct mtd_info *mtd) -{ - module_put(mtd->module); -} struct mtd_notifier { void (*add)(struct mtd_info *mtd); void (*remove)(struct mtd_info *mtd); - struct mtd_notifier *next; + struct list_head list; }; extern void register_mtd_user (struct mtd_notifier *new); extern int unregister_mtd_user (struct mtd_notifier *old); +int default_mtd_writev(struct mtd_info *mtd, const struct iovec *vecs, + unsigned long count, loff_t to, size_t *retlen); + +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) @@ -249,7 +268,6 @@ extern int unregister_mtd_user (struct mtd_notifier *old); #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 @@ -266,7 +284,8 @@ extern int unregister_mtd_user (struct mtd_notifier *old); 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 --git a/include/linux/mtd/nand.h b/include/linux/mtd/nand.h index 8c678ab9761a..88d48a26bf75 100644 --- a/include/linux/mtd/nand.h +++ b/include/linux/mtd/nand.h @@ -2,9 +2,10 @@ * linux/include/linux/mtd/nand.h * * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com> - * Steven J. Hill <sjhill@cotw.com> + * Steven J. Hill <sjhill@realitydiluted.com> + * Thomas Gleixner <tglx@linutronix.de> * - * $Id: nand.h,v 1.8 2000/10/30 17:16:17 sjhill Exp $ + * $Id: nand.h,v 1.25 2003/05/21 15:15: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 @@ -23,19 +24,51 @@ * bat later if I did something naughty. * 10-11-2000 SJH Added private NAND flash structure for driver * 10-24-2000 SJH Added prototype for 'nand_scan' function + * 10-29-2001 TG changed nand_chip structure to support + * hardwarespecific function for accessing control lines + * 02-21-2002 TG added support for different read/write adress and + * ready/busy line access function + * 02-26-2002 TG added chip_delay to nand_chip structure to optimize + * command delay times for different chips + * 04-28-2002 TG OOB config defines moved from nand.c to avoid duplicate + * defines in jffs2/wbuf.c + * 08-07-2002 TG forced bad block location to byte 5 of OOB, even if + * CONFIG_MTD_NAND_ECC_JFFS2 is not set + * 08-10-2002 TG extensions to nand_chip structure to support HW-ECC + * + * 08-29-2002 tglx nand_chip structure: data_poi for selecting + * internal / fs-driver buffer + * support for 6byte/512byte hardware ECC + * read_ecc, write_ecc extended for different oob-layout + * oob layout selections: NAND_NONE_OOB, NAND_JFFS2_OOB, + * NAND_YAFFS_OOB + * 11-25-2002 tglx Added Manufacturer code FUJITSU, NATIONAL + * Split manufacturer and device ID structures */ #ifndef __LINUX_MTD_NAND_H #define __LINUX_MTD_NAND_H #include <linux/config.h> -#include <linux/sched.h> +#include <linux/wait.h> +#include <linux/spinlock.h> +struct mtd_info; /* * Searches for a NAND device */ extern int nand_scan (struct mtd_info *mtd); /* + * Constants for hardware specific CLE/ALE/NCE function +*/ +#define NAND_CTL_SETNCE 1 +#define NAND_CTL_CLRNCE 2 +#define NAND_CTL_SETCLE 3 +#define NAND_CTL_CLRCLE 4 +#define NAND_CTL_SETALE 5 +#define NAND_CTL_CLRALE 6 + +/* * Standard NAND flash commands */ #define NAND_CMD_READ0 0 @@ -49,6 +82,29 @@ extern int nand_scan (struct mtd_info *mtd); #define NAND_CMD_ERASE2 0xd0 #define NAND_CMD_RESET 0xff +/* + * Constants for ECC_MODES + * + * NONE: No ECC + * SOFT: Software ECC 3 byte ECC per 256 Byte data + * HW3_256: Hardware ECC 3 byte ECC per 256 Byte data + * HW3_512: Hardware ECC 3 byte ECC per 512 Byte data + * + * +*/ +#define NAND_ECC_NONE 0 +#define NAND_ECC_SOFT 1 +#define NAND_ECC_HW3_256 2 +#define NAND_ECC_HW3_512 3 +#define NAND_ECC_HW6_512 4 +#define NAND_ECC_DISKONCHIP 5 + +/* + * Constants for Hardware ECC +*/ +#define NAND_ECC_READ 0 +#define NAND_ECC_WRITE 1 + /* * Enumeration for NAND flash chip state */ @@ -60,20 +116,33 @@ typedef enum { FL_SYNCING } nand_state_t; + /* * NAND Private Flash Chip Data * * Structure overview: * - * IO_ADDR - address to access the 8 I/O lines to the flash device + * IO_ADDR_R - address to read the 8 I/O lines of the flash device + * + * IO_ADDR_W - address to write the 8 I/O lines of the flash device * - * CTRL_ADDR - address where ALE, CLE and CE control bits are accessed + * hwcontrol - hardwarespecific function for accesing control-lines * - * CLE - location in control word for Command Latch Enable bit + * dev_ready - hardwarespecific function for accesing device ready/busy line * - * ALE - location in control word for Address Latch Enable bit + * waitfunc - hardwarespecific function for wait on ready * - * NCE - location in control word for nChip Enable bit + * calculate_ecc - function for ecc calculation or readback from ecc hardware + * + * correct_data - function for ecc correction, matching to ecc generator (sw/hw) + * + * enable_hwecc - function to enable (reset) hardware ecc generator + * + * eccmod - mode of ecc: see constants + * + * eccsize - databytes used per ecc-calculation + * + * chip_delay - chip dependent delay for transfering data from array to read regs (tR) * * chip_lock - spinlock used to protect access to this structure * @@ -85,27 +154,30 @@ typedef enum { * * data_buf - data buffer passed to/from MTD user modules * - * ecc_code_buf - used only for holding calculated or read ECCs for - * a page read or written when ECC is in use + * data_cache - data cache for redundant page access and shadow for + * ECC failure * - * reserved - padding to make structure fall on word boundary if - * when ECC is in use + * cache_page - number of last valid page in page_cache */ struct nand_chip { - unsigned long IO_ADDR; - unsigned long CTRL_ADDR; - unsigned int CLE; - unsigned int ALE; - unsigned int NCE; - spinlock_t chip_lock; + unsigned long IO_ADDR_R; + unsigned long IO_ADDR_W; + void (*hwcontrol)(int cmd); + int (*dev_ready)(void); + 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); + int eccmode; + int eccsize; + int chip_delay; + spinlock_t chip_lock; wait_queue_head_t wq; - nand_state_t state; - int page_shift; - u_char *data_buf; -#ifdef CONFIG_MTD_NAND_ECC - u_char ecc_code_buf[6]; - u_char reserved[2]; -#endif + nand_state_t state; + int page_shift; + u_char *data_buf; + u_char *data_poi; }; /* @@ -113,17 +185,17 @@ struct nand_chip { */ #define NAND_MFR_TOSHIBA 0x98 #define NAND_MFR_SAMSUNG 0xec +#define NAND_MFR_FUJITSU 0x04 +#define NAND_MFR_NATIONAL 0x8f /* * NAND Flash Device ID Structure * * Structure overview: * - * name - Complete name of device - * - * manufacture_id - manufacturer ID code of device. + * name - Identify the device type * - * model_id - model ID code of device. + * id - device ID code * * chipshift - total number of address bits for the device which * is used to calculate address offsets and the total @@ -143,12 +215,30 @@ struct nand_chip { */ struct nand_flash_dev { char * name; - int manufacture_id; - int model_id; + int id; int chipshift; - char page256; - char pageadrlen; unsigned long erasesize; + char page256; }; +/* + * NAND Flash Manufacturer ID Structure + * + * name - Manufacturer name + * + * id - manufacturer ID code of device. +*/ +struct nand_manufacturers { + int id; + char * name; +}; + +extern struct nand_flash_dev nand_flash_ids[]; +extern struct nand_manufacturers nand_manuf_ids[]; + +/* +* Constants for oob configuration +*/ +#define NAND_BADBLOCK_POS 5 + #endif /* __LINUX_MTD_NAND_H */ diff --git a/include/linux/mtd/nand_ecc.h b/include/linux/mtd/nand_ecc.h index f3f1d7e0f6aa..c3b493c99b05 100644 --- a/include/linux/mtd/nand_ecc.h +++ b/include/linux/mtd/nand_ecc.h @@ -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.2 2003/02/20 13:34:20 sjhill 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 diff --git a/include/linux/mtd/nand_ids.h b/include/linux/mtd/nand_ids.h deleted file mode 100644 index 0918b8c1e92d..000000000000 --- a/include/linux/mtd/nand_ids.h +++ /dev/null @@ -1,52 +0,0 @@ -/* - * linux/include/linux/mtd/nand_ids.h - * - * Copyright (c) 2000 David Woodhouse <dwmw2@mvhi.com> - * Steven J. Hill <sjhill@cotw.com> - * - * $Id: nand_ids.h,v 1.1 2000/10/13 16:16:26 mdeans 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 - * published by the Free Software Foundation. - * - * Info: - * Contains standard defines and IDs for NAND flash devices - * - * Changelog: - * 01-31-2000 DMW Created - * 09-18-2000 SJH Moved structure out of the Disk-On-Chip drivers - * so it can be used by other NAND flash device - * drivers. I also changed the copyright since none - * of the original contents of this file are specific - * to DoC devices. David can whack me with a baseball - * bat later if I did something naughty. - * 10-11-2000 SJH Added private NAND flash structure for driver - * 2000-10-13 BE Moved out of 'nand.h' - avoids duplication. - */ - -#ifndef __LINUX_MTD_NAND_IDS_H -#define __LINUX_MTD_NAND_IDS_H - -static struct nand_flash_dev nand_flash_ids[] = { - {"Toshiba TC5816BDC", NAND_MFR_TOSHIBA, 0x64, 21, 1, 2, 0x1000}, - {"Toshiba TC5832DC", NAND_MFR_TOSHIBA, 0x6b, 22, 0, 2, 0x2000}, - {"Toshiba TH58V128DC", NAND_MFR_TOSHIBA, 0x73, 24, 0, 2, 0x4000}, - {"Toshiba TC58256FT/DC", NAND_MFR_TOSHIBA, 0x75, 25, 0, 2, 0x4000}, - {"Toshiba TH58512FT", NAND_MFR_TOSHIBA, 0x76, 26, 0, 3, 0x4000}, - {"Toshiba TC58V32DC", NAND_MFR_TOSHIBA, 0xe5, 22, 0, 2, 0x2000}, - {"Toshiba TC58V64AFT/DC", NAND_MFR_TOSHIBA, 0xe6, 23, 0, 2, 0x2000}, - {"Toshiba TC58V16BDC", NAND_MFR_TOSHIBA, 0xea, 21, 1, 2, 0x1000}, - {"Samsung KM29N16000", NAND_MFR_SAMSUNG, 0x64, 21, 1, 2, 0x1000}, - {"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0x6b, 22, 0, 2, 0x2000}, - {"Samsung KM29U128T", NAND_MFR_SAMSUNG, 0x73, 24, 0, 2, 0x4000}, - {"Samsung KM29U256T", NAND_MFR_SAMSUNG, 0x75, 25, 0, 2, 0x4000}, - {"Samsung unknown 64Mb", NAND_MFR_SAMSUNG, 0x76, 26, 0, 3, 0x4000}, - {"Samsung KM29W32000", NAND_MFR_SAMSUNG, 0xe3, 22, 0, 2, 0x2000}, - {"Samsung unknown 4Mb", NAND_MFR_SAMSUNG, 0xe5, 22, 0, 2, 0x2000}, - {"Samsung KM29U64000", NAND_MFR_SAMSUNG, 0xe6, 23, 0, 2, 0x2000}, - {"Samsung KM29W16000", NAND_MFR_SAMSUNG, 0xea, 21, 1, 2, 0x1000}, - {NULL,} -}; - -#endif /* __LINUX_MTD_NAND_IDS_H */ diff --git a/include/linux/mtd/nftl.h b/include/linux/mtd/nftl.h index 55778a673b5d..fd57ffddd69a 100644 --- a/include/linux/mtd/nftl.h +++ b/include/linux/mtd/nftl.h @@ -1,15 +1,14 @@ - -/* Defines for NAND Flash Translation Layer */ -/* (c) 1999 Machine Vision Holdings, Inc. */ -/* Author: David Woodhouse <dwmw2@mvhi.com> */ -/* $Id: nftl.h,v 1.10 2000/12/29 00:25:38 dwmw2 Exp $ */ +/* + * $Id: nftl.h,v 1.13 2003/05/23 11:25:02 dwmw2 Exp $ + * + * (C) 1999-2003 David Woodhouse <dwmw2@infradead.org> + */ #ifndef __MTD_NFTL_H__ #define __MTD_NFTL_H__ -#ifndef __BOOT__ #include <linux/mtd/mtd.h> -#endif +#include <linux/mtd/blktrans.h> /* Block Control Information */ @@ -84,8 +83,7 @@ struct NFTLMediaHeader { #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,14 +95,12 @@ struct NFTLrecord { __u16 lastEUN; /* should be suppressed */ __u16 numfreeEUNs; __u16 LastFreeEUN; /* To speed up finding a free EUN */ - __u32 long nr_sects; int head,sect,cyl; __u16 *EUNtable; /* [numvunits]: First EUN for each virtual unit */ __u16 *ReplUnitTable; /* [numEUNs]: ReplUnitNumber for each */ unsigned int nb_blocks; /* number of physical blocks */ unsigned int nb_boot_blocks; /* number of blocks used by the bios */ struct erase_info instr; - struct gendisk *disk; }; int NFTL_mount(struct NFTLrecord *s); @@ -115,7 +111,7 @@ int NFTL_formatblock(struct NFTLrecord *s, int block); #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 --git a/include/linux/mtd/partitions.h b/include/linux/mtd/partitions.h index 1d3351cc1eca..5c5f8770b86d 100644 --- a/include/linux/mtd/partitions.h +++ b/include/linux/mtd/partitions.h @@ -5,7 +5,7 @@ * * This code is GPL * - * $Id: partitions.h,v 1.6 2001/03/17 17:10:21 dwmw2 Exp $ + * $Id: partitions.h,v 1.14 2003/05/20 21:56:29 dwmw2 Exp $ */ #ifndef MTD_PARTITIONS_H @@ -26,23 +26,26 @@ * will extend to the end of the master MTD device. * offset: absolute starting position within the master MTD device; if * defined as MTDPART_OFS_APPEND, the partition will start where the - * previous one ended. + * previous one ended; if MTDPART_OFS_NXTBLK, at the next erase block. * mask_flags: contains flags that have to be masked (removed) from the * master MTD flag set for the corresponding MTD partition. * For example, to force a read-only partition, simply adding * MTD_WRITEABLE to the mask_flags will do the trick. * * Note: writeable partitions require their size and offset be - * erasesize aligned. + * erasesize aligned (e.g. use MTDPART_OFS_NEXTBLK). */ struct mtd_partition { - char *name; /* identifier string */ - u_int32_t size; /* partition size */ + char *name; /* identifier string */ + 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 */ + 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 */ }; +#define MTDPART_OFS_NXTBLK (-2) #define MTDPART_OFS_APPEND (-1) #define MTDPART_SIZ_FULL (0) @@ -50,5 +53,24 @@ struct mtd_partition { int add_mtd_partitions(struct mtd_info *, 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 --git a/include/linux/mtd/pmc551.h b/include/linux/mtd/pmc551.h index 640d3ebbc2ce..113e3087f68a 100644 --- a/include/linux/mtd/pmc551.h +++ b/include/linux/mtd/pmc551.h @@ -1,5 +1,5 @@ /* - * $Id: pmc551.h,v 1.4 2001/06/12 16:19:38 major Exp $ + * $Id: pmc551.h,v 1.5 2003/01/24 16:49:53 dwmw2 Exp $ * * PMC551 PCI Mezzanine Ram Device * @@ -17,7 +17,7 @@ #include <linux/mtd/mtd.h> -#define PMC551_VERSION "$Id: pmc551.h,v 1.4 2001/06/12 16:19:38 major Exp $\n"\ +#define PMC551_VERSION "$Id: pmc551.h,v 1.5 2003/01/24 16:49:53 dwmw2 Exp $\n"\ "Ramix PMC551 PCI Mezzanine Ram Driver. (C) 1999,2000 Nortel Networks.\n" /* @@ -36,7 +36,7 @@ struct mypriv { * Function Prototypes */ static int pmc551_erase(struct mtd_info *, struct erase_info *); -static void pmc551_unpoint(struct mtd_info *, u_char *); +static void pmc551_unpoint(struct mtd_info *, u_char *, loff_t, size_t); static int pmc551_point (struct mtd_info *mtd, loff_t from, size_t len, size_t *retlen, u_char **mtdbuf); static int pmc551_read(struct mtd_info *, loff_t, size_t, size_t *, u_char *); static int pmc551_write(struct mtd_info *, loff_t, size_t, size_t *, const u_char *); |
