diff options
| author | David S. Miller <davem@nuts.ninka.net> | 2002-10-15 07:41:35 -0700 |
|---|---|---|
| committer | David S. Miller <davem@nuts.ninka.net> | 2002-10-15 07:41:35 -0700 |
| commit | 8fbfe7cd5594010a23cb4e81786d1fb8015ffdee (patch) | |
| tree | b5be190f22984395209823ec3cac1c76fc93f67f /drivers/mtd | |
| parent | e22f7f5fd43205bfd20ea3a7bb4e689cb3f3d278 (diff) | |
| parent | 5a7728c6d3eb83df9d120944cca4cf476dd326a1 (diff) | |
Merge nuts.ninka.net:/home/davem/src/BK/network-2.5
into nuts.ninka.net:/home/davem/src/BK/net-2.5
Diffstat (limited to 'drivers/mtd')
| -rw-r--r-- | drivers/mtd/Config.help | 60 | ||||
| -rw-r--r-- | drivers/mtd/Config.in | 5 | ||||
| -rw-r--r-- | drivers/mtd/Makefile | 11 | ||||
| -rw-r--r-- | drivers/mtd/bootldr.c | 214 | ||||
| -rw-r--r-- | drivers/mtd/cmdline.c | 343 | ||||
| -rw-r--r-- | drivers/mtd/ftl.c | 3 | ||||
| -rw-r--r-- | drivers/mtd/maps/Config.help | 109 | ||||
| -rw-r--r-- | drivers/mtd/maps/Config.in | 12 | ||||
| -rw-r--r-- | drivers/mtd/maps/Makefile | 20 | ||||
| -rw-r--r-- | drivers/mtd/maps/autcpu12-nvram.c | 179 | ||||
| -rw-r--r-- | drivers/mtd/maps/ceiva.c | 408 | ||||
| -rw-r--r-- | drivers/mtd/maps/dc21285.c | 25 | ||||
| -rw-r--r-- | drivers/mtd/maps/edb7312.c | 202 | ||||
| -rw-r--r-- | drivers/mtd/maps/epxa10db-flash.c | 233 | ||||
| -rw-r--r-- | drivers/mtd/maps/fortunet.c | 309 | ||||
| -rw-r--r-- | drivers/mtd/maps/impa7.c | 234 | ||||
| -rw-r--r-- | drivers/mtd/maps/iq80310.c | 5 | ||||
| -rw-r--r-- | drivers/mtd/maps/pci.c | 385 | ||||
| -rw-r--r-- | drivers/mtd/maps/pcmciamtd.c | 893 | ||||
| -rw-r--r-- | drivers/mtd/maps/sa1100-flash.c | 1554 | ||||
| -rw-r--r-- | drivers/mtd/mtdblock.c | 5 | ||||
| -rw-r--r-- | drivers/mtd/mtdblock_ro.c | 4 | ||||
| -rw-r--r-- | drivers/mtd/mtdconcat.c | 675 | ||||
| -rw-r--r-- | drivers/mtd/nftlcore.c | 4 |
24 files changed, 5111 insertions, 781 deletions
diff --git a/drivers/mtd/Config.help b/drivers/mtd/Config.help index 822dc7424d34..83e9b5c7d44b 100644 --- a/drivers/mtd/Config.help +++ b/drivers/mtd/Config.help @@ -21,36 +21,62 @@ CONFIG_MTD_PARTITIONS devices. Partitioning on NFTL 'devices' is a different - that's the 'normal' form of partitioning used on a block device. +CONFIG_MTD_CONCAT + Support for concatenating several MTD devices into a single + (virtual) one. This allows you to have -for example- a JFFS(2) + file system spanning multiple physical flash chips. If unsure, + say 'Y'. + CONFIG_MTD_REDBOOT_PARTS RedBoot is a ROM monitor and bootloader which deals with multiple - 'images' in flash devices by putting a table in the last erase block - of the device, similar to a partition table, which gives the - offsets, lengths and names of all the images stored in the flash. + 'images' in flash devices by putting a table in the last erase + block of the device, similar to a partition table, which gives + the offsets, lengths and names of all the images stored in the + flash. If you need code which can detect and parse this table, and register MTD 'partitions' corresponding to each image in the table, enable - this option. + this option. You will still need the parsing functions to be called by the driver - for your particular device. It won't happen automatically. The - SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for + for your particular device. It won't happen automatically. The + SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for example. -CONFIG_MTD_BOOTLDR_PARTS - The Compaq bootldr deals with multiple 'images' in flash devices - by putting a table in one of the first erase blocks of the device, - similar to a partition table, which gives the offsets, lengths and - names of all the images stored in the flash. - - If you need code which can detect and parse this table, and register - MTD 'partitions' corresponding to each image in the table, enable - this option. - +CONFIG_MTD_CMDLINE_PARTS + Allow generic configuration of the MTD paritition tables via the kernel + command line. Multiple flash resources are supported for hardware where + different kinds of flash memory are available. + You will still need the parsing functions to be called by the driver for your particular device. It won't happen automatically. The SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for example. + The format for the command line is as follows: + + mtdparts=<mtddef>[;<mtddef] + <mtddef> := <mtd-id>:<partdef>[,<partdef>] + <partdef> := <size>[@offset][<name>][ro] + <mtd-id> := unique id used in mapping driver/device + <size> := standard linux memsize OR "-" to denote all + remaining space + <name> := (NAME) + + Due to the way Linux handles the command line, no spaces are + allowed in the partition definition, including mtd id's and partition + names. + + Examples: + + 1 flash resource (mtd-id "sa1100"), with 1 single writable partition: + mtdparts=sa1100:- + + Same flash, but 2 named partitions, the first one being read-only: + mtdparts=sa1100:256k(ARMboot)ro,-(root) + + If unsure, say 'N'. + CONFIG_MTD_AFS_PARTS The ARM Firmware Suite allows the user to divide flash devices into multiple 'images'. Each such image has a header containing its name @@ -61,7 +87,7 @@ CONFIG_MTD_AFS_PARTS enable this option. You will still need the parsing functions to be called by the driver - for your particular device. It won't happen automatically. The + for your particular device. It won't happen automatically. The 'armflash' map driver (CONFIG_MTD_ARMFLASH) does this, for example. CONFIG_MTD_DEBUG_VERBOSE diff --git a/drivers/mtd/Config.in b/drivers/mtd/Config.in index 797f79667844..7e3d3ffd2983 100644 --- a/drivers/mtd/Config.in +++ b/drivers/mtd/Config.in @@ -1,5 +1,5 @@ -# $Id: Config.in,v 1.71 2001/10/03 11:38:38 dwmw2 Exp $ +# $Id: Config.in,v 1.74 2002/04/23 13:52:14 mag Exp $ mainmenu_option next_comment comment 'Memory Technology Devices (MTD)' @@ -12,9 +12,10 @@ if [ "$CONFIG_MTD" = "y" -o "$CONFIG_MTD" = "m" ]; then int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0 fi dep_tristate ' MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD + dep_tristate ' MTD concatenating support' CONFIG_MTD_CONCAT $CONFIG_MTD dep_tristate ' RedBoot partition table parsing' CONFIG_MTD_REDBOOT_PARTS $CONFIG_MTD_PARTITIONS + dep_tristate ' Command line partition table parsing' CONFIG_MTD_CMDLINE_PARTS $CONFIG_MTD_PARTITIONS if [ "$CONFIG_ARM" = "y" ]; then - dep_tristate ' Compaq bootldr partition table parsing' CONFIG_MTD_BOOTLDR_PARTS $CONFIG_MTD_PARTITIONS dep_tristate ' ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS fi diff --git a/drivers/mtd/Makefile b/drivers/mtd/Makefile index 4b8108198e7e..7ec5dfbb2501 100644 --- a/drivers/mtd/Makefile +++ b/drivers/mtd/Makefile @@ -1,12 +1,12 @@ # # Makefile for the memory technology device drivers. # -# -# $Id: Makefile,v 1.63 2001/06/13 09:43:07 dwmw2 Exp $ +# Based on: +# $Id: Makefile,v 1.66 2002/04/23 13:52:14 mag Exp $ -export-objs := mtdcore.o mtdpart.o redboot.o bootldr.o afs.o +export-objs := mtdcore.o mtdpart.o redboot.o cmdline.o afs.o mtdconcat.o -obj-y += chips/ maps/ devices/ nand/ +obj-y += chips/ maps/ devices/ nand/ # *** BIG UGLY NOTE *** # @@ -26,9 +26,10 @@ obj-y += chips/ maps/ devices/ nand/ # Core functionality. 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_BOOTLDR_PARTS) += bootldr.o +obj-$(CONFIG_MTD_CMDLINE_PARTS) += cmdline.o obj-$(CONFIG_MTD_AFS_PARTS) += afs.o # 'Users' - code which presents functionality to userspace. diff --git a/drivers/mtd/bootldr.c b/drivers/mtd/bootldr.c deleted file mode 100644 index 43fcd6bea8b8..000000000000 --- a/drivers/mtd/bootldr.c +++ /dev/null @@ -1,214 +0,0 @@ -/* - * Read flash partition table from Compaq Bootloader - * - * Copyright 2001 Compaq Computer Corporation. - * - * $Id: bootldr.c,v 1.6 2001/10/02 15:05:11 dwmw2 Exp $ - * - * Use consistent with the GNU GPL is permitted, - * provided that this copyright notice is - * preserved in its entirety in all copies and derived works. - * - * COMPAQ COMPUTER CORPORATION MAKES NO WARRANTIES, EXPRESSED OR IMPLIED, - * AS TO THE USEFULNESS OR CORRECTNESS OF THIS CODE OR ITS - * FITNESS FOR ANY PARTICULAR PURPOSE. - * - */ - -/* - * Maintainer: Jamey Hicks (jamey.hicks@compaq.com) - */ - -#include <linux/kernel.h> -#include <linux/slab.h> - -#include <linux/mtd/mtd.h> -#include <linux/mtd/partitions.h> -#include <asm/setup.h> -#include <linux/bootmem.h> - -#define FLASH_PARTITION_NAMELEN 32 -enum LFR_FLAGS { - LFR_SIZE_PREFIX = 1, /* prefix data with 4-byte size */ - LFR_PATCH_BOOTLDR = 2, /* patch bootloader's 0th instruction */ - LFR_KERNEL = 4, /* add BOOTIMG_MAGIC, imgsize and VKERNEL_BASE to head of programmed region (see bootldr.c) */ - LFR_EXPAND = 8 /* expand partition size to fit rest of flash */ -}; - -// the tags are parsed too early to malloc or alloc_bootmem so we'll fix it -// for now -#define MAX_NUM_PARTITIONS 8 -typedef struct FlashRegion { - char name[FLASH_PARTITION_NAMELEN]; - unsigned long base; - unsigned long size; - enum LFR_FLAGS flags; -} FlashRegion; - -typedef struct BootldrFlashPartitionTable { - int magic; /* should be filled with 0x646c7470 (btlp) BOOTLDR_PARTITION_MAGIC */ - int npartitions; - struct FlashRegion partition[8]; -} BootldrFlashPartitionTable; - -#define BOOTLDR_MAGIC 0x646c7462 /* btld: marks a valid bootldr image */ -#define BOOTLDR_PARTITION_MAGIC 0x646c7470 /* btlp: marks a valid bootldr partition table in params sector */ - -#define BOOTLDR_MAGIC_OFFSET 0x20 /* offset 0x20 into the bootldr */ -#define BOOTCAP_OFFSET 0X30 /* offset 0x30 into the bootldr */ - -#define BOOTCAP_WAKEUP (1<<0) -#define BOOTCAP_PARTITIONS (1<<1) /* partition table stored in params sector */ -#define BOOTCAP_PARAMS_AFTER_BOOTLDR (1<<2) /* params sector right after bootldr sector(s), else in last sector */ - -static struct BootldrFlashPartitionTable Table; -static struct BootldrFlashPartitionTable *partition_table = NULL; - - -int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts) -{ - struct mtd_partition *parts; - int ret, retlen, i; - int npartitions = 0; - long partition_table_offset; - long bootmagic = 0; - long bootcap = 0; - int namelen = 0; - - char *names; - -#if 0 - /* verify bootldr magic */ - ret = master->read(master, BOOTLDR_MAGIC_OFFSET, sizeof(long), &retlen, (void *)&bootmagic); - if (ret) - goto out; - if (bootmagic != BOOTLDR_MAGIC) - goto out; - /* see if bootldr supports partition tables and where to find the partition table */ - ret = master->read(master, BOOTCAP_OFFSET, sizeof(long), &retlen, (void *)&bootcap); - if (ret) - goto out; - - if (!(bootcap & BOOTCAP_PARTITIONS)) - goto out; - if (bootcap & BOOTCAP_PARAMS_AFTER_BOOTLDR) - partition_table_offset = master->erasesize; - else - partition_table_offset = master->size - master->erasesize; - - printk(__FUNCTION__ ": partition_table_offset=%#lx\n", partition_table_offset); - printk(__FUNCTION__ ": ptable_addr=%#lx\n", ptable_addr); - - - /* Read the partition table */ - partition_table = (struct BootldrFlashPartitionTable *)kmalloc(PAGE_SIZE, GFP_KERNEL); - if (!partition_table) - return -ENOMEM; - - ret = master->read(master, partition_table_offset, - PAGE_SIZE, &retlen, (void *)partition_table); - if (ret) - goto out; - -#endif - if (!partition_table) - return -ENOMEM; - - - printk(__FUNCTION__ ": magic=%#x\n", partition_table->magic); - printk(__FUNCTION__ ": numPartitions=%#x\n", partition_table->npartitions); - - - /* check for partition table magic number */ - if (partition_table->magic != BOOTLDR_PARTITION_MAGIC) - goto out; - npartitions = (partition_table->npartitions > MAX_NUM_PARTITIONS)? - MAX_NUM_PARTITIONS:partition_table->npartitions; - - printk(__FUNCTION__ ": npartitions=%#x\n", npartitions); - - for (i = 0; i < npartitions; i++) { - namelen += strlen(partition_table->partition[i].name) + 1; - } - - parts = kmalloc(sizeof(*parts)*npartitions + namelen, GFP_KERNEL); - if (!parts) { - ret = -ENOMEM; - goto out; - } - names = (char *)&parts[npartitions]; - memset(parts, 0, sizeof(*parts)*npartitions + namelen); - - - - // from here we use the partition table - for (i = 0; i < npartitions; i++) { - struct FlashRegion *partition = &partition_table->partition[i]; - const char *name = partition->name; - parts[i].name = names; - names += strlen(name) + 1; - strcpy(parts[i].name, name); - - if (partition->flags & LFR_EXPAND) - parts[i].size = MTDPART_SIZ_FULL; - else - parts[i].size = partition->size; - parts[i].offset = partition->base; - parts[i].mask_flags = 0; - - printk(" partition %s o=%x s=%x\n", - parts[i].name, parts[i].offset, parts[i].size); - - } - - ret = npartitions; - *pparts = parts; - - out: -#if 0 - if (partition_table) - kfree(partition_table); -#endif - - return ret; -} - - -static int __init parse_tag_ptable(const struct tag *tag) -{ - char buf[128]; - int i; - int j; - - partition_table = &Table; - -#ifdef CONFIG_DEBUG_LL - sprintf(buf,"ptable: magic = = 0x%lx npartitions= %d \n", - tag->u.ptable.magic,tag->u.ptable.npartitions); - printascii(buf); - - for (i=0; i<tag->u.ptable.npartitions; i++){ - sprintf(buf,"ptable: partition name = %s base= 0x%lx size= 0x%lx flags= 0x%lx\n", - (char *) (&tag->u.ptable.partition[i].name[0]), - tag->u.ptable.partition[i].base, - tag->u.ptable.partition[i].size, - tag->u.ptable.partition[i].flags); - printascii(buf); - } -#endif - - memcpy((void *)partition_table,(void *) (&(tag->u.ptable)),sizeof(partition_table) + - sizeof(struct FlashRegion)*tag->u.ptable.npartitions); - - - return 0; -} - -__tagtable(ATAG_PTABLE, parse_tag_ptable); - -EXPORT_SYMBOL(parse_bootldr_partitions); - - -MODULE_LICENSE("GPL"); -MODULE_AUTHOR("Compaq Computer Corporation"); -MODULE_DESCRIPTION("Parsing code for Compaq bootldr partitions"); diff --git a/drivers/mtd/cmdline.c b/drivers/mtd/cmdline.c new file mode 100644 index 000000000000..4d92157f46de --- /dev/null +++ b/drivers/mtd/cmdline.c @@ -0,0 +1,343 @@ +/* + * $Id: cmdline.c,v 1.4 2002/09/13 01:18:38 jamey Exp $ + * + * Read flash partition table from command line + * + * Copyright 2002 SYSGO Real-Time Solutions GmbH + * + * The format for the command line is as follows: + * + * mtdparts=<mtddef>[;<mtddef] + * <mtddef> := <mtd-id>:<partdef>[,<partdef>] + * <partdef> := <size>[@offset][<name>][ro] + * <mtd-id> := unique id used in mapping driver/device + * <size> := standard linux memsize OR "-" to denote all remaining space + * <name> := '(' NAME ')' + * + * Examples: + * + * 1 NOR Flash, with 1 single writable partition: + * edb7312-nor:- + * + * 1 NOR Flash with 2 partitions, 1 NAND with one + * edb7312-nor:256k(ARMboot)ro,-(root);edb7312-nand:-(home) + */ + +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/partitions.h> +#include <asm/setup.h> +#include <linux/bootmem.h> + +/* error message prefix */ +#define ERRP "mtd: " + +/* debug macro */ +#if 0 +#define dbg(x) do { printk("DEBUG-CMDLINE-PART: "); printk x; } while(0) +#else +#define dbg(x) +#endif + + +/* special size referring to all the remaining space in a partition */ +#define SIZE_REMAINING 0xffffffff + +struct cmdline_mtd_partition { + struct cmdline_mtd_partition *next; + char *mtd_id; + int num_parts; + struct mtd_partition *parts; +}; + +/* mtdpart_setup() parses into here */ +static struct cmdline_mtd_partition *partitions; + +/* the command line passed to mtdpart_setupd() */ +static char *cmdline; +static int cmdline_parsed = 0; + +/* + * Parse one partition definition for an MTD. Since there can be many + * comma separated partition definitions, this function calls itself + * recursively until no more partition definitions are found. Nice side + * effect: the memory to keep the mtd_partition structs and the names + * is allocated upon the last definition being found. At that point the + * syntax has been verified ok. + */ +static struct mtd_partition * newpart(char *s, + char **retptr, + int *num_parts, + int this_part, + unsigned char **extra_mem_ptr, + int extra_mem_size) +{ + struct mtd_partition *parts; + unsigned long size; + unsigned long offset = 0; + char *name; + int name_len; + unsigned char *extra_mem; + char delim; + unsigned int mask_flags; + + /* fetch the partition size */ + if (*s == '-') + { /* assign all remaining space to this partition */ + size = SIZE_REMAINING; + s++; + } + else + { + size = memparse(s, &s); + if (size < PAGE_SIZE) + { + printk(KERN_ERR ERRP "partition size too small (%lx)\n", size); + return 0; + } + } + + /* fetch partition name and flags */ + mask_flags = 0; /* this is going to be a regular partition */ + delim = 0; + /* check for offset */ + if (*s == '@') + { + s++; + offset = memparse(s, &s); + } + /* now look for name */ + if (*s == '(') + { + delim = ')'; + } + if (delim) + { + char *p; + + name = ++s; + if ((p = strchr(name, delim)) == 0) + { + printk(KERN_ERR ERRP "no closing %c found in partition name\n", delim); + return 0; + } + name_len = p - name; + s = p + 1; + } + else + { + name = NULL; + name_len = 13; /* Partition_000 */ + } + + /* record name length for memory allocation later */ + extra_mem_size += name_len + 1; + + /* test for options */ + if (strncmp(s, "ro", 2) == 0) + { + mask_flags |= MTD_WRITEABLE; + s += 2; + } + + /* test if more partitions are following */ + if (*s == ',') + { + if (size == SIZE_REMAINING) + { + printk(KERN_ERR ERRP "no partitions allowed after a fill-up partition\n"); + return 0; + } + /* more partitions follow, parse them */ + if ((parts = newpart(s + 1, &s, num_parts, + this_part + 1, &extra_mem, extra_mem_size)) == 0) + return 0; + } + else + { /* this is the last partition: allocate space for all */ + int alloc_size; + + *num_parts = this_part + 1; + alloc_size = *num_parts * sizeof(struct mtd_partition) + + extra_mem_size; + parts = kmalloc(alloc_size, GFP_KERNEL); + if (!parts) + { + printk(KERN_ERR ERRP "out of memory\n"); + return 0; + } + memset(parts, 0, alloc_size); + extra_mem = (unsigned char *)(parts + *num_parts); + } + /* enter this partition (offset will be calculated later if it is zero at this point) */ + parts[this_part].size = size; + parts[this_part].offset = offset; + parts[this_part].mask_flags = mask_flags; + if (name) + { + strncpy(extra_mem, name, name_len); + extra_mem[name_len] = 0; + } + else + { + sprintf(extra_mem, "Partition_%03d", this_part); + } + parts[this_part].name = extra_mem; + extra_mem += name_len + 1; + + dbg(("partition %d: name <%s>, offset %x, size %x, mask flags %x\n", + this_part, + parts[this_part].name, + parts[this_part].offset, + parts[this_part].size, + parts[this_part].mask_flags)); + + /* return (updated) pointer to extra_mem memory */ + if (extra_mem_ptr) + *extra_mem_ptr = extra_mem; + + /* return (updated) pointer command line string */ + *retptr = s; + + /* return partition table */ + return parts; +} + +/* + * Parse the command line. + */ +static int mtdpart_setup_real(char *s) +{ + cmdline_parsed = 1; + + for( ; s != NULL; ) + { + struct cmdline_mtd_partition *this_mtd; + struct mtd_partition *parts; + int mtd_id_len; + int num_parts; + char *p, *mtd_id; + + mtd_id = s; + /* fetch <mtd-id> */ + if (!(p = strchr(s, ':'))) + { + printk(KERN_ERR ERRP "no mtd-id\n"); + return 0; + } + mtd_id_len = p - mtd_id; + + dbg(("parsing <%s>\n", p+1)); + + /* + * parse one mtd. have it reserve memory for the + * struct cmdline_mtd_partition and the mtd-id string. + */ + parts = newpart(p + 1, /* cmdline */ + &s, /* out: updated cmdline ptr */ + &num_parts, /* out: number of parts */ + 0, /* first partition */ + (unsigned char**)&this_mtd, /* out: extra mem */ + mtd_id_len + 1 + sizeof(*this_mtd)); + + /* enter results */ + this_mtd->parts = parts; + this_mtd->num_parts = num_parts; + this_mtd->mtd_id = (char*)(this_mtd + 1); + strncpy(this_mtd->mtd_id, mtd_id, mtd_id_len); + this_mtd->mtd_id[mtd_id_len] = 0; + + /* link into chain */ + this_mtd->next = partitions; + partitions = this_mtd; + + dbg(("mtdid=<%s> num_parts=<%d>\n", + this_mtd->mtd_id, this_mtd->num_parts)); + + + /* EOS - we're done */ + if (*s == 0) + break; + + /* does another spec follow? */ + if (*s != ';') + { + printk(KERN_ERR ERRP "bad character after partition (%c)\n", *s); + return 0; + } + s++; + } + return 1; +} + +/* + * 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. + */ +int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + const char *mtd_id) +{ + unsigned long offset; + int i; + struct cmdline_mtd_partition *part; + + if (!cmdline) + return -EINVAL; + + /* parse command line */ + if (!cmdline_parsed) + mtdpart_setup_real(cmdline); + + for(part = partitions; part; part = part->next) + { + if (!strcmp(part->mtd_id, mtd_id)) + { + for(i = 0, offset = 0; i < part->num_parts; i++) + { + if (!part->parts[i].offset) + part->parts[i].offset = offset; + else + offset = part->parts[i].offset; + if (part->parts[i].size == SIZE_REMAINING) + part->parts[i].size = master->size - offset; + if (offset + part->parts[i].size > master->size) + { + printk(KERN_WARNING ERRP + "%s: partitioning exceeds flash size, truncating\n", + mtd_id); + part->parts[i].size = master->size - offset; + part->num_parts = i; + } + offset += part->parts[i].size; + } + *pparts = part->parts; + return part->num_parts; + } + } + return -EINVAL; +} + + +/* + * This is the handler for our kernel parameter, called from + * main.c::checksetup(). Note that we can not yet kmalloc() anything, + * so we only save the commandline for later processing. + */ +static int __init mtdpart_setup(char *s) +{ + cmdline = s; + return 1; +} + +__setup("mtdparts=", mtdpart_setup); + +EXPORT_SYMBOL(parse_cmdline_partitions); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); +MODULE_DESCRIPTION("Command line configuration of MTD partitions"); diff --git a/drivers/mtd/ftl.c b/drivers/mtd/ftl.c index 341ad2252885..e40e34d3c7d6 100644 --- a/drivers/mtd/ftl.c +++ b/drivers/mtd/ftl.c @@ -1223,7 +1223,7 @@ static void ftl_notify_add(struct mtd_info *mtd) } partition = kmalloc(sizeof(partition_t), GFP_KERNEL); - disk = alloc_disk(); + disk = alloc_disk(1 << PART_BITS); if (!partition||!disk) { printk(KERN_WARNING "No memory to scan for FTL on %s\n", @@ -1237,7 +1237,6 @@ static void ftl_notify_add(struct mtd_info *mtd) sprintf(disk->disk_name, "ftl%c", 'a' + device); disk->major = FTL_MAJOR; disk->first_minor = device << 4; - disk->minor_shift = PART_BITS; disk->fops = &ftl_blk_fops; partition->mtd = mtd; partition->disk = disk; diff --git a/drivers/mtd/maps/Config.help b/drivers/mtd/maps/Config.help index aaf3a1aa894e..d4cc1af6505e 100644 --- a/drivers/mtd/maps/Config.help +++ b/drivers/mtd/maps/Config.help @@ -1,3 +1,32 @@ +CONFIG_MTD_CDB89712 + This enables access to the flash or ROM chips on the CDB89712 board. + If you have such a board, say 'Y'. + +CONFIG_MTD_CEIVA + This enables access to the flash chips on the Ceiva/Polaroid + PhotoMax Digital Picture Frame. + If you have such a device, say 'Y'. + +CONFIG_MTD_FORTUNET + This enables access to the Flash on the FortuNet board. If you + have such a board, say 'Y'. + +CONFIG_MTD_AUTCPU12 + This enables access to the NV-RAM on autronix autcpu12 board. + If you have such a board, say 'Y'. + +CONFIG_MTD_EDB7312 + This enables access to the CFI Flash on the Cogent EDB7312 board. + If you have such a board, say 'Y' here. + +CONFIG_MTD_NAND_EDB7312 + This enables access to the NAND Flash on the Cogent EDB7312 board. + If you have such a board, say 'Y' here. + +CONFIG_MTD_IMPA7 + This enables access to the NOR Flash on the impA7 board of + implementa GmbH. If you have such a board, say 'Y' here. + CONFIG_MTD_SA1100 This enables access to the flash chips on most platforms based on the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. @@ -39,6 +68,12 @@ CONFIG_MTD_SUN_UFLASH CONFIG_MTD_NORA If you had to ask, you don't have one. Say 'N'. +CONFIG_MTD_L440GX + Support for treating the BIOS flash chip on Intel L440GX motherboards + as an MTD device - with this you can reprogram your BIOS. + + BE VERY CAREFUL. + CONFIG_MTD_PNC2000 PNC-2000 is the name of Network Camera product from PHOTRON Ltd. in Japan. It uses CFI-compliant flash. @@ -50,6 +85,13 @@ 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_TQM8XXL + The TQM8xxL PowerPC board has up to two banks of CFI-compliant + chips, currently uses AMD one. This 'mapping' driver supports + that arrangement, allowing the CFI probe and command set driver + code to communicate with the chips on the TQM8xxL board. More at + <http://www.denx.de/embedded-ppc-en.html>. + CONFIG_MTD_SC520CDP The SC520 CDP board has two banks of CFI-compliant chips and one Dual-in-line JEDEC chip. This 'mapping' driver supports that @@ -59,7 +101,7 @@ CONFIG_MTD_SBC_GXX This provides a driver for the on-board flash of Arcom Control Systems' SBC-GXn family of boards, formerly known as SBC-MediaGX. By default the flash is split into 3 partitions which are accessed - as separate MTD devices. This board utilizes Intel StrataFlash. + as separate MTD devices. This board utilizes Intel StrataFlash. More info at <http://www.arcomcontrols.com/products/icp/pc104/processors/>. @@ -78,6 +120,11 @@ CONFIG_MTD_NETSC520 demonstration board. If you have one of these boards and would like to use the flash chips on it, say 'Y'. +CONFIG_MTD_OCELOT + This enables access routines for the boot flash device and for the + NVRAM on the Momenco Ocelot board. If you have one of these boards + and would like access to either of these, say 'Y'. + CONFIG_MTD_ELAN_104NC This provides a driver for the on-board flash of the Arcom Control System's ELAN-104NC development board. By default the flash @@ -91,17 +138,17 @@ CONFIG_MTD_DC21285 <http://developer.intel.com/design/bridge/quicklist/dsc-21285.htm>. CONFIG_MTD_CSTM_MIPS_IXX - 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, buswidth, vpp - code and addition setup of the flash device for these boards. In - addition, this mapping driver can be used for other boards via - setting of the CONFIG_MTD_CSTM_MIPS_IXX_START/LEN/BUSWIDTH - parameters. This mapping will provide one mtd device using one - partition. The start address can be offset from the beginning of - flash and the len can be less than the total flash device size to - allow a window into the flash. Both CFI and JEDEC probes are - called. + 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, + buswidth, vpp code and addition setup of the flash device for + these boards. In addition, this mapping driver can be used for + other boards via setting of the CONFIG_MTD_CSTM_MIPS_IXX_START/ + LEN/BUSWIDTH parameters. This mapping will provide one mtd device + using one partition. The start address can be offset from the + beginning of flash and the len can be less than the total flash + device size to allow a window into the flash. Both CFI and JEDEC + probes are called. CONFIG_MTD_CSTM_MIPS_IXX_START This is the physical memory location that the MTD driver will @@ -141,6 +188,11 @@ CONFIG_MTD_OCTAGON Computer. More information on the board is available at <http://www.octagonsystems.com/Products/5066/5066.html>. +CONFIG_MTD_PCMCIA + Map driver for accessing PCMCIA linear flash memory cards. These + cards are usually around 4-16MiB in size. This does not include + Compact Flash cards which are treated as IDE devices. + CONFIG_MTD_VMAX This provides a 'mapping' driver which supports the way in which the flash chips are connected in the Tempustech VMAX SBC301 Single @@ -148,32 +200,21 @@ CONFIG_MTD_VMAX <http://www.tempustech.com/tt301.htm>. CONFIG_MTD_CFI_FLAGADM - Mapping for the Flaga digital module. If you don´t have one, ignore + Mapping for the Flaga digital module. If you don´t have one, ignore this setting. -CONFIG_MTD_OCELOT - This enables access routines for the boot flash device and for the - NVRAM on the Momenco Ocelot board. If you have one of these boards - and would like access to either of these, say 'Y'. - -CONFIG_MTD_CDB89712 - This enables access to the flash or ROM chips on the CDB89712 board. - If you have such a board, say 'Y'. - -CONFIG_MTD_L440GX - Support for treating the BIOS flash chip on Intel L440GX motherboards - as an MTD device - with this you can reprogram your BIOS. - - BE VERY CAREFUL. - 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_TQM8XXL - The TQM8xxL PowerPC board has up to two banks of CFI-compliant - chips, currently uses AMD one. This 'mapping' driver supports - that arrangement, allowing the CFI probe and command set driver - code to communicate with the chips on the TQM8xxL board. More at - <http://www.denx.de/embedded-ppc-en.html>. +CONFIG_MTD_EPXA10DB + This enables support for the flash devices on the Altera + Excalibur XA10 Development Board. If you are building a kernel + for on of these boards then you should say 'Y' otherwise say 'N'. + +CONFIG_MTD_PCI + Mapping for accessing flash devices on add-in cards like the Intel XScale + IQ80310 card, and the Intel EBSA285 card in blank ROM programming mode + (please see the manual for the link settings). + If you are not sure, say N. diff --git a/drivers/mtd/maps/Config.in b/drivers/mtd/maps/Config.in index 7b4cbd4eda85..e0668372fa79 100644 --- a/drivers/mtd/maps/Config.in +++ b/drivers/mtd/maps/Config.in @@ -56,8 +56,18 @@ if [ "$CONFIG_ARM" = "y" ]; then dep_tristate ' CFI Flash device mapped on ARM Integrator/P720T' CONFIG_MTD_ARM_INTEGRATOR $CONFIG_MTD_CFI dep_tristate ' Cirrus CDB89712 evaluation board mappings' CONFIG_MTD_CDB89712 $CONFIG_MTD_CFI $CONFIG_ARCH_CDB89712 dep_tristate ' CFI Flash device mapped on StrongARM SA11x0' CONFIG_MTD_SA1100 $CONFIG_MTD_CFI $CONFIG_ARCH_SA1100 $CONFIG_MTD_PARTITIONS - dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE $CONFIG_MTD_PARTITIONS + dep_tristate ' CFI Flash device mapped on DC21285 Footbridge' CONFIG_MTD_DC21285 $CONFIG_MTD_CFI $CONFIG_ARCH_FOOTBRIDGE dep_tristate ' CFI Flash device mapped on the XScale IQ80310 board' CONFIG_MTD_IQ80310 $CONFIG_MTD_CFI $CONFIG_ARCH_IQ80310 + dep_tristate ' CFI Flash device mapped on Epxa10db' CONFIG_MTD_EPXA10DB $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_ARCH_CAMELOT + dep_tristate ' CFI Flash device mapped on the FortuNet board' CONFIG_MTD_FORTUNET $CONFIG_MTD_CFI $CONFIG_MTD_PARTITIONS $CONFIG_SA1100_FORTUNET + dep_tristate ' NV-RAM mapping AUTCPU12 board' CONFIG_MTD_AUTCPU12 $CONFIG_ARCH_AUTCPU12 + dep_tristate ' CFI Flash device mapped on EDB7312' CONFIG_MTD_EDB7312 $CONFIG_MTD_CFI + dep_tristate ' JEDEC Flash device mapped on impA7' CONFIG_MTD_IMPA7 $CONFIG_MTD_JEDECPROBE + dep_tristate ' JEDEC Flash device mapped on Ceiva/Polaroid PhotoMax Digital Picture Frame' CONFIG_MTD_CEIVA $CONFIG_MTD_JEDECPROBE $CONFIG_ARCH_CEIVA fi +# This needs CFI or JEDEC, depending on the cards found. +dep_tristate ' PCI MTD driver' CONFIG_MTD_PCI $CONFIG_MTD $CONFIG_PCI +dep_tristate ' PCMCIA MTD driver' CONFIG_MTD_PCMCIA $CONFIG_MTD $CONFIG_PCMCIA + endmenu diff --git a/drivers/mtd/maps/Makefile b/drivers/mtd/maps/Makefile index c0bdc2fa8f23..f4acee989d04 100644 --- a/drivers/mtd/maps/Makefile +++ b/drivers/mtd/maps/Makefile @@ -4,29 +4,37 @@ # $Id: Makefile,v 1.13 2001/08/16 15:16:58 rmk Exp $ # Chip mappings -obj-$(CONFIG_MTD_CDB89712) += cdb89712.o +obj-$(CONFIG_MTD_CDB89712) += cdb89712.o 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_ELAN_104NC) += elan-104nc.o +obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o +obj-$(CONFIG_MTD_DC21285) += dc21285.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_L440GX) += l440gx.o obj-$(CONFIG_MTD_NORA) += nora.o +obj-$(CONFIG_MTD_CEIVA) += ceiva.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PNC2000) += pnc2000.o +obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o -obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o +obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o obj-$(CONFIG_MTD_NETSC520) += netsc520.o -obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o +obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o 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_SOLUTIONENGINE)+= solutionengine.o +obj-$(CONFIG_MTD_PCI) += pci.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 include $(TOPDIR)/Rules.make diff --git a/drivers/mtd/maps/autcpu12-nvram.c b/drivers/mtd/maps/autcpu12-nvram.c new file mode 100644 index 000000000000..db78b01e6438 --- /dev/null +++ b/drivers/mtd/maps/autcpu12-nvram.c @@ -0,0 +1,179 @@ +/* + * 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 $ + * + * 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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/ioport.h> +#include <asm/io.h> +#include <asm/sizes.h> +#include <asm/hardware.h> +#include <asm/arch/autcpu12.h> +#include <linux/mtd/mtd.h> +#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 +}; + +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) { + printk("Failed to ioremap autcpu12 NV-RAM space\n"); + err = -EIO; + goto out; + } + + /* + * Check for 32K/128K + * read ofs 0 + * read ofs 0x10000 + * Write complement to ofs 0x100000 + * 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); + /* 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); + goto map; + } + /* We have a 128K found, restore 0x10000 and set size + * to 128K + */ + autcpu12_write32(&autcpu12_sram_map,save1,0x10000); + autcpu12_sram_map.size = SZ_128K; + +map: + sram_mtd = do_map_probe("map_ram", &autcpu12_sram_map); + if (!sram_mtd) { + printk("NV-RAM probe failed\n"); + err = -ENXIO; + goto out_ioremap; + } + + sram_mtd->module = THIS_MODULE; + sram_mtd->erasesize = 16; + + if (add_mtd_device(sram_mtd)) { + printk("NV-RAM device addition failed\n"); + err = -ENOMEM; + goto out_probe; + } + + printk("NV-RAM device size %ldK registered on AUTCPU12\n",autcpu12_sram_map.size/SZ_1K); + + return 0; + +out_probe: + map_destroy(sram_mtd); + sram_mtd = 0; + +out_ioremap: + iounmap((void *)autcpu12_sram_map.map_priv_1); +out: + return err; +} + +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); + } +} + +module_init(init_autcpu12_sram); +module_exit(cleanup_autcpu12_maps); + +MODULE_AUTHOR("Thomas Gleixner"); +MODULE_DESCRIPTION("autcpu12 NV-RAM map driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/ceiva.c b/drivers/mtd/maps/ceiva.c new file mode 100644 index 000000000000..259a9a8b76c0 --- /dev/null +++ b/drivers/mtd/maps/ceiva.c @@ -0,0 +1,408 @@ +/* + * Ceiva flash memory driver. + * Copyright (C) 2002 Rob Scott <rscott@mtrob.fdns.net> + * + * Note: this driver supports jedec compatible devices. Modification + * for CFI compatible devices should be straight forward: change + * jedec_probe to cfi_probe. + * + * Based on: sa1100-flash.c, which has the following copyright: + * Flash memory access on SA11x0 based devices + * + * (C) 2000 Nicolas Pitre <nico@cam.org> + * + * $Id: ceiva.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ + */ + +#include <linux/config.h> +#include <linux/module.h> +#include <linux/types.h> +#include <linux/ioport.h> +#include <linux/kernel.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> + +/* + * This isnt 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 + * structure. + * + * Please note: + * 1. The flash size given should be the largest flash size that can + * be accomodated. + * + * 2. The bus width must defined in clps_setup_flash. + * + * The MTD layer will detect flash chip aliasing and reduce the size of + * the map accordingly. + * + */ + +#ifdef CONFIG_ARCH_CEIVA +/* Flash / Partition sizing */ +/* For the 28F8003, we use the block mapping to calcuate the sizes */ +#define MAX_SIZE_KiB (16 + 8 + 8 + 96 + (7*128)) +#define BOOT_PARTITION_SIZE_KiB (16) +#define PARAMS_PARTITION_SIZE_KiB (8) +#define KERNEL_PARTITION_SIZE_KiB (4*128) +/* Use both remaing portion of first flash, and all of second flash */ +#define ROOT_PARTITION_SIZE_KiB (3*128) + (8*128) + +static struct mtd_partition ceiva_partitions[] = { + { + 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 + +static int __init clps_static_partitions(struct mtd_partition **parts) +{ + int nb_parts = 0; + +#ifdef CONFIG_ARCH_CEIVA + if (machine_is_ceiva()) { + *parts = ceiva_partitions; + nb_parts = ARRAY_SIZE(ceiva_partitions); + } +#endif + return nb_parts; +} +#endif + +struct clps_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 clps_info info[NR_SUBMTD]; + +static int __init clps_setup_mtd(struct clps_info *clps, 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. + */ + 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 (clps[i].base == (unsigned long)-1) + break; + + clps[i].res = request_mem_region(clps[i].base, clps[i].size, "clps flash"); + if (!clps[i].res) { + ret = -EBUSY; + break; + } + + clps[i].map = maps + i; + memcpy(clps[i].map, &clps_map, sizeof(struct map_info)); + + clps[i].vbase = ioremap(clps[i].base, clps[i].size); + if (!clps[i].vbase) { + ret = -ENOMEM; + break; + } + + clps[i].map->map_priv_1 = (unsigned long)clps[i].vbase; + clps[i].map->buswidth = clps[i].width; + clps[i].map->size = clps[i].size; + + 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; + subdev[i] = clps[i].mtd; + + printk(KERN_INFO "clps flash: JEDEC device at 0x%08lx, %dMiB, " + "%d-bit\n", clps[i].base, clps[i].mtd->size >> 20, + clps[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(clps[i].vbase); + clps[i].vbase = NULL; + release_resource(clps[i].res); + clps[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, + "clps flash"); + if (*rmtd == NULL) + ret = -ENXIO; +#else + printk(KERN_ERR "clps flash: multiple devices " + "found but MTD concat support disabled.\n"); + ret = -ENXIO; +#endif + } + } + + /* + * If we failed, clean up. + */ + if (ret) { + do { + if (clps[i].mtd) + map_destroy(clps[i].mtd); + if (clps[i].vbase) + iounmap(clps[i].vbase); + if (clps[i].res) + release_resource(clps[i].res); + } while (i--); + + kfree(maps); + } + + return ret; +} + +static void __exit clps_destroy_mtd(struct clps_info *clps, struct mtd_info *mtd) +{ + int i; + + del_mtd_partitions(mtd); + + if (mtd != clps[0].mtd) + mtd_concat_destroy(mtd); + + for (i = NR_SUBMTD; i >= 0; i--) { + if (clps[i].mtd) + map_destroy(clps[i].mtd); + if (clps[i].vbase) + iounmap(clps[i].vbase); + if (clps[i].res) + release_resource(clps[i].res); + } + kfree(clps[0].map); +} + +/* + * We define the memory space, size, and width for the flash memory + * space here. + */ + +static int __init clps_setup_flash(void) +{ + int nr; + +#ifdef CONFIG_ARCH_CEIVA + if (machine_is_ceiva()) { + info[0].base = CS0_PHYS_BASE; + info[0].size = SZ_32M; + info[0].width = CEIVA_FLASH_WIDTH; + info[1].base = CS1_PHYS_BASE; + info[1].size = SZ_32M; + info[1].width = CEIVA_FLASH_WIDTH; + nr = 2; + } +#endif + 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 void __init clps_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, "clps"); + 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) { + part_type = "static"; + break; + } + printk("found: %d partitions\n", nr_parts); +#endif + } while (0); + + if (nr_parts == 0) { + printk(KERN_NOTICE "clps flash: no partition info " + "available, registering whole flash\n"); + add_mtd_device(mtd); + } else { + printk(KERN_NOTICE "clps flash: using %s partition " + "definition\n", part_type); + add_mtd_partitions(mtd, parsed_parts, nr_parts); + } + + /* Always succeeds. */ +} + +static void __exit clps_destroy_partitions(void) +{ + if (parsed_parts) + kfree(parsed_parts); +} + +static struct mtd_info *mymtd; + +static int __init clps_mtd_init(void) +{ + int ret; + int nr; + + nr = clps_setup_flash(); + if (nr < 0) + return nr; + + ret = clps_setup_mtd(info, nr, &mymtd); + if (ret) + return ret; + + clps_locate_partitions(mymtd); + + return 0; +} + +static void __exit clps_mtd_cleanup(void) +{ + clps_destroy_mtd(info, mymtd); + clps_destroy_partitions(); +} + +module_init(clps_mtd_init); +module_exit(clps_mtd_cleanup); + +MODULE_AUTHOR("Rob Scott"); +MODULE_DESCRIPTION("Cirrus Logic JEDEC map driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/dc21285.c b/drivers/mtd/maps/dc21285.c index e7eea7ef53b2..f030f3447302 100644 --- a/drivers/mtd/maps/dc21285.c +++ b/drivers/mtd/maps/dc21285.c @@ -5,9 +5,9 @@ * * This code is GPL * - * $Id: dc21285.c,v 1.6 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: dc21285.c,v 1.9 2002/10/14 12:22:10 rmk Exp $ */ - +#include <linux/config.h> #include <linux/module.h> #include <linux/types.h> #include <linux/kernel.h> @@ -44,15 +44,15 @@ void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr) { - *CSR_ROMWRITEREG = adr; + *CSR_ROMWRITEREG = adr & 3; adr &= ~3; *(__u8*)(map->map_priv_1 + adr) = d; } void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr) { - *CSR_ROMWRITEREG = adr; - adr &= ~1; + *CSR_ROMWRITEREG = adr & 3; + adr &= ~3; *(__u16*)(map->map_priv_1 + adr) = d; } @@ -131,7 +131,7 @@ int __init init_dc21285(void) dc21285_map.buswidth*8); /* Let's map the flash area */ - dc21285_map.map_priv_1 = (unsigned long)__ioremap(DC21285_FLASH, 16*1024*1024, 0); + dc21285_map.map_priv_1 = (unsigned long)ioremap(DC21285_FLASH, 16*1024*1024); if (!dc21285_map.map_priv_1) { printk("Failed to ioremap\n"); return -EIO; @@ -139,21 +139,22 @@ int __init init_dc21285(void) mymtd = do_map_probe("cfi_probe", &dc21285_map); if (mymtd) { - int nrparts; + int nrparts = 0; mymtd->module = THIS_MODULE; /* partition fixup */ +#ifdef CONFIG_MTD_REDBOOT_PARTS nrparts = parse_redboot_partitions(mymtd, &dc21285_parts); - if (nrparts <=0) { +#endif + if (nrparts > 0) { + add_mtd_partitions(mymtd, dc21285_parts, nrparts); + } else if (nrparts == 0) { printk(KERN_NOTICE "RedBoot partition table failed\n"); - iounmap((void *)dc21285_map.map_priv_1); - return -ENXIO; + add_mtd_device(mymtd); } - add_mtd_partitions(mymtd, dc21285_parts, nrparts); - /* * Flash timing is determined with bits 19-16 of the * CSR_SA110_CNTL. The value is the number of wait cycles, or diff --git a/drivers/mtd/maps/edb7312.c b/drivers/mtd/maps/edb7312.c new file mode 100644 index 000000000000..405429d92735 --- /dev/null +++ b/drivers/mtd/maps/edb7312.c @@ -0,0 +1,202 @@ +/* + * $Id: edb7312.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ + * + * Handle mapping of the NOR flash on Cogent EDB7312 boards + * + * Copyright 2002 SYSGO Real-Time Solutions GmbH + * + * 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/config.h> + +#ifdef CONFIG_MTD_PARTITIONS +#include <linux/mtd/partitions.h> +#endif + +#define WINDOW_ADDR 0x00000000 /* physical properties of flash */ +#define WINDOW_SIZE 0x01000000 +#define BUSWIDTH 2 +#define FLASH_BLOCKSIZE_MAIN 0x20000 +#define FLASH_NUMBLOCKS_MAIN 128 +/* can be "cfi_probe", "jedec_probe", "map_rom", 0 }; */ +#define PROBETYPES { "cfi_probe", 0 } + +#define MSG_PREFIX "EDB7312-NOR:" /* prefix for our printk()'s */ +#define MTDID "edb7312-nor" /* for mtdparts= partitioning */ + +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 +}; + +#ifdef CONFIG_MTD_PARTITIONS + +/* + * MTD partitioning stuff + */ +static struct mtd_partition static_partitions[3] = +{ + { + name: "ARMboot", + size: 0x40000, + offset: 0 + }, + { + name: "Kernel", + size: 0x200000, + offset: 0x40000 + }, + { + name: "RootFS", + size: 0xDC0000, + offset: 0x240000 + }, +}; + +#define NB_OF(x) (sizeof (x) / sizeof (x[0])) + +#ifdef CONFIG_MTD_CMDLINE_PARTS +int parse_cmdline_partitions(struct mtd_info *master, + struct mtd_partition **pparts, + const char *mtd_id); +#endif + +#endif + +static int mtd_parts_nb = 0; +static struct mtd_partition *mtd_parts = 0; + +int __init init_edb7312nor(void) +{ + static const char *rom_probe_types[] = PROBETYPES; + const char **type; + const char *part_type = 0; + + printk(KERN_NOTICE MSG_PREFIX "0x%08x at 0x%08x\n", + WINDOW_SIZE, WINDOW_ADDR); + edb7312nor_map.map_priv_1 = (unsigned long) + ioremap(WINDOW_ADDR, WINDOW_SIZE); + + if (!edb7312nor_map.map_priv_1) { + printk(MSG_PREFIX "failed to ioremap\n"); + return -EIO; + } + + mymtd = 0; + type = rom_probe_types; + for(; !mymtd && *type; type++) { + mymtd = do_map_probe(*type, &edb7312nor_map); + } + if (mymtd) { + mymtd->module = THIS_MODULE; + +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + mtd_parts_nb = parse_cmdline_partitions(mymtd, &mtd_parts, MTDID); + if (mtd_parts_nb > 0) + part_type = "command line"; +#endif + if (mtd_parts_nb == 0) + { + mtd_parts = static_partitions; + mtd_parts_nb = NB_OF(static_partitions); + part_type = "static"; + } +#endif + add_mtd_device(mymtd); + if (mtd_parts_nb == 0) + printk(KERN_NOTICE MSG_PREFIX "no partition info available\n"); + else + { + printk(KERN_NOTICE MSG_PREFIX + "using %s partition definition\n", part_type); + add_mtd_partitions(mymtd, mtd_parts, mtd_parts_nb); + } + return 0; + } + + iounmap((void *)edb7312nor_map.map_priv_1); + return -ENXIO; +} + +static void __exit cleanup_edb7312nor(void) +{ + if (mymtd) { + 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; + } +} + +module_init(init_edb7312nor); +module_exit(cleanup_edb7312nor); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Marius Groeger <mag@sysgo.de>"); +MODULE_DESCRIPTION("Generic configurable MTD map driver"); diff --git a/drivers/mtd/maps/epxa10db-flash.c b/drivers/mtd/maps/epxa10db-flash.c new file mode 100644 index 000000000000..cb4c76e4bb71 --- /dev/null +++ b/drivers/mtd/maps/epxa10db-flash.c @@ -0,0 +1,233 @@ +/* + * Flash memory access on EPXA based devices + * + * (C) 2000 Nicolas Pitre <nico@cam.org> + * 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 $ + * + * 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/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <asm/io.h> +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +#include <asm/hardware.h> +#ifdef CONFIG_EPXA10DB +#define BOARD_NAME "EPXA10DB" +#else +#define BOARD_NAME "EPXA1DB" +#endif + +static int nr_parts = 0; +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 +}; + + +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("Failed to ioremap %s flash\n",BOARD_NAME); + return -EIO; + } + + mymtd = do_map_probe("cfi_probe", &epxa_map); + if (!mymtd) { + iounmap((void *)epxa_map.map_priv_1); + return -ENXIO; + } + + mymtd->module = THIS_MODULE; + + /* Unlock the flash device. */ + if(mymtd->unlock){ + for (i=0; i<mymtd->numeraseregions;i++){ + int j; + for(j=0;j<mymtd->eraseregions[i].numblocks;j++){ + mymtd->unlock(mymtd,mymtd->eraseregions[i].offset + j * mymtd->eraseregions[i].erasesize,mymtd->eraseregions[i].erasesize); + } + } + } + +#ifdef CONFIG_MTD_REDBOOT_PARTS + nr_parts = parse_redboot_partitions(mymtd, &parts); + + if (nr_parts > 0) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } +#endif +#ifdef CONFIG_MTD_AFS_PARTS + nr_parts = parse_afs_partitions(mymtd, &parts); + + 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) { + add_mtd_partitions(mymtd, parts, nr_parts); + return 0; + } + + /* If all else fails... */ + add_mtd_device(mymtd); + return 0; +} + +static void __exit epxa_mtd_cleanup(void) +{ + if (mymtd) { + if (nr_parts) + del_mtd_partitions(mymtd); + else + 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; + } +} + + +/* + * This will do for now, once we decide which bootldr we're finally + * going to use then we'll remove this function and do it properly + * + * Partions are currently (as offsets from base of flash): + * 0x00000000 - 0x003FFFFF - bootloader (!) + * 0x00400000 - 0x00FFFFFF - Flashdisk + */ + +static int __init epxa_default_partitions(struct mtd_info *master, struct mtd_partition **pparts) +{ + struct mtd_partition *parts; + int ret, i; + int npartitions = 0; + char *names; + const char *name = "jffs"; + + printk("Using default partitions for %s\n",BOARD_NAME); + npartitions=1; + parts = kmalloc(npartitions*sizeof(*parts)+strlen(name), GFP_KERNEL); + memzero(parts,npartitions*sizeof(*parts)+strlen(name)); + if (!parts) { + ret = -ENOMEM; + goto out; + } + i=0; + names = (char *)&parts[npartitions]; + parts[i].name = names; + names += strlen(name) + 1; + strcpy(parts[i].name, name); + +#ifdef CONFIG_EPXA10DB + parts[i].size = FLASH_SIZE-0x00400000; + parts[i].offset = 0x00400000; +#else + parts[i].size = FLASH_SIZE-0x00180000; + parts[i].offset = 0x00180000; +#endif + + out: + *pparts = parts; + return npartitions; +} + + +module_init(epxa_mtd_init); +module_exit(epxa_mtd_cleanup); + +MODULE_AUTHOR("Clive Davies"); +MODULE_DESCRIPTION("Altera epxa mtd flash map"); +MODULE_LICENSE("GPL"); diff --git a/drivers/mtd/maps/fortunet.c b/drivers/mtd/maps/fortunet.c new file mode 100644 index 000000000000..98fd322e9523 --- /dev/null +++ b/drivers/mtd/maps/fortunet.c @@ -0,0 +1,309 @@ +/* fortunet.c memory map + * + * $Id: fortunet.c,v 1.2 2002/10/14 12:50:22 rmk Exp $ + */ + +#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 MAX_NUM_REGIONS 4 +#define MAX_NUM_PARTITIONS 8 + +#define DEF_WINDOW_ADDR_PHY 0x00000000 +#define DEF_WINDOW_SIZE 0x00800000 // 8 Mega Bytes + +#define MTD_FORTUNET_PK "MTD FortuNet: " + +#define MAX_NAME_SIZE 128 + +struct map_region +{ + int window_addr_phyical; + int altbuswidth; + struct map_info map_info; + struct mtd_info *mymtd; + struct mtd_partition parts[MAX_NUM_PARTITIONS]; + char map_name[MAX_NAME_SIZE]; + char parts_name[MAX_NUM_PARTITIONS][MAX_NAME_SIZE]; +}; + +static struct map_region map_regions[MAX_NUM_REGIONS]; +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 +}; + +static char * __init get_string_option(char *dest,int dest_size,char *sor) +{ + if(!dest_size) + return sor; + dest_size--; + while(*sor) + { + if(*sor==',') + { + sor++; + break; + } + else if(*sor=='\"') + { + sor++; + while(*sor) + { + if(*sor=='\"') + { + sor++; + break; + } + *dest = *sor; + dest++; + sor++; + dest_size--; + if(!dest_size) + { + *dest = 0; + return sor; + } + } + } + else + { + *dest = *sor; + dest++; + sor++; + dest_size--; + if(!dest_size) + { + *dest = 0; + return sor; + } + } + } + *dest = 0; + return sor; +} + +static int __init MTD_New_Region(char *line) +{ + char string[MAX_NAME_SIZE]; + int params[6]; + get_options (get_string_option(string,sizeof(string),line),6,params); + if(params[0]<1) + { + printk(MTD_FORTUNET_PK "Bad paramters for MTD Region " + " name,region-number[,base,size,buswidth,altbuswidth]\n"); + return 1; + } + if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) + { + printk(MTD_FORTUNET_PK "Bad region index of %d only have 0..%u regions\n", + params[1],MAX_NUM_REGIONS-1); + return 1; + } + memset(&map_regions[params[1]],0,sizeof(map_regions[params[1]])); + 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]].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]; + } + if(params[0]>2) + { + map_regions[params[1]].map_info.size = params[3]; + } + if(params[0]>3) + { + map_regions[params[1]].map_info.buswidth = params[4]; + } + if(params[0]>4) + { + map_regions[params[1]].altbuswidth = params[5]; + } + return 1; +} + +static int __init MTD_New_Partion(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 " + " name,region-number,size,offset\n"); + return 1; + } + if((params[1]<0)||(params[1]>=MAX_NUM_REGIONS)) + { + printk(MTD_FORTUNET_PK "Bad region index of %d only have 0..%u regions\n", + params[1],MAX_NUM_REGIONS-1); + return 1; + } + if(map_regions_parts[params[1]]>=MAX_NUM_PARTITIONS) + { + printk(MTD_FORTUNET_PK "Out of space for partion in this region\n"); + return 1; + } + map_regions[params[1]].parts[map_regions_parts[params[1]]].name = + map_regions[params[1]]. parts_name[map_regions_parts[params[1]]]; + strcpy(map_regions[params[1]].parts[map_regions_parts[params[1]]].name,string); + map_regions[params[1]].parts[map_regions_parts[params[1]]].size = + params[2]; + map_regions[params[1]].parts[map_regions_parts[params[1]]].offset = + params[3]; + map_regions[params[1]].parts[map_regions_parts[params[1]]].mask_flags = 0; + map_regions_parts[params[1]]++; + return 1; +} + +__setup("MTD_Region=", MTD_New_Region); +__setup("MTD_Partion=", MTD_New_Partion); + +int __init init_fortunet(void) +{ + int ix,iy; + for(iy=ix=0;ix<MAX_NUM_REGIONS;ix++) + { + if(map_regions_parts[ix]&&(!map_regions_set[ix])) + { + printk(MTD_FORTUNET_PK "Region %d is not setup (Seting 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].altbuswidth = 2; + map_regions[ix].mymtd = NULL; + map_regions[ix].map_info.name = map_regions[ix].map_name; + strcpy(map_regions[ix].map_info.name,"FORTUNET"); + } + if(map_regions_set[ix]) + { + iy++; + printk(KERN_NOTICE MTD_FORTUNET_PK "%s flash device at phyicaly " + " address %x size %x\n", + map_regions[ix].map_info.name, + map_regions[ix].window_addr_phyical, + map_regions[ix].map_info.size); + map_regions[ix].map_info.map_priv_1 = + (int)ioremap_nocache( + map_regions[ix].window_addr_phyical, + map_regions[ix].map_info.size); + if(!map_regions[ix].map_info.map_priv_1) + { + 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", + map_regions[ix].map_info.name, + map_regions[ix].map_info.map_priv_1); + 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 " + "for %s flash.\n", + map_regions[ix].map_info.name); + map_regions[ix].map_info.buswidth = + map_regions[ix].altbuswidth; + map_regions[ix].mymtd = do_map_probe("cfi_probe", + &map_regions[ix].map_info); + } + map_regions[ix].mymtd->module = THIS_MODULE; + add_mtd_partitions(map_regions[ix].mymtd, + map_regions[ix].parts,map_regions_parts[ix]); + } + } + if(iy) + return 0; + return -ENXIO; +} + +static void __exit cleanup_fortunet(void) +{ + int ix; + for(ix=0;ix<MAX_NUM_REGIONS;ix++) + { + if(map_regions_set[ix]) + { + if( map_regions[ix].mymtd ) + { + del_mtd_partitions( map_regions[ix].mymtd ); + map_destroy( map_regions[ix].mymtd ); + } + iounmap((void *)map_regions[ix].map_info.map_priv_1); + } + } +} + +module_init(init_fortunet); +module_exit(cleanup_fortunet); + +MODULE_AUTHOR("FortuNet, Inc."); +MODULE_DESCRIPTION("MTD map driver for FortuNet boards"); diff --git a/drivers/mtd/maps/impa7.c b/drivers/mtd/maps/impa7.c new file mode 100644 index 000000000000..3dc382bc9511 --- /dev/null +++ b/drivers/mtd/maps/impa7.c @@ -0,0 +1,234 @@ +/* + * $Id: impa7.c,v 1.2 2002/09/05 05:11:24 acurtis Exp $ + * + * Handle mapping of the NOR flash on implementa A7 boards + * + * Copyright 2002 SYSGO Real-Time Solutions GmbH + * + * 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/config.h> + +#ifdef CONFIG_MTD_PARTITIONS +#include <linux/mtd/partitions.h> +#endif + +#define WINDOW_ADDR0 0x00000000 /* physical properties of flash */ +#define WINDOW_SIZE0 0x00800000 +#define WINDOW_ADDR1 0x10000000 /* physical properties of flash */ +#define WINDOW_SIZE1 0x00800000 +#define NUM_FLASHBANKS 2 +#define BUSWIDTH 4 + +/* can be { "cfi_probe", "jedec_probe", "map_rom", 0 }; */ +#define PROBETYPES { "jedec_probe", 0 } + +#define MSG_PREFIX "impA7:" /* prefix for our printk()'s */ +#define MTDID "impa7-%d" /* for mtdparts= partitioning */ + +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 #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 + }, +}; + +#ifdef CONFIG_MTD_PARTITIONS + +/* + * MTD partitioning stuff + */ +static struct mtd_partition static_partitions[] = +{ + { + 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; + +int __init init_impa7(void) +{ + static const char *rom_probe_types[] = PROBETYPES; + const char **type; + const char *part_type = 0; + int i; + static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = { + { WINDOW_ADDR0, WINDOW_SIZE0 }, + { WINDOW_ADDR1, WINDOW_SIZE1 }, + }; + char mtdid[10]; + int devicesfound = 0; + + for(i=0; i<NUM_FLASHBANKS; i++) + { + printk(KERN_NOTICE MSG_PREFIX "probing 0x%08lx at 0x%08lx\n", + pt[i].size, pt[i].addr); + impa7_map[i].map_priv_1 = (unsigned long) + ioremap(pt[i].addr, pt[i].size); + + if (!impa7_map[i].map_priv_1) { + printk(MSG_PREFIX "failed to ioremap\n"); + return -EIO; + } + + impa7_mtd[i] = 0; + type = rom_probe_types; + for(; !impa7_mtd[i] && *type; type++) { + impa7_mtd[i] = do_map_probe(*type, &impa7_map[i]); + } + + if (impa7_mtd[i]) + { + impa7_mtd[i]->module = THIS_MODULE; + add_mtd_device(impa7_mtd[i]); + devicesfound++; +#ifdef CONFIG_MTD_PARTITIONS +#ifdef CONFIG_MTD_CMDLINE_PARTS + sprintf(mtdid, MTDID, i); + mtd_parts_nb = parse_cmdline_partitions(impa7_mtd[i], + &mtd_parts, + mtdid); + if (mtd_parts_nb > 0) + part_type = "command line"; +#endif + if (mtd_parts_nb <= 0) + { + mtd_parts = static_partitions; + mtd_parts_nb = NB_OF(static_partitions); + part_type = "static"; + } + if (mtd_parts_nb <= 0) + { + printk(KERN_NOTICE MSG_PREFIX + "no partition info available\n"); + } + else + { + printk(KERN_NOTICE MSG_PREFIX + "using %s partition definition\n", + part_type); + add_mtd_partitions(impa7_mtd[i], + mtd_parts, mtd_parts_nb); + } +#endif + } + else + iounmap((void *)impa7_map[i].map_priv_1); + } + return devicesfound == 0 ? -ENXIO : 0; +} + +static void __exit cleanup_impa7(void) +{ + int i; + for (i=0; i<NUM_FLASHBANKS; i++) + { + if (impa7_mtd[i]) + { + del_mtd_device(impa7_mtd[i]); + map_destroy(impa7_mtd[i]); + } + if (impa7_map[i].map_priv_1) + { + iounmap((void *)impa7_map[i].map_priv_1); + impa7_map[i].map_priv_1 = 0; + } + } +} + +module_init(init_impa7); +module_exit(cleanup_impa7); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Pavel Bartusek <pba@sysgo.de>"); +MODULE_DESCRIPTION("MTD map driver for implementa impA7"); diff --git a/drivers/mtd/maps/iq80310.c b/drivers/mtd/maps/iq80310.c index cb3cb05766d1..3a301135831b 100644 --- a/drivers/mtd/maps/iq80310.c +++ b/drivers/mtd/maps/iq80310.c @@ -1,5 +1,5 @@ /* - * $Id: iq80310.c,v 1.8 2001/10/02 15:05:14 dwmw2 Exp $ + * $Id: iq80310.c,v 1.9 2002/01/01 22:45:02 rmk Exp $ * * Mapping for the Intel XScale IQ80310 evaluation board * @@ -116,7 +116,7 @@ static int __init init_iq80310(void) int parsed_nr_parts = 0; char *part_type = "static"; - iq80310_map.map_priv_1 = (unsigned long)__ioremap(WINDOW_ADDR, WINDOW_SIZE, 0); + iq80310_map.map_priv_1 = (unsigned long)ioremap(WINDOW_ADDR, WINDOW_SIZE); if (!iq80310_map.map_priv_1) { printk("Failed to ioremap\n"); return -EIO; @@ -161,7 +161,6 @@ static void __exit cleanup_iq80310(void) } if (iq80310_map.map_priv_1) iounmap((void *)iq80310_map.map_priv_1); - return 0; } module_init(init_iq80310); diff --git a/drivers/mtd/maps/pci.c b/drivers/mtd/maps/pci.c new file mode 100644 index 000000000000..ccc854980c6f --- /dev/null +++ b/drivers/mtd/maps/pci.c @@ -0,0 +1,385 @@ +/* + * linux/drivers/mtd/maps/pci.c + * + * Copyright (C) 2001 Russell King, All rights reserved. + * + * 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: pci.c,v 1.1 2001/09/27 20:28:45 rmk Exp $ + * + * Generic PCI memory map driver. We support the following boards: + * - Intel IQ80310 ATU. + * - Intel EBSA285 (blank rom programming mode). Tested working 27/09/2001 + */ +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/pci.h> +#include <linux/init.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/map.h> +#include <linux/mtd/partitions.h> + +struct map_pci_info; + +struct mtd_pci_info { + int (*init)(struct pci_dev *dev, struct map_pci_info *map); + void (*exit)(struct pci_dev *dev, struct map_pci_info *map); + unsigned long (*translate)(struct map_pci_info *map, unsigned long ofs); + const char *map_name; +}; + +struct map_pci_info { + struct map_info map; + void *base; + void (*exit)(struct pci_dev *dev, struct map_pci_info *map); + unsigned long (*translate)(struct map_pci_info *map, unsigned long ofs); + struct pci_dev *dev; +}; + +/* + * Intel IOP80310 Flash driver + */ + +static int +intel_iq80310_init(struct pci_dev *dev, struct map_pci_info *map) +{ + u32 win_base; + + map->map.buswidth = 1; + map->map.size = 0x00800000; + map->base = ioremap_nocache(pci_resource_start(dev, 0), + pci_resource_len(dev, 0)); + + if (!map->base) + return -ENOMEM; + + /* + * We want to base the memory window at Xscale + * bus address 0, not 0x1000. + */ + pci_read_config_dword(dev, 0x44, &win_base); + pci_write_config_dword(dev, 0x44, 0); + + map->map.map_priv_2 = win_base; + + return 0; +} + +static void +intel_iq80310_exit(struct pci_dev *dev, struct map_pci_info *map) +{ + if (map->base) + iounmap((void *)map->base); + pci_write_config_dword(dev, 0x44, map->map.map_priv_2); +} + +static unsigned long +intel_iq80310_translate(struct map_pci_info *map, unsigned long ofs) +{ + unsigned long page_addr = ofs & 0x00400000; + + /* + * This mundges the flash location so we avoid + * the first 80 bytes (they appear to read nonsense). + */ + if (page_addr) { + writel(0x00000008, map->base + 0x1558); + writel(0x00000000, map->base + 0x1550); + } else { + writel(0x00000007, map->base + 0x1558); + writel(0x00800000, map->base + 0x1550); + ofs += 0x00800000; + } + + return 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", +}; + +/* + * Intel DC21285 driver + */ + +static int +intel_dc21285_init(struct pci_dev *dev, struct map_pci_info *map) +{ + unsigned long base, len; + + base = pci_resource_start(dev, PCI_ROM_RESOURCE); + len = pci_resource_len(dev, PCI_ROM_RESOURCE); + + if (!len || !base) { + /* + * No ROM resource + */ + base = pci_resource_start(dev, 2); + len = pci_resource_len(dev, 2); + + /* + * We need to re-allocate PCI BAR2 address range to the + * PCI ROM BAR, and disable PCI BAR2. + */ + } else { + /* + * Hmm, if an address was allocated to the ROM resource, but + * not enabled, should we be allocating a new resource for it + * or simply enabling it? + */ + if (!(pci_resource_flags(dev, PCI_ROM_RESOURCE) & + PCI_ROM_ADDRESS_ENABLE)) { + u32 val; + pci_resource_flags(dev, PCI_ROM_RESOURCE) |= PCI_ROM_ADDRESS_ENABLE; + pci_read_config_dword(dev, PCI_ROM_ADDRESS, &val); + val |= PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(dev, PCI_ROM_ADDRESS, val); + printk("%s: enabling expansion ROM\n", dev->slot_name); + } + } + + if (!len || !base) + return -ENXIO; + + map->map.buswidth = 4; + map->map.size = len; + map->base = ioremap_nocache(base, len); + + if (!map->base) + return -ENOMEM; + + return 0; +} + +static void +intel_dc21285_exit(struct pci_dev *dev, struct map_pci_info *map) +{ + u32 val; + + if (map->base) + iounmap((void *)map->base); + + /* + * We need to undo the PCI BAR2/PCI ROM BAR address alteration. + */ + pci_resource_flags(dev, PCI_ROM_RESOURCE) &= ~PCI_ROM_ADDRESS_ENABLE; + pci_read_config_dword(dev, PCI_ROM_ADDRESS, &val); + val &= ~PCI_ROM_ADDRESS_ENABLE; + pci_write_config_dword(dev, PCI_ROM_ADDRESS, val); +} + +static unsigned long +intel_dc21285_translate(struct map_pci_info *map, unsigned long ofs) +{ + return ofs & 0x00ffffc0 ? ofs : (ofs ^ (1 << 5)); +} + +static struct mtd_pci_info intel_dc21285_info = { + init: intel_dc21285_init, + exit: intel_dc21285_exit, + translate: intel_dc21285_translate, + map_name: "jedec_probe", +}; + +/* + * PCI device ID table + */ + +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_DEC, + device: PCI_DEVICE_ID_DEC_21285, + subvendor: 0, /* DC21285 defaults to 0 on reset */ + subdevice: 0, /* DC21285 defaults to 0 on reset */ + class: 0, + class_mask: 0, + driver_data: (unsigned long)&intel_dc21285_info, + }, + { 0, } +}; + +/* + * Generic code follows. + */ + +static u8 mtd_pci_read8(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + u8 val = readb(map->base + map->translate(map, ofs)); +// printk("read8 : %08lx => %02x\n", ofs, val); + return val; +} + +static u16 mtd_pci_read16(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + u16 val = readw(map->base + map->translate(map, ofs)); +// printk("read16: %08lx => %04x\n", ofs, val); + return val; +} + +static u32 mtd_pci_read32(struct map_info *_map, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + u32 val = readl(map->base + map->translate(map, ofs)); +// printk("read32: %08lx => %08x\n", ofs, val); + return val; +} + +static void mtd_pci_copyfrom(struct map_info *_map, void *to, unsigned long from, ssize_t len) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + memcpy_fromio(to, map->base + map->translate(map, from), len); +} + +static void mtd_pci_write8(struct map_info *_map, u8 val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write8 : %08lx <= %02x\n", ofs, val); + writeb(val, map->base + map->translate(map, ofs)); +} + +static void mtd_pci_write16(struct map_info *_map, u16 val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write16: %08lx <= %04x\n", ofs, val); + writew(val, map->base + map->translate(map, ofs)); +} + +static void mtd_pci_write32(struct map_info *_map, u32 val, unsigned long ofs) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; +// printk("write32: %08lx <= %08x\n", ofs, val); + writel(val, map->base + map->translate(map, ofs)); +} + +static void mtd_pci_copyto(struct map_info *_map, unsigned long to, const void *from, ssize_t len) +{ + struct map_pci_info *map = (struct map_pci_info *)_map; + memcpy_toio(map->base + map->translate(map, to), from, len); +} + +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, +}; + +static int __devinit +mtd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) +{ + struct mtd_pci_info *info = (struct mtd_pci_info *)id->driver_data; + struct map_pci_info *map = NULL; + struct mtd_info *mtd = NULL; + int err; + + err = pci_enable_device(dev); + if (err) + goto out; + + err = pci_request_regions(dev, "pci mtd"); + if (err) + goto out; + + map = kmalloc(sizeof(*map), GFP_KERNEL); + err = -ENOMEM; + if (!map) + goto release; + + map->map = mtd_pci_map; + map->map.name = dev->slot_name; + map->dev = dev; + map->exit = info->exit; + map->translate = info->translate; + + err = info->init(dev, map); + if (err) + goto release; + + /* tsk - do_map_probe should take const char * */ + mtd = do_map_probe((char *)info->map_name, &map->map); + err = -ENODEV; + if (!mtd) + goto release; + + mtd->module = THIS_MODULE; + add_mtd_device(mtd); + + pci_set_drvdata(dev, mtd); + + return 0; + +release: + if (mtd) + map_destroy(mtd); + + if (map) { + map->exit(dev, map); + kfree(map); + } + + pci_release_regions(dev); +out: + return err; +} + +static void __devexit +mtd_pci_remove(struct pci_dev *dev) +{ + struct mtd_info *mtd = pci_get_drvdata(dev); + struct map_pci_info *map = mtd->priv; + + del_mtd_device(mtd); + map_destroy(mtd); + map->exit(dev, map); + kfree(map); + + pci_set_drvdata(dev, NULL); + pci_release_regions(dev); +} + +static struct pci_driver mtd_pci_driver = { + name: "MTD PCI", + probe: mtd_pci_probe, + remove: mtd_pci_remove, + id_table: mtd_pci_ids, +}; + +static int __init mtd_pci_maps_init(void) +{ + return pci_module_init(&mtd_pci_driver); +} + +static void __exit mtd_pci_maps_exit(void) +{ + pci_unregister_driver(&mtd_pci_driver); +} + +module_init(mtd_pci_maps_init); +module_exit(mtd_pci_maps_exit); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Russell King <rmk@arm.linux.org.uk>"); +MODULE_DESCRIPTION("Generic PCI map driver"); +MODULE_DEVICE_TABLE(pci, mtd_pci_ids); + diff --git a/drivers/mtd/maps/pcmciamtd.c b/drivers/mtd/maps/pcmciamtd.c new file mode 100644 index 000000000000..fb87cdd8b873 --- /dev/null +++ b/drivers/mtd/maps/pcmciamtd.c @@ -0,0 +1,893 @@ +/* + * $Id: pcmciamtd.c,v 1.36 2002/10/14 18:49:12 rmk Exp $ + * + * pcmciamtd.c - MTD driver for PCMCIA flash memory cards + * + * Author: Simon Evans <spse@secret.org.uk> + * + * Copyright (C) 2002 Simon Evans + * + * Licence: GPL + * + */ + +#include <linux/module.h> +#include <linux/slab.h> +#include <linux/timer.h> +#include <asm/io.h> +#include <asm/system.h> + +#include <pcmcia/version.h> +#include <pcmcia/cs_types.h> +#include <pcmcia/cs.h> +#include <pcmcia/cistpl.h> +#include <pcmcia/ds.h> + +#include <linux/mtd/map.h> + +#ifdef CONFIG_MTD_DEBUG +static int debug = CONFIG_MTD_DEBUG_VERBOSE; +MODULE_PARM(debug, "i"); +MODULE_PARM_DESC(debug, "Set Debug Level 0=quiet, 5=noisy"); +#undef DEBUG +#define DEBUG(n, format, arg...) \ + if (n <= debug) { \ + printk(KERN_DEBUG __FILE__ ":%s(): " format "\n", __FUNCTION__ , ## arg); \ + } + +#else +#undef DEBUG +#define DEBUG(n, arg...) +static const int debug = 0; +#endif + +#define err(format, arg...) printk(KERN_ERR __FILE__ ": " format "\n" , ## arg) +#define info(format, arg...) printk(KERN_INFO __FILE__ ": " format "\n" , ## arg) +#define warn(format, arg...) printk(KERN_WARNING __FILE__ ": " format "\n" , ## arg) + + +#define DRIVER_DESC "PCMCIA Flash memory card driver" +#define DRIVER_VERSION "$Revision: 1.36 $" + +/* Size of the PCMCIA address space: 26 bits = 64 MB */ +#define MAX_PCMCIA_ADDR 0x4000000 + +struct pcmciamtd_dev { + struct list_head list; + dev_link_t link; /* PCMCIA link */ + caddr_t win_base; /* ioremapped address of PCMCIA window */ + unsigned int win_size; /* size of window */ + unsigned int cardsize; /* size of whole card */ + unsigned int offset; /* offset into card the window currently points at */ + struct map_info pcmcia_map; + struct mtd_info *mtd_info; + u8 vpp; + char mtd_name[sizeof(struct cistpl_vers_1_t)]; +}; + + +static dev_info_t dev_info = "pcmciamtd"; +static LIST_HEAD(dev_list); + +/* Module parameters */ + +/* 2 = do 16-bit transfers, 1 = do 8-bit transfers */ +static int buswidth = 2; + +/* Speed of memory accesses, in ns */ +static int mem_speed; + +/* Force the size of an SRAM card */ +static int force_size; + +/* Force Vpp */ +static int vpp; + +/* Set Vpp */ +static int setvpp; + +/* Force card to be treated as FLASH, ROM or RAM */ +static int mem_type; + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Simon Evans <spse@secret.org.uk>"); +MODULE_DESCRIPTION(DRIVER_DESC); +MODULE_PARM(buswidth, "i"); +MODULE_PARM_DESC(buswidth, "Set buswidth (1=8 bit, 2=16 bit, default=2)"); +MODULE_PARM(mem_speed, "i"); +MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns"); +MODULE_PARM(force_size, "i"); +MODULE_PARM_DESC(force_size, "Force size of card in MB (1-64)"); +MODULE_PARM(setvpp, "i"); +MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)"); +MODULE_PARM(vpp, "i"); +MODULE_PARM_DESC(vpp, "Vpp value in 1/10ths eg 33=3.3V 120=12V (Dangerous)"); +MODULE_PARM(mem_type, "i"); +MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)"); + + + +static void inline cs_error(client_handle_t handle, int func, int ret) +{ + error_info_t err = { func, ret }; + CardServices(ReportError, handle, &err); +} + + +/* read/write{8,16} copy_{from,to} routines with window remapping to access whole card */ + +static caddr_t remap_window(struct map_info *map, unsigned long to) +{ + struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; + window_handle_t win = (window_handle_t)map->map_priv_2; + memreq_t mrq; + int ret; + + mrq.CardOffset = to & ~(dev->win_size-1); + if(mrq.CardOffset != dev->offset) { + DEBUG(2, "Remapping window from 0x%8.8x to 0x%8.8x", + dev->offset, mrq.CardOffset); + mrq.Page = 0; + if( (ret = CardServices(MapMemPage, win, &mrq)) != CS_SUCCESS) { + cs_error(dev->link.handle, MapMemPage, ret); + return NULL; + } + dev->offset = mrq.CardOffset; + } + return dev->win_base + (to & (dev->win_size-1)); +} + + +static u8 pcmcia_read8_remap(struct map_info *map, unsigned long ofs) +{ + caddr_t addr; + u8 d; + + addr = remap_window(map, ofs); + if(!addr) + return 0; + + d = readb(addr); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, addr, d); + return d; +} + + +static u16 pcmcia_read16_remap(struct map_info *map, unsigned long ofs) +{ + caddr_t addr; + u16 d; + + addr = remap_window(map, ofs); + if(!addr) + return 0; + + d = readw(addr); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, addr, d); + return d; +} + + +static void pcmcia_copy_from_remap(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; + unsigned long win_size = dev->win_size; + + DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + while(len) { + int toread = win_size - (from & (win_size-1)); + caddr_t addr; + + if(toread > len) + toread = len; + + addr = remap_window(map, from); + if(!addr) + return; + + DEBUG(4, "memcpy from %p to %p len = %d", addr, to, toread); + memcpy_fromio(to, addr, toread); + len -= toread; + to += toread; + from += toread; + } +} + + +static void pcmcia_write8_remap(struct map_info *map, u8 d, unsigned long adr) +{ + caddr_t addr = remap_window(map, adr); + + if(!addr) + return; + + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, addr, d); + writeb(d, addr); +} + + +static void pcmcia_write16_remap(struct map_info *map, u16 d, unsigned long adr) +{ + caddr_t addr = remap_window(map, adr); + if(!addr) + return; + + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, addr, d); + writew(d, addr); +} + + +static void pcmcia_copy_to_remap(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; + unsigned long win_size = dev->win_size; + + DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + while(len) { + int towrite = win_size - (to & (win_size-1)); + caddr_t addr; + + if(towrite > len) + towrite = len; + + addr = remap_window(map, to); + if(!addr) + return; + + DEBUG(4, "memcpy from %p to %p len = %d", from, addr, towrite); + memcpy_toio(addr, from, towrite); + len -= towrite; + to += towrite; + from += towrite; + } +} + + +/* read/write{8,16} copy_{from,to} routines with direct access */ + +static u8 pcmcia_read8(struct map_info *map, unsigned long ofs) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + u8 d; + + d = readb(win_base + ofs); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%02x", ofs, win_base + ofs, d); + return d; +} + + +static u16 pcmcia_read16(struct map_info *map, unsigned long ofs) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + u16 d; + + d = readw(win_base + ofs); + DEBUG(3, "ofs = 0x%08lx (%p) data = 0x%04x", ofs, win_base + ofs, d); + return d; +} + + +static void pcmcia_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + + DEBUG(3, "to = %p from = %lu len = %u", to, from, len); + memcpy_fromio(to, win_base + from, len); +} + + +static void pcmcia_write8(struct map_info *map, u8 d, unsigned long adr) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%02x", adr, win_base + adr, d); + writeb(d, win_base + adr); +} + + +static void pcmcia_write16(struct map_info *map, u16 d, unsigned long adr) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + + DEBUG(3, "adr = 0x%08lx (%p) data = 0x%04x", adr, win_base + adr, d); + writew(d, win_base + adr); +} + + +static void pcmcia_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len) +{ + caddr_t win_base = (caddr_t)map->map_priv_2; + + DEBUG(3, "to = %lu from = %p len = %u", to, from, len); + memcpy_toio(win_base + to, from, len); +} + + +static void pcmciamtd_set_vpp(struct map_info *map, int on) +{ + struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1; + dev_link_t *link = &dev->link; + modconf_t mod; + int ret; + + mod.Attributes = CONF_VPP1_CHANGE_VALID | CONF_VPP2_CHANGE_VALID; + mod.Vcc = 0; + mod.Vpp1 = mod.Vpp2 = on ? dev->vpp : 0; + + DEBUG(2, "dev = %p on = %d vpp = %d\n", dev, on, dev->vpp); + ret = CardServices(ModifyConfiguration, link->handle, &mod); + if(ret != CS_SUCCESS) { + cs_error(link->handle, ModifyConfiguration, ret); + } +} + + +/* After a card is removed, pcmciamtd_release() will unregister the + * device, and release the PCMCIA configuration. If the device is + * still open, this will be postponed until it is closed. + */ + +static void pcmciamtd_release(u_long arg) +{ + dev_link_t *link = (dev_link_t *)arg; + struct pcmciamtd_dev *dev = NULL; + int ret; + struct list_head *temp1, *temp2; + + DEBUG(3, "link = 0x%p", link); + /* Find device in list */ + list_for_each_safe(temp1, temp2, &dev_list) { + dev = list_entry(temp1, struct pcmciamtd_dev, list); + if(link == &dev->link) + break; + } + if(link != &dev->link) { + DEBUG(1, "Cant find %p in dev_list", link); + return; + } + + if(dev) { + if(dev->mtd_info) { + del_mtd_device(dev->mtd_info); + dev->mtd_info = NULL; + MOD_DEC_USE_COUNT; + } + if (link->win) { + if(dev->win_base) { + iounmap(dev->win_base); + dev->win_base = NULL; + } + CardServices(ReleaseWindow, link->win); + } + ret = CardServices(ReleaseConfiguration, link->handle); + if(ret != CS_SUCCESS) + cs_error(link->handle, ReleaseConfiguration, ret); + + } + link->state &= ~DEV_CONFIG; +} + + +static void card_settings(struct pcmciamtd_dev *dev, dev_link_t *link, int *new_name) +{ + int rc; + tuple_t tuple; + cisparse_t parse; + u_char buf[64]; + + tuple.Attributes = 0; + tuple.TupleData = (cisdata_t *)buf; + tuple.TupleDataMax = sizeof(buf); + tuple.TupleOffset = 0; + tuple.DesiredTuple = RETURN_FIRST_TUPLE; + + rc = CardServices(GetFirstTuple, link->handle, &tuple); + while(rc == CS_SUCCESS) { + rc = CardServices(GetTupleData, link->handle, &tuple); + if(rc != CS_SUCCESS) { + cs_error(link->handle, GetTupleData, rc); + break; + } + rc = CardServices(ParseTuple, link->handle, &tuple, &parse); + if(rc != CS_SUCCESS) { + cs_error(link->handle, ParseTuple, rc); + break; + } + + switch(tuple.TupleCode) { + case CISTPL_FORMAT: { + cistpl_format_t *t = &parse.format; + (void)t; /* Shut up, gcc */ + DEBUG(2, "Format type: %u, Error Detection: %u, offset = %u, length =%u", + t->type, t->edc, t->offset, t->length); + break; + + } + + case CISTPL_DEVICE: { + cistpl_device_t *t = &parse.device; + int i; + DEBUG(2, "Common memory:"); + dev->pcmcia_map.size = t->dev[0].size; + for(i = 0; i < t->ndev; i++) { + DEBUG(2, "Region %d, type = %u", i, t->dev[i].type); + DEBUG(2, "Region %d, wp = %u", i, t->dev[i].wp); + DEBUG(2, "Region %d, speed = %u ns", i, t->dev[i].speed); + DEBUG(2, "Region %d, size = %u bytes", i, t->dev[i].size); + } + break; + } + + case CISTPL_VERS_1: { + cistpl_vers_1_t *t = &parse.version_1; + int i; + if(t->ns) { + dev->mtd_name[0] = '\0'; + for(i = 0; i < t->ns; i++) { + if(i) + strcat(dev->mtd_name, " "); + strcat(dev->mtd_name, t->str+t->ofs[i]); + } + } + DEBUG(2, "Found name: %s", dev->mtd_name); + break; + } + + case CISTPL_JEDEC_C: { + cistpl_jedec_t *t = &parse.jedec; + int i; + for(i = 0; i < t->nid; i++) { + DEBUG(2, "JEDEC: 0x%02x 0x%02x", t->id[i].mfr, t->id[i].info); + } + break; + } + + case CISTPL_DEVICE_GEO: { + cistpl_device_geo_t *t = &parse.device_geo; + int i; + dev->pcmcia_map.buswidth = t->geo[0].buswidth; + for(i = 0; i < t->ngeo; i++) { + DEBUG(2, "region: %d buswidth = %u", i, t->geo[i].buswidth); + DEBUG(2, "region: %d erase_block = %u", i, t->geo[i].erase_block); + DEBUG(2, "region: %d read_block = %u", i, t->geo[i].read_block); + DEBUG(2, "region: %d write_block = %u", i, t->geo[i].write_block); + DEBUG(2, "region: %d partition = %u", i, t->geo[i].partition); + DEBUG(2, "region: %d interleave = %u", i, t->geo[i].interleave); + } + break; + } + + default: + DEBUG(2, "Unknown tuple code %d", tuple.TupleCode); + } + + rc = CardServices(GetNextTuple, link->handle, &tuple, &parse); + } + if(!dev->pcmcia_map.size) + dev->pcmcia_map.size = MAX_PCMCIA_ADDR; + + if(!dev->pcmcia_map.buswidth) + dev->pcmcia_map.buswidth = 2; + + if(force_size) { + dev->pcmcia_map.size = force_size << 20; + DEBUG(2, "size forced to %dM", force_size); + + } + + if(buswidth) { + dev->pcmcia_map.buswidth = buswidth; + DEBUG(2, "buswidth forced to %d", buswidth); + } + + dev->pcmcia_map.name = dev->mtd_name; + if(!dev->mtd_name[0]) { + strcpy(dev->mtd_name, "PCMCIA Memory card"); + *new_name = 1; + } + + DEBUG(1, "Device: Size: %lu Width:%d Name: %s", + dev->pcmcia_map.size, dev->pcmcia_map.buswidth << 3, dev->mtd_name); +} + + +/* pcmciamtd_config() is scheduled to run after a CARD_INSERTION event + * is received, to configure the PCMCIA socket, and to make the + * MTD device available to the system. + */ + +#define CS_CHECK(fn, args...) \ +while ((last_ret=CardServices(last_fn=(fn), args))!=0) goto cs_failed + +static void pcmciamtd_config(dev_link_t *link) +{ + struct pcmciamtd_dev *dev = link->priv; + struct mtd_info *mtd = NULL; + cs_status_t status; + win_req_t req; + int last_ret = 0, last_fn = 0; + int ret; + int i; + config_info_t t; + static char *probes[] = { "jedec_probe", "cfi_probe" }; + cisinfo_t cisinfo; + int new_name = 0; + + DEBUG(3, "link=0x%p", link); + + /* Configure card */ + link->state |= DEV_CONFIG; + + DEBUG(2, "Validating CIS"); + ret = CardServices(ValidateCIS, link->handle, &cisinfo); + if(ret != CS_SUCCESS) { + cs_error(link->handle, GetTupleData, ret); + } else { + DEBUG(2, "ValidateCIS found %d chains", cisinfo.Chains); + } + + card_settings(dev, link, &new_name); + + dev->pcmcia_map.read8 = pcmcia_read8_remap; + dev->pcmcia_map.read16 = pcmcia_read16_remap; + dev->pcmcia_map.copy_from = pcmcia_copy_from_remap; + dev->pcmcia_map.write8 = pcmcia_write8_remap; + dev->pcmcia_map.write16 = pcmcia_write16_remap; + dev->pcmcia_map.copy_to = pcmcia_copy_to_remap; + if(setvpp == 1) + 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 + whole card - otherwise we try smaller windows until we succeed */ + + req.Attributes = WIN_MEMORY_TYPE_CM | WIN_ENABLE; + req.Attributes |= (dev->pcmcia_map.buswidth == 1) ? WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16; + req.Base = 0; + req.AccessSpeed = mem_speed; + link->win = (window_handle_t)link->handle; + req.Size = (force_size) ? force_size << 20 : MAX_PCMCIA_ADDR; + dev->win_size = 0; + + do { + int ret; + DEBUG(2, "requesting window with size = %dKB memspeed = %d", + req.Size >> 10, req.AccessSpeed); + link->win = (window_handle_t)link->handle; + ret = CardServices(RequestWindow, &link->win, &req); + DEBUG(2, "ret = %d dev->win_size = %d", ret, dev->win_size); + if(ret) { + req.Size >>= 1; + } else { + DEBUG(2, "Got window of size %dKB", req.Size >> 10); + dev->win_size = req.Size; + break; + } + } while(req.Size >= 0x1000); + + DEBUG(2, "dev->win_size = %d", dev->win_size); + + if(!dev->win_size) { + err("Cant allocate memory window"); + pcmciamtd_release((u_long)link); + return; + } + DEBUG(1, "Allocated a window of %dKB", dev->win_size >> 10); + + /* Get write protect status */ + CS_CHECK(GetStatus, link->handle, &status); + DEBUG(2, "status value: 0x%x window handle = 0x%8.8lx", + status.CardState, (unsigned long)link->win); + dev->win_base = ioremap(req.Base, req.Size); + if(!dev->win_base) { + err("ioremap(%lu, %u) failed", req.Base, req.Size); + pcmciamtd_release((u_long)link); + return; + } + DEBUG(1, "mapped window dev = %p req.base = 0x%lx base = %p size = 0x%x", + dev, req.Base, dev->win_base, req.Size); + dev->cardsize = 0; + dev->offset = 0; + + dev->pcmcia_map.map_priv_1 = (unsigned long)dev; + dev->pcmcia_map.map_priv_2 = (unsigned long)link->win; + + DEBUG(2, "Getting configuration"); + CS_CHECK(GetConfigurationInfo, link->handle, &t); + DEBUG(2, "Vcc = %d Vpp1 = %d Vpp2 = %d", t.Vcc, t.Vpp1, t.Vpp2); + dev->vpp = (vpp) ? vpp : t.Vpp1; + link->conf.Attributes = 0; + link->conf.Vcc = t.Vcc; + if(setvpp == 2) { + link->conf.Vpp1 = dev->vpp; + link->conf.Vpp2 = dev->vpp; + } else { + link->conf.Vpp1 = 0; + link->conf.Vpp2 = 0; + } + + link->conf.IntType = INT_MEMORY; + link->conf.ConfigBase = t.ConfigBase; + link->conf.Status = t.Status; + link->conf.Pin = t.Pin; + link->conf.Copy = t.Copy; + link->conf.ExtStatus = t.ExtStatus; + link->conf.ConfigIndex = 0; + link->conf.Present = t.Present; + DEBUG(2, "Setting Configuration"); + ret = CardServices(RequestConfiguration, link->handle, &link->conf); + if(ret != CS_SUCCESS) { + cs_error(link->handle, RequestConfiguration, ret); + } + + link->dev = NULL; + link->state &= ~DEV_CONFIG_PENDING; + + if(mem_type == 1) { + mtd = do_map_probe("map_ram", &dev->pcmcia_map); + } else if(mem_type == 2) { + mtd = do_map_probe("map_rom", &dev->pcmcia_map); + } else { + for(i = 0; i < sizeof(probes) / sizeof(char *); i++) { + DEBUG(1, "Trying %s", probes[i]); + mtd = do_map_probe(probes[i], &dev->pcmcia_map); + if(mtd) + break; + + DEBUG(1, "FAILED: %s", probes[i]); + } + } + + if(!mtd) { + DEBUG(1, "Cant find an MTD"); + pcmciamtd_release((u_long)link); + return; + } + + dev->mtd_info = mtd; + mtd->module = THIS_MODULE; + dev->cardsize = mtd->size; + + if(new_name) { + int size = 0; + char unit = ' '; + /* Since we are using a default name, make it better by adding in the + size */ + if(mtd->size < 1048576) { /* <1MB in size, show size in K */ + size = mtd->size >> 10; + unit = 'K'; + } else { + size = mtd->size >> 20; + unit = 'M'; + } + sprintf(mtd->name, "%d%cB %s", size, unit, "PCMCIA Memory card"); + } + + /* If the memory found is fits completely into the mapped PCMCIA window, + use the faster non-remapping read/write functions */ + if(dev->cardsize <= dev->win_size) { + DEBUG(1, "Using non remapping memory functions"); + + dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base; + dev->pcmcia_map.read8 = pcmcia_read8; + dev->pcmcia_map.read16 = pcmcia_read16; + dev->pcmcia_map.copy_from = pcmcia_copy_from; + dev->pcmcia_map.write8 = pcmcia_write8; + dev->pcmcia_map.write16 = pcmcia_write16; + dev->pcmcia_map.copy_to = pcmcia_copy_to; + } + + MOD_INC_USE_COUNT; + if(add_mtd_device(mtd)) { + dev->mtd_info = NULL; + MOD_DEC_USE_COUNT; + err("Couldnt register MTD device"); + pcmciamtd_release((u_long)link); + return; + } + DEBUG(1, "mtd added @ %p mtd->priv = %p", mtd, mtd->priv); + + return; + + cs_failed: + cs_error(link->handle, last_fn, last_ret); + err("CS Error, exiting"); + pcmciamtd_release((u_long)link); + return; +} + + +/* The card status event handler. Mostly, this schedules other + * stuff to run after an event is received. A CARD_REMOVAL event + * also sets some flags to discourage the driver from trying + * to talk to the card any more. + */ + +static int pcmciamtd_event(event_t event, int priority, + event_callback_args_t *args) +{ + dev_link_t *link = args->client_data; + + DEBUG(1, "event=0x%06x", event); + switch (event) { + case CS_EVENT_CARD_REMOVAL: + DEBUG(2, "EVENT_CARD_REMOVAL"); + link->state &= ~DEV_PRESENT; + if (link->state & DEV_CONFIG) + mod_timer(&link->release, jiffies + HZ/20); + break; + case CS_EVENT_CARD_INSERTION: + DEBUG(2, "EVENT_CARD_INSERTION"); + link->state |= DEV_PRESENT | DEV_CONFIG_PENDING; + pcmciamtd_config(link); + break; + case CS_EVENT_PM_SUSPEND: + DEBUG(2, "EVENT_PM_SUSPEND"); + link->state |= DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_RESET_PHYSICAL: + DEBUG(2, "EVENT_RESET_PHYSICAL"); + /* get_lock(link); */ + break; + case CS_EVENT_PM_RESUME: + DEBUG(2, "EVENT_PM_RESUME"); + link->state &= ~DEV_SUSPEND; + /* Fall through... */ + case CS_EVENT_CARD_RESET: + DEBUG(2, "EVENT_CARD_RESET"); + /* free_lock(link); */ + break; + default: + DEBUG(2, "Unknown event %d", event); + } + return 0; +} + + +/* This deletes a driver "instance". The device is de-registered + * with Card Services. If it has been released, all local data + * structures are freed. Otherwise, the structures will be freed + * when the device is released. + */ + +static void pcmciamtd_detach(dev_link_t *link) +{ + int ret; + struct pcmciamtd_dev *dev = NULL; + struct list_head *temp1, *temp2; + + DEBUG(3, "link=0x%p", link); + + /* Find device in list */ + list_for_each_safe(temp1, temp2, &dev_list) { + dev = list_entry(temp1, struct pcmciamtd_dev, list); + if(link == &dev->link) + break; + } + if(link != &dev->link) { + DEBUG(1, "Cant find %p in dev_list", link); + return; + } + + del_timer(&link->release); + + if(!dev) { + DEBUG(3, "dev is NULL"); + return; + } + + if (link->state & DEV_CONFIG) { + //pcmciamtd_release((u_long)link); + DEBUG(3, "DEV_CONFIG set"); + link->state |= DEV_STALE_LINK; + return; + } + + if (link->handle) { + DEBUG(2, "Deregistering with card services"); + ret = CardServices(DeregisterClient, link->handle); + if (ret != CS_SUCCESS) + cs_error(link->handle, DeregisterClient, ret); + } + DEBUG(3, "Freeing dev (%p)", dev); + list_del(&dev->list); + link->priv = NULL; + kfree(dev); +} + + +/* pcmciamtd_attach() creates an "instance" of the driver, allocating + * local data structures for one device. The device is registered + * with Card Services. + */ + +static dev_link_t *pcmciamtd_attach(void) +{ + struct pcmciamtd_dev *dev; + dev_link_t *link; + client_reg_t client_reg; + int ret; + + /* Create new memory card device */ + dev = kmalloc(sizeof(*dev), GFP_KERNEL); + if (!dev) return NULL; + DEBUG(1, "dev=0x%p", dev); + + memset(dev, 0, sizeof(*dev)); + link = &dev->link; link->priv = dev; + + link->release.function = &pcmciamtd_release; + link->release.data = (u_long)link; + + link->conf.Attributes = 0; + link->conf.IntType = INT_MEMORY; + + list_add(&dev->list, &dev_list); + + /* Register with Card Services */ + client_reg.dev_info = &dev_info; + client_reg.Attributes = INFO_IO_CLIENT | INFO_CARD_SHARE; + client_reg.EventMask = + CS_EVENT_RESET_PHYSICAL | CS_EVENT_CARD_RESET | + CS_EVENT_CARD_INSERTION | CS_EVENT_CARD_REMOVAL | + CS_EVENT_PM_SUSPEND | CS_EVENT_PM_RESUME; + client_reg.event_handler = &pcmciamtd_event; + client_reg.Version = 0x0210; + client_reg.event_callback_args.client_data = link; + DEBUG(2, "Calling RegisterClient"); + ret = CardServices(RegisterClient, &link->handle, &client_reg); + if (ret != 0) { + cs_error(link->handle, RegisterClient, ret); + pcmciamtd_detach(link); + return NULL; + } + + return link; +} + + +static int __init init_pcmciamtd(void) +{ + servinfo_t serv; + + info(DRIVER_DESC " " DRIVER_VERSION); + CardServices(GetCardServicesInfo, &serv); + if (serv.Revision != CS_RELEASE_CODE) { + err("Card Services release does not match!"); + return -1; + } + + if(buswidth && buswidth != 1 && buswidth != 2) { + info("bad buswidth (%d), using default", buswidth); + buswidth = 2; + } + if(force_size && (force_size < 1 || force_size > 64)) { + info("bad force_size (%d), using default", force_size); + force_size = 0; + } + if(mem_type && mem_type != 1 && mem_type != 2) { + info("bad mem_type (%d), using default", mem_type); + mem_type = 0; + } + register_pccard_driver(&dev_info, &pcmciamtd_attach, &pcmciamtd_detach); + return 0; +} + + +static void __exit exit_pcmciamtd(void) +{ + struct list_head *temp1, *temp2; + + DEBUG(1, DRIVER_DESC " unloading"); + unregister_pccard_driver(&dev_info); + list_for_each_safe(temp1, temp2, &dev_list) { + dev_link_t *link = &list_entry(temp1, struct pcmciamtd_dev, list)->link; + if (link && (link->state & DEV_CONFIG)) { + pcmciamtd_release((u_long)link); + pcmciamtd_detach(link); + } + } +} + +module_init(init_pcmciamtd); +module_exit(exit_pcmciamtd); diff --git a/drivers/mtd/maps/sa1100-flash.c b/drivers/mtd/maps/sa1100-flash.c index b6c1c0f9efe7..b2592a0a0d63 100644 --- a/drivers/mtd/maps/sa1100-flash.c +++ b/drivers/mtd/maps/sa1100-flash.c @@ -3,28 +3,35 @@ * * (C) 2000 Nicolas Pitre <nico@cam.org> * - * $Id: sa1100-flash.c,v 1.22 2001/10/02 10:04:52 rmk Exp $ + * $Id: sa1100-flash.c,v 1.28 2002/05/07 13:48:38 abz Exp $ */ #include <linux/config.h> #include <linux/module.h> #include <linux/types.h> +#include <linux/ioport.h> #include <linux/kernel.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 - -#define WINDOW_ADDR 0xe8000000 +/* + * This isnt complete yet, so... + */ +#define CONFIG_MTD_SA1100_STATICMAP 1 static __u8 sa1100_read8(struct map_info *map, unsigned long ofs) { @@ -66,33 +73,7 @@ static void sa1100_copy_to(struct map_info *map, unsigned long to, const void *f memcpy((void *)(map->map_priv_1 + to), from, len); } - -#ifdef CONFIG_SA1100_H3600 - -static void h3600_set_vpp(struct map_info *map, int vpp) -{ - if (vpp) - set_h3600_egpio(EGPIO_H3600_VPP_ON); - else - clr_h3600_egpio(EGPIO_H3600_VPP_ON); -} - -#endif - -#ifdef CONFIG_SA1100_JORNADA720 - -static void jornada720_set_vpp(int vpp) -{ - if (vpp) - PPSR |= 0x80; - else - PPSR &= ~0x80; - PPDR |= 0x80; -} - -#endif - -static struct map_info sa1100_map = { +static struct map_info sa1100_map __initdata = { name: "SA1100 flash", read8: sa1100_read8, read16: sa1100_read16, @@ -102,609 +83,1232 @@ static struct map_info sa1100_map = { write16: sa1100_write16, write32: sa1100_write32, copy_to: sa1100_copy_to, - - map_priv_1: WINDOW_ADDR, }; +#ifdef CONFIG_MTD_SA1100_STATICMAP /* * Here are partition information for all known SA1100-based devices. * See include/linux/mtd/partitions.h for definition of the mtd_partition * structure. - * - * The *_max_flash_size is the maximum possible mapped flash size which - * is not necessarily the actual flash size. It must correspond to the - * value specified in the mapping definition defined by the - * "struct map_desc *_io_desc" for the corresponding machine. + * + * Please note: + * 1. We no longer support static flash mappings via the machine io_desc + * structure. + * 2. The flash size given should be the largest flash size that can + * be accomodated. + * + * The MTD layer will detect flash chip aliasing and reduce the size of + * the map accordingly. + * + * Please keep these in alphabetical order, and formatted as per existing + * entries. Thanks. */ -#ifdef CONFIG_SA1100_ASSABET +#ifdef CONFIG_SA1100_ADSBITSY +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, + } +}; +#endif +#ifdef CONFIG_SA1100_ASSABET /* Phase 4 Assabet has two 28F160B3 flash parts in bank 0: */ -static unsigned long assabet4_max_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: */ -static unsigned long assabet5_max_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_max_flash_size assabet5_max_flash_size -#define assabet_partitions assabet5_partitions - +#define assabet_partitions assabet5_partitions #endif -#ifdef CONFIG_SA1100_FLEXANET - -/* Flexanet has two 28F128J3A flash parts in bank 0: */ -static unsigned long flexanet_max_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 - } +#ifdef CONFIG_SA1100_BADGE4 +/* + * 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) + */ +static struct mtd_partition badge4_partitions[] = { + { + name: "BLOB boot loader", + offset: 0, + size: 0x0000A000 + }, { + name: "params", + offset: MTDPART_OFS_APPEND, + size: 0x00006000 + }, { + name: "kernel", + offset: MTDPART_OFS_APPEND, + size: 0x00100000 + }, { + name: "root", + offset: MTDPART_OFS_APPEND, + size: MTDPART_SIZ_FULL + } }; - #endif -#ifdef CONFIG_SA1100_HUW_WEBPANEL -static unsigned long huw_webpanel_max_flash_size = 0x01000000; -static struct mtd_partition huw_webpanel_partitions[] = { - { - name: "Loader", - size: 0x00040000, - offset: 0, - },{ - name: "Sector 1", - size: 0x00040000, - offset: MTDPART_OFS_APPEND, - },{ - size: MTDPART_SIZ_FULL, - offset: MTDPART_OFS_APPEND, + +#ifdef CONFIG_SA1100_CERF +#ifdef CONFIG_SA1100_CERF_FLASH_32MB +static struct mtd_partition cerf_partitions[] = { + { + name: "firmware", + size: 0x00040000, + offset: 0, + }, { + name: "params", + size: 0x00040000, + offset: 0x00040000, + }, { + name: "kernel", + size: 0x00100000, + offset: 0x00080000, + }, { + name: "rootdisk", + size: 0x01E80000, + offset: 0x00180000, } }; -#endif /* CONFIG_SA1100_HUW_WEBPANEL */ - - -#ifdef CONFIG_SA1100_H3600 - -static unsigned long h3600_max_flash_size = 0x02000000; -static struct mtd_partition h3600_partitions[] = { +#elif defined CONFIG_SA1100_CERF_FLASH_16MB +static struct mtd_partition cerf_partitions[] = { { - name: "H3600 boot firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE /* force read-only */ - },{ - name: "H3600 kernel", - size: 0x00080000, - offset: 0x40000 - },{ - name: "H3600 params", - size: 0x00040000, - offset: 0xC0000 - },{ -#ifdef CONFIG_JFFS2_FS - name: "H3600 root jffs2", - offset: 0x00100000, - size: MTDPART_SIZ_FULL + name: "firmware", + size: 0x00020000, + offset: 0, + }, { + name: "params", + size: 0x00020000, + offset: 0x00020000, + }, { + name: "kernel", + size: 0x00100000, + offset: 0x00040000, + }, { + name: "rootdisk", + size: 0x00EC0000, + offset: 0x00140000, + } +}; +#elif defined CONFIG_SA1100_CERF_FLASH_8MB +# error "Unwritten type definition" #else - name: "H3600 initrd", - size: 0x00100000, - offset: 0x00100000 - },{ - name: "H3600 root cramfs", - size: 0x00300000, - offset: 0x00200000 - },{ - name: "H3600 usr cramfs", - size: 0x00800000, - offset: 0x00500000 - },{ - name: "H3600 usr local", - offset: 0x00d00000, - size: MTDPART_SIZ_FULL +# error "Undefined memory orientation for CERF in sa1100-flash.c" +#endif #endif + +#ifdef CONFIG_SA1100_CONSUS +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, + /* 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, + }, { + /* 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, } }; +#endif +#ifdef CONFIG_SA1100_FLEXANET +/* Flexanet has two 28F128J3A flash parts in bank 0: */ +#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, + } +}; #endif + #ifdef CONFIG_SA1100_FREEBIRD -static unsigned long freebird_max_flash_size = 0x02000000; static struct mtd_partition freebird_partitions[] = { #if CONFIG_SA1100_FREEBIRD_NEW - { - name: "firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE /* force read-only */ - },{ - name: "kernel", - size: 0x00080000, - offset: 0x40000 - },{ - name: "params", - size: 0x00040000, - offset: 0xC0000 - },{ - name: "initrd", - size: 0x00100000, - offset: 0x00100000 - },{ - name: "root cramfs", - size: 0x00300000, - offset: 0x00200000 - },{ - name: "usr cramfs", - size: 0x00C00000, - offset: 0x00500000 - },{ - name: "local", - offset: 0x01100000, - size: MTDPART_SIZ_FULL + { + 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 - { offset: 0, size: 0x00040000, }, - { offset: MTDPART_OFS_APPEND, size: 0x000c0000, }, - { offset: MTDPART_OFS_APPEND, size: 0x00400000, }, - { offset: MTDPART_OFS_APPEND, size: MTDPART_SIZ_FULL } + { + size: 0x00040000, + offset: 0, + }, { + size: 0x000c0000, + offset: MTDPART_OFS_APPEND, + }, { + size: 0x00400000, + offset: MTDPART_OFS_APPEND, + }, { + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } #endif - }; +}; #endif - - -#ifdef CONFIG_SA1100_CERF -static unsigned long cerf_max_flash_size = 0x01000000; -static struct mtd_partition cerf_partitions[] = { - { offset: 0, size: 0x00800000 }, - { offset: MTDPART_OFS_APPEND, size: 0x00800000 } +#ifdef CONFIG_SA1100_FRODO +/* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */ +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 + } }; - #endif #ifdef CONFIG_SA1100_GRAPHICSCLIENT - -static unsigned long graphicsclient_max_flash_size = 0x01000000; static struct mtd_partition graphicsclient_partitions[] = { - { - name: "zImage", - offset: 0, - size: 0x100000 - }, - { - name: "ramdisk.gz", - offset: MTDPART_OFS_APPEND, - size: 0x300000 - }, - { - name: "User FS", - offset: MTDPART_OFS_APPEND, - size: MTDPART_SIZ_FULL + { + 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 - -static unsigned long graphicsmaster_max_flash_size = 0x01000000; static struct mtd_partition graphicsmaster_partitions[] = { - { - name: "zImage", - offset: 0, - size: 0x100000 + { + name: "zImage", + size: 0x100000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ }, - { - name: "ramdisk.gz", - offset: MTDPART_OFS_APPEND, - size: 0x300000 + { + name: "ramdisk.gz", + size: 0x300000, + offset: MTDPART_OFS_APPEND, + mask_flags: MTD_WRITEABLE, /* force read-only */ }, - { - name: "User FS", - offset: MTDPART_OFS_APPEND, - size: MTDPART_SIZ_FULL + { + name: "User FS", + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, } }; +#endif +#ifdef CONFIG_SA1100_H3XXX +static struct mtd_partition h3xxx_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: "H3XXX params", + size: 0x00040000, + offset: 0x000C0000, + }, { +#ifdef CONFIG_JFFS2_FS + name: "H3XXX root jffs2", + size: MTDPART_SIZ_FULL, + offset: 0x00100000, +#else + name: "H3XXX initrd", + size: 0x00100000, + offset: 0x00100000, + }, { + name: "H3XXX root cramfs", + size: 0x00300000, + offset: 0x00200000, + }, { + name: "H3XXX usr cramfs", + size: 0x00800000, + offset: 0x00500000, + }, { + name: "H3XXX usr local", + size: MTDPART_SIZ_FULL, + offset: 0x00d00000, +#endif #endif + } +}; -#ifdef CONFIG_SA1100_PANGOLIN +static void h3xxx_set_vpp(struct map_info *map, int vpp) +{ + assign_h3600_egpio(IPAQ_EGPIO_VPP_ON, vpp); +} +#else +#define h3xxx_set_vpp NULL +#endif -static unsigned long pangolin_max_flash_size = 0x04000000; -static struct mtd_partition pangolin_partitions[] = { - { - name: "boot firmware", - offset: 0x00000000, - size: 0x00080000, - mask_flags: MTD_WRITEABLE, /* force read-only */ - }, - { - name: "kernel", - offset: 0x00080000, - size: 0x00100000, - }, +#ifdef CONFIG_SA1100_HUW_WEBPANEL +static struct mtd_partition huw_webpanel_partitions[] = { { - name: "initrd", - offset: 0x00180000, - size: 0x00280000, - }, + name: "Loader", + size: 0x00040000, + offset: 0, + }, { + name: "Sector 1", + size: 0x00040000, + offset: MTDPART_OFS_APPEND, + }, { + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif + +#ifdef CONFIG_SA1100_JORNADA720 +static struct mtd_partition jornada720_partitions[] = { { - name: "initrd-test", - offset: 0x00400000, - size: 0x03C00000, + 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(int vpp) +{ + if (vpp) + PPSR |= 0x80; + else + PPSR &= ~0x80; + PPDR |= 0x80; +} +#else +#define jornada720_set_vpp NULL #endif -#ifdef CONFIG_SA1100_YOPY +#ifdef CONFIG_SA1100_PANGOLIN +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, + } +}; +#endif -static unsigned long yopy_max_flash_size = 0x08000000; -static struct mtd_partition yopy_partitions[] = { +#ifdef CONFIG_SA1100_PT_SYSTEM3 +/* erase size is 0x40000 == 256k partitions have to have this boundary */ +static struct mtd_partition system3_partitions[] = { { - name: "boot firmware", - offset: 0x00000000, - size: 0x00040000, - mask_flags: MTD_WRITEABLE, /* force read-only */ + 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 +static struct mtd_partition shannon_partitions[] = { + { + name: "BLOB boot loader", + offset: 0, + size: 0x20000 }, { name: "kernel", - offset: 0x00080000, - size: 0x00080000, + offset: MTDPART_OFS_APPEND, + size: 0xe0000 }, - { + { name: "initrd", - offset: 0x00100000, - size: 0x00300000, - }, - { - name: "root", - offset: 0x00400000, - size: 0x01000000, - }, + offset: MTDPART_OFS_APPEND, + size: MTDPART_SIZ_FULL + } }; #endif -#ifdef CONFIG_SA1100_JORNADA720 - -static unsigned long jornada720_max_flash_size = 0x02000000; -static struct mtd_partition jornada720_partitions[] = { +#ifdef CONFIG_SA1100_SHERMAN +static struct mtd_partition sherman_partitions[] = { { - name: "JORNADA720 boot firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE /* force read-only */ - },{ - name: "JORNADA720 kernel", - size: 0x000c0000, - offset: 0x40000 - },{ - name: "JORNADA720 params", - size: 0x00040000, - offset: 0x100000 - },{ - 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", - offset: 0x00d00000, - size: 0 /* will expand to the end of the flash */ + size: 0x50000, + offset: 0, + }, { + size: 0x70000, + offset: MTDPART_OFS_APPEND, + }, { + size: 0x600000, + offset: MTDPART_OFS_APPEND, + }, { + size: 0xA0000, + offset: MTDPART_OFS_APPEND, } }; #endif -#ifdef CONFIG_SA1100_SHERMAN - -static unsigned long sherman_max_flash_size = 0x02000000; -static struct mtd_partition sherman_partitions[] = { - { offset: 0, size: 0x50000 }, - { offset: MTDPART_OFS_APPEND, size: 0x70000 }, - { offset: MTDPART_OFS_APPEND, size: 0x600000 }, - { offset: MTDPART_OFS_APPEND, size: 0xA0000 } -}; - +#ifdef CONFIG_SA1100_SIMPAD +static struct mtd_partition simpad_partitions[] = { + { + name: "SIMpad boot firmware", + size: 0x00080000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "SIMpad kernel", + size: 0x00100000, + offset: 0x00080000, + }, { +#ifdef CONFIG_JFFS2_FS + name: "SIMpad root jffs2", + size: MTDPART_SIZ_FULL, + offset: 0x00180000, +#else + name: "SIMpad initrd", + size: 0x00300000, + offset: 0x00180000, + }, { + name: "SIMpad root cramfs", + size: 0x00300000, + offset: 0x00480000, + }, { + name: "SIMpad usr cramfs", + size: 0x005c0000, + offset: 0x00780000, + }, { + name: "SIMpad usr local", + size: MTDPART_SIZ_FULL, + offset: 0x00d40000, #endif + } +}; +#endif /* CONFIG_SA1100_SIMPAD */ #ifdef CONFIG_SA1100_STORK - -static unsigned long stork_max_flash_size = 0x02000000; static struct mtd_partition stork_partitions[] = { { - name: "STORK boot firmware", - size: 0x00040000, - offset: 0, - mask_flags: MTD_WRITEABLE /* force read-only */ - },{ - name: "STORK params", - size: 0x00040000, - offset: 0x40000 - },{ - name: "STORK kernel", - size: 0x00100000, - offset: 0x80000 - },{ + name: "STORK boot firmware", + size: 0x00040000, + offset: 0, + mask_flags: MTD_WRITEABLE, /* force read-only */ + }, { + name: "STORK params", + size: 0x00040000, + offset: 0x00040000, + }, { + name: "STORK kernel", + size: 0x00100000, + offset: 0x00080000, + }, { #ifdef CONFIG_JFFS2_FS - name: "STORK root jffs2", - offset: 0x00180000, - size: MTDPART_SIZ_FULL + name: "STORK root jffs2", + offset: 0x00180000, + size: MTDPART_SIZ_FULL, #else - name: "STORK initrd", - size: 0x00100000, - offset: 0x00180000 - },{ - name: "STORK root cramfs", - size: 0x00300000, - offset: 0x00280000 - },{ - name: "STORK usr cramfs", - size: 0x00800000, - offset: 0x00580000 - },{ - name: "STORK usr local", - offset: 0x00d80000, - size: MTDPART_SIZ_FULL + name: "STORK initrd", + size: 0x00100000, + offset: 0x00180000, + }, { + name: "STORK root cramfs", + size: 0x00300000, + offset: 0x00280000, + }, { + name: "STORK usr cramfs", + size: 0x00800000, + offset: 0x00580000, + }, { + name: "STORK usr local", + offset: 0x00d80000, + size: MTDPART_SIZ_FULL, #endif } }; - #endif -#define NB_OF(x) (sizeof(x)/sizeof(x[0])) - - -extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); -extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); +#ifdef CONFIG_SA1100_TRIZEPS +static struct mtd_partition trizeps_partitions[] = { + { + name: "Bootloader & the kernel", + size: 0x00200000, + offset: 0, + }, { + name: "Data", + size: 0x00400000, + offset: MTDPART_OFS_APPEND, + }, { + size: MTDPART_SIZ_FULL, + offset: MTDPART_OFS_APPEND, + } +}; +#endif -static struct mtd_partition *parsed_parts; -static struct mtd_info *mymtd; +#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 -int __init sa1100_mtd_init(void) +static int __init sa1100_static_partitions(struct mtd_partition **parts) { - struct mtd_partition *parts; int nb_parts = 0; - int parsed_nr_parts = 0; - char *part_type; - - /* Default flash buswidth */ - sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4; - /* - * Static partition definition selection - */ - part_type = "static"; +#ifdef CONFIG_SA1100_ADSBITSY + if (machine_is_adsbitsy()) { + *parts = adsbitsy_partitions; + nb_parts = ARRAY_SIZE(adsbitsy_partitions); + } +#endif #ifdef CONFIG_SA1100_ASSABET if (machine_is_assabet()) { - parts = assabet_partitions; - nb_parts = NB_OF(assabet_partitions); - sa1100_map.size = assabet_max_flash_size; + *parts = assabet_partitions; + nb_parts = ARRAY_SIZE(assabet_partitions); } #endif - -#ifdef CONFIG_SA1100_HUW_WEBPANEL - if (machine_is_huw_webpanel()) { - parts = huw_webpanel_partitions; - nb_parts = NB_OF(huw_webpanel_partitions); - sa1100_map.size = huw_webpanel_max_flash_size; +#ifdef CONFIG_SA1100_BADGE4 + if (machine_is_badge4()) { + *parts = badge4_partitions; + nb_parts = ARRAY_SIZE(badge4_partitions); } #endif - -#ifdef CONFIG_SA1100_H3600 - if (machine_is_h3600()) { - parts = h3600_partitions; - nb_parts = NB_OF(h3600_partitions); - sa1100_map.size = h3600_max_flash_size; - sa1100_map.set_vpp = h3600_set_vpp; +#ifdef CONFIG_SA1100_CERF + if (machine_is_cerf()) { + *parts = cerf_partitions; + nb_parts = ARRAY_SIZE(cerf_partitions); + } +#endif +#ifdef CONFIG_SA1100_CONSUS + if (machine_is_consus()) { + *parts = consus_partitions; + nb_parts = ARRAY_SIZE(consus_partitions); + } +#endif +#ifdef CONFIG_SA1100_FLEXANET + if (machine_is_flexanet()) { + *parts = flexanet_partitions; + nb_parts = ARRAY_SIZE(flexanet_partitions); } #endif #ifdef CONFIG_SA1100_FREEBIRD if (machine_is_freebird()) { - parts = freebird_partitions; - nb_parts = NB_OF(freebird_partitions); - sa1100_map.size = freebird_max_flash_size; + *parts = freebird_partitions; + nb_parts = ARRAY_SIZE(freebird_partitions); } #endif -#ifdef CONFIG_SA1100_CERF - if (machine_is_cerf()) { - parts = cerf_partitions; - nb_parts = NB_OF(cerf_partitions); - sa1100_map.size = cerf_max_flash_size; +#ifdef CONFIG_SA1100_FRODO + if (machine_is_frodo()) { + *parts = frodo_partitions; + nb_parts = ARRAY_SIZE(frodo_partitions); } -#endif +#endif #ifdef CONFIG_SA1100_GRAPHICSCLIENT if (machine_is_graphicsclient()) { - parts = graphicsclient_partitions; - nb_parts = NB_OF(graphicsclient_partitions); - sa1100_map.size = graphicsclient_max_flash_size; - sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + *parts = graphicsclient_partitions; + nb_parts = ARRAY_SIZE(graphicsclient_partitions); } #endif #ifdef CONFIG_SA1100_GRAPHICSMASTER if (machine_is_graphicsmaster()) { - parts = graphicsmaster_partitions; - nb_parts = NB_OF(graphicsmaster_partitions); - sa1100_map.size = graphicsmaster_max_flash_size; - sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4; + *parts = graphicsmaster_partitions; + nb_parts = ARRAY_SIZE(graphicsmaster_partitions); } #endif -#ifdef CONFIG_SA1100_PANGOLIN - if (machine_is_pangolin()) { - parts = pangolin_partitions; - nb_parts = NB_OF(pangolin_partitions); - sa1100_map.size = pangolin_max_flash_size; +#ifdef CONFIG_SA1100_H3XXX + if (machine_is_h3xxx()) { + *parts = h3xxx_partitions; + nb_parts = ARRAY_SIZE(h3xxx_partitions); + } +#endif +#ifdef CONFIG_SA1100_HUW_WEBPANEL + if (machine_is_huw_webpanel()) { + *parts = huw_webpanel_partitions; + nb_parts = ARRAY_SIZE(huw_webpanel_partitions); } #endif #ifdef CONFIG_SA1100_JORNADA720 if (machine_is_jornada720()) { - parts = jornada720_partitions; - nb_parts = NB_OF(jornada720_partitions); - sa1100_map.size = jornada720_max_flash_size; - sa1100_map.set_vpp = jornada720_set_vpp; + *parts = jornada720_partitions; + nb_parts = ARRAY_SIZE(jornada720_partitions); } #endif -#ifdef CONFIG_SA1100_YOPY - if (machine_is_yopy()) { - parts = yopy_partitions; - nb_parts = NB_OF(yopy_partitions); - sa1100_map.size = yopy_max_flash_size; +#ifdef CONFIG_SA1100_PANGOLIN + if (machine_is_pangolin()) { + *parts = pangolin_partitions; + nb_parts = ARRAY_SIZE(pangolin_partitions); + } +#endif +#ifdef CONFIG_SA1100_PT_SYSTEM3 + if (machine_is_pt_system3()) { + *parts = system3_partitions; + nb_parts = ARRAY_SIZE(system3_partitions); + } +#endif +#ifdef CONFIG_SA1100_SHANNON + if (machine_is_shannon()) { + *parts = shannon_partitions; + nb_parts = ARRAY_SIZE(shannon_partitions); } #endif #ifdef CONFIG_SA1100_SHERMAN if (machine_is_sherman()) { - parts = sherman_partitions; - nb_parts = NB_OF(sherman_partitions); - sa1100_map.size = sherman_max_flash_size; + *parts = sherman_partitions; + nb_parts = ARRAY_SIZE(sherman_partitions); } #endif -#ifdef CONFIG_SA1100_FLEXANET - if (machine_is_flexanet()) { - parts = flexanet_partitions; - nb_parts = NB_OF(flexanet_partitions); - sa1100_map.size = flexanet_max_flash_size; +#ifdef CONFIG_SA1100_SIMPAD + if (machine_is_simpad()) { + *parts = simpad_partitions; + nb_parts = ARRAY_SIZE(simpad_partitions); } #endif #ifdef CONFIG_SA1100_STORK if (machine_is_stork()) { - parts = stork_partitions; - nb_parts = NB_OF(stork_partitions); - sa1100_map.size = stork_max_flash_size; + *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_parititons); + } +#endif +#ifdef CONFIG_SA1100_YOPY + if (machine_is_yopy()) { + *parts = yopy_partitions; + nb_parts = ARRAY_SIZE(yopy_partitions); } #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; + /* - * Now let's probe for the actual flash. Do it here since - * specific machine settings might have been set above. + * Allocate the map_info structs in one go. */ - printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); - mymtd = do_map_probe("cfi_probe", &sa1100_map); - if (!mymtd) - return -ENXIO; - mymtd->module = THIS_MODULE; + maps = kmalloc(sizeof(struct map_info) * nr, GFP_KERNEL); + if (!maps) + return -ENOMEM; /* - * Dynamic partition selection stuff (might override the static ones) + * Claim and then map the memory regions. */ -#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; + 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 -#ifdef CONFIG_MTD_BOOTLDR_PARTS - if (parsed_nr_parts == 0) { - int ret = parse_bootldr_partitions(mymtd, &parsed_parts); - if (ret > 0) { - part_type = "Compaq bootldr"; - parsed_nr_parts = ret; } } -#endif - if (parsed_nr_parts > 0) { - parts = parsed_parts; - nb_parts = parsed_nr_parts; + /* + * If we failed, clean up. + */ + if (ret) { + do { + if (sa[i].mtd) + map_destroy(sa[i].mtd); + if (sa[i].vbase) + iounmap(sa[i].vbase); + if (sa[i].res) + release_resource(sa[i].res); + } while (i--); + + kfree(maps); } - if (nb_parts == 0) { - printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n"); - add_mtd_device(mymtd); + return ret; +} + +static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd) +{ + int i; + + del_mtd_partitions(mtd); + + if (mtd != sa[0].mtd) + mtd_concat_destroy(mtd); + + 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); +} + +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_4M; + 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; + } + + if (nr < 0) + return nr; + + /* + * Retrieve the buswidth from the MSC registers. + * We currently only implement CS0 and CS1 here. + */ + for (i = 0; i < nr; i++) { + switch (info[i].base) { + default: + printk(KERN_WARNING "SA1100 flash: unknown base address " + "0x%08lx, assuming CS0\n", info[i].base); + case SA1100_CS0_PHYS: + info[i].width = (MSC0 & MSC_RBW) ? 2 : 4; + break; + + case SA1100_CS1_PHYS: + info[i].width = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4; + break; + } + } + + return 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 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 (nr_parts == 0) { + printk(KERN_NOTICE "SA1100 flash: no partition info " + "available, registering whole flash\n"); + add_mtd_device(mtd); } else { - printk(KERN_NOTICE "Using %s partition definition\n", part_type); - add_mtd_partitions(mymtd, parts, nb_parts); + printk(KERN_NOTICE "SA1100 flash: using %s partition " + "definition\n", part_type); + add_mtd_partitions(mtd, parsed_parts, nr_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); + + return ret; } static void __exit sa1100_mtd_cleanup(void) { - if (mymtd) { - del_mtd_partitions(mymtd); - map_destroy(mymtd); - if (parsed_parts) - kfree(parsed_parts); - } + sa1100_destroy_mtd(info, mymtd); + sa1100_destroy_partitions(); } module_init(sa1100_mtd_init); diff --git a/drivers/mtd/mtdblock.c b/drivers/mtd/mtdblock.c index 1ad148bd3364..a39bcab25891 100644 --- a/drivers/mtd/mtdblock.c +++ b/drivers/mtd/mtdblock.c @@ -295,7 +295,7 @@ static int mtdblock_open(struct inode *inode, struct file *file) spin_unlock(&mtdblks_lock); mtdblk = kmalloc(sizeof(struct mtdblk_dev), GFP_KERNEL); - disk = alloc_disk(); + disk = alloc_disk(1); if (!mtdblk || !disk) goto Enomem; memset(mtdblk, 0, sizeof(*mtdblk)); @@ -313,7 +313,6 @@ static int mtdblock_open(struct inode *inode, struct file *file) } disk->major = MAJOR_NR; disk->first_minor = dev; - disk->minor_shift = 0; disk->fops = &mtd_fops; sprintf(disk->disk_name, "mtd%d", dev); mtdblk->disk = disk; @@ -518,8 +517,6 @@ static int mtdblock_ioctl(struct inode * inode, struct file * file, switch (cmd) { case BLKFLSBUF: - if(!capable(CAP_SYS_ADMIN)) - return -EACCES; fsync_bdev(inode->i_bdev); invalidate_bdev(inode->i_bdev, 0); down(&mtdblk->cache_sem); diff --git a/drivers/mtd/mtdblock_ro.c b/drivers/mtd/mtdblock_ro.c index 65b97e3a11df..1878f540f3b6 100644 --- a/drivers/mtd/mtdblock_ro.c +++ b/drivers/mtd/mtdblock_ro.c @@ -201,8 +201,6 @@ static int mtdblock_ioctl(struct inode * inode, struct file * file, if (!mtd || cmd != BLKFLSBUF) return -EINVAL; - if(!capable(CAP_SYS_ADMIN)) - return -EACCES; fsync_bdev(inode->i_bdev); invalidate_bdev(inode->i_bdev, 0); if (mtd->sync) @@ -224,7 +222,7 @@ int __init init_mtdblock(void) int i; for (i = 0; i < MAX_MTD_DEVICES; i++) { - struct gendisk *disk = alloc_disk(); + struct gendisk *disk = alloc_disk(1); if (!disk) goto out; disk->major = MAJOR_NR; diff --git a/drivers/mtd/mtdconcat.c b/drivers/mtd/mtdconcat.c new file mode 100644 index 000000000000..4c16c0e43e0f --- /dev/null +++ b/drivers/mtd/mtdconcat.c @@ -0,0 +1,675 @@ +/* + * MTD device concatenation layer + * + * (C) 2002 Robert Kaiser <rkaiser@sysgo.de> + * + * This code is GPL + * + * $Id: mtdconcat.c,v 1.3 2002/05/21 21:04:25 dwmw2 Exp $ + */ + +#include <linux/module.h> +#include <linux/types.h> +#include <linux/kernel.h> +#include <linux/slab.h> + +#include <linux/mtd/mtd.h> +#include <linux/mtd/concat.h> + +/* + * Our storage structure: + * Subdev points to an array of pointers to struct mtd_info objects + * which is allocated along with this structure + * + */ +struct mtd_concat { + struct mtd_info mtd; + int num_subdev; + struct mtd_info **subdev; +}; + +/* + * how to calculate the size required for the above structure, + * including the pointer array subdev points to: + */ +#define SIZEOF_STRUCT_MTD_CONCAT(num_subdev) \ + ((sizeof(struct mtd_concat) + (num_subdev) * sizeof(struct mtd_info *))) + + +/* + * Given a pointer to the MTD object in the mtd_concat structure, + * we can retrieve the pointer to that structure with this macro. + */ +#define CONCAT(x) ((struct mtd_concat *)(x)) + + +/* + * MTD methods which look up the relevant subdevice, translate the + * effective address and pass through to the subdevice. + */ + +static int concat_read (struct mtd_info *mtd, loff_t from, size_t len, + size_t *retlen, u_char *buf) +{ + 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) + { + size = 0; + from -= subdev->size; + } + else + { + if (from + len > subdev->size) + size = subdev->size - from; + else + size = len; + + err = subdev->read(subdev, from, size, &retsize, buf); + + if(err) + break; + + *retlen += retsize; + len -= size; + if(len == 0) + break; + + err = -EINVAL; + buf += size; + from = 0; + } + } + return err; +} + +static int concat_write (struct mtd_info *mtd, loff_t to, size_t len, + size_t *retlen, const u_char *buf) +{ + 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 + err = subdev->write(subdev, to, size, &retsize, buf); + + if(err) + break; + + *retlen += retsize; + len -= size; + if(len == 0) + break; + + err = -EINVAL; + buf += size; + to = 0; + } + } + return err; +} + +static void concat_erase_callback (struct erase_info *instr) +{ + wake_up((wait_queue_head_t *)instr->priv); +} + +static int concat_dev_erase(struct mtd_info *mtd, struct erase_info *erase) +{ + int err; + wait_queue_head_t waitq; + DECLARE_WAITQUEUE(wait, current); + + /* + * This code was stol^H^H^H^Hinspired by mtdchar.c + */ + init_waitqueue_head(&waitq); + + erase->mtd = mtd; + erase->callback = concat_erase_callback; + erase->priv = (unsigned long)&waitq; + + /* + * FIXME: Allow INTERRUPTIBLE. Which means + * not having the wait_queue head on the stack. + */ + err = mtd->erase(mtd, erase); + if (!err) + { + set_current_state(TASK_UNINTERRUPTIBLE); + add_wait_queue(&waitq, &wait); + if (erase->state != MTD_ERASE_DONE && erase->state != MTD_ERASE_FAILED) + schedule(); + remove_wait_queue(&waitq, &wait); + set_current_state(TASK_RUNNING); + + err = (erase->state == MTD_ERASE_FAILED) ? -EIO : 0; + } + return err; +} + +static int concat_erase (struct mtd_info *mtd, struct erase_info *instr) +{ + struct mtd_concat *concat = CONCAT(mtd); + struct mtd_info *subdev; + int i, err; + u_int32_t length; + struct erase_info *erase; + + if (!(mtd->flags & MTD_WRITEABLE)) + return -EROFS; + + if(instr->addr > concat->mtd.size) + return -EINVAL; + + if(instr->len + instr->addr > concat->mtd.size) + return -EINVAL; + + /* + * Check for proper erase block alignment of the to-be-erased area. + * It is easier to do this based on the super device's erase + * region info rather than looking at each particular sub-device + * in turn. + */ + if (!concat->mtd.numeraseregions) + { /* the easy case: device has uniform erase block size */ + if(instr->addr & (concat->mtd.erasesize - 1)) + return -EINVAL; + if(instr->len & (concat->mtd.erasesize - 1)) + return -EINVAL; + } + else + { /* device has variable erase size */ + struct mtd_erase_region_info *erase_regions = concat->mtd.eraseregions; + + /* + * Find the erase region where the to-be-erased area begins: + */ + for(i = 0; i < concat->mtd.numeraseregions && + instr->addr >= erase_regions[i].offset; i++) + ; + --i; + + /* + * Now erase_regions[i] is the region in which the + * to-be-erased area begins. Verify that the starting + * offset is aligned to this region's erase size: + */ + if (instr->addr & (erase_regions[i].erasesize-1)) + return -EINVAL; + + /* + * now find the erase region where the to-be-erased area ends: + */ + for(; i < concat->mtd.numeraseregions && + (instr->addr + instr->len) >= erase_regions[i].offset ; ++i) + ; + --i; + /* + * check if the ending offset is aligned to this region's erase size + */ + if ((instr->addr + instr->len) & (erase_regions[i].erasesize-1)) + return -EINVAL; + } + + /* make a local copy of instr to avoid modifying the caller's struct */ + erase = kmalloc(sizeof(struct erase_info),GFP_KERNEL); + + if (!erase) + return -ENOMEM; + + *erase = *instr; + length = instr->len; + + /* + * find the subdevice where the to-be-erased area begins, adjust + * starting offset to be relative to the subdevice start + */ + for(i = 0; i < concat->num_subdev; i++) + { + subdev = concat->subdev[i]; + if(subdev->size <= erase->addr) + erase->addr -= subdev->size; + else + break; + } + if(i >= concat->num_subdev) /* must never happen since size */ + BUG(); /* limit has been verified above */ + + /* now do the erase: */ + err = 0; + for(;length > 0; i++) /* loop for all subevices affected by this request */ + { + subdev = concat->subdev[i]; /* get current subdevice */ + + /* limit length to subdevice's size: */ + if(erase->addr + length > subdev->size) + erase->len = subdev->size - erase->addr; + else + erase->len = length; + + if (!(subdev->flags & MTD_WRITEABLE)) + { + err = -EROFS; + break; + } + length -= erase->len; + if ((err = concat_dev_erase(subdev, erase))) + { + if(err == -EINVAL) /* sanity check: must never happen since */ + BUG(); /* block alignment has been checked above */ + break; + } + /* + * erase->addr specifies the offset of the area to be + * erased *within the current subdevice*. It can be + * non-zero only the first time through this loop, i.e. + * for the first subdevice where blocks need to be erased. + * All the following erases must begin at the start of the + * current subdevice, i.e. at offset zero. + */ + erase->addr = 0; + } + kfree(erase); + if (err) + return err; + + instr->state = MTD_ERASE_DONE; + if (instr->callback) + instr->callback(instr); + return 0; +} + +static int concat_lock (struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = -EINVAL; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + + if (ofs >= subdev->size) + { + size = 0; + ofs -= subdev->size; + } + else + { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = subdev->lock(subdev, ofs, size); + + if(err) + break; + + len -= size; + if(len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + } + return err; +} + +static int concat_unlock (struct mtd_info *mtd, loff_t ofs, size_t len) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, err = 0; + + if ((len + ofs) > mtd->size) + return -EINVAL; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + size_t size; + + if (ofs >= subdev->size) + { + size = 0; + ofs -= subdev->size; + } + else + { + if (ofs + len > subdev->size) + size = subdev->size - ofs; + else + size = len; + + err = subdev->unlock(subdev, ofs, size); + + if(err) + break; + + len -= size; + if(len == 0) + break; + + err = -EINVAL; + ofs = 0; + } + } + return err; +} + +static void concat_sync(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + subdev->sync(subdev); + } +} + +static int concat_suspend(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i, rc = 0; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + if((rc = subdev->suspend(subdev)) < 0) + return rc; + } + return rc; +} + +static void concat_resume(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + int i; + + for(i = 0; i < concat->num_subdev; i++) + { + struct mtd_info *subdev = concat->subdev[i]; + subdev->resume(subdev); + } +} + +/* + * This function constructs a virtual MTD device by concatenating + * num_devs MTD devices. A pointer to the new device object is + * stored to *new_dev upon success. This function does _not_ + * register any devices: this is the caller's responsibility. + */ +struct mtd_info *mtd_concat_create( + struct mtd_info *subdev[], /* subdevices to concatenate */ + int num_devs, /* number of subdevices */ + char *name) /* name for the new device */ +{ + int i; + size_t size; + struct mtd_concat *concat; + u_int32_t max_erasesize, curr_erasesize; + int num_erase_region; + + printk(KERN_NOTICE "Concatenating MTD devices:\n"); + for(i = 0; i < num_devs; i++) + printk(KERN_NOTICE "(%d): \"%s\"\n", i, subdev[i]->name); + printk(KERN_NOTICE "into device \"%s\"\n", name); + + /* allocate the device structure */ + size = SIZEOF_STRUCT_MTD_CONCAT(num_devs); + concat = kmalloc (size, GFP_KERNEL); + if(!concat) + { + printk ("memory allocation error while creating concatenated device \"%s\"\n", + name); + return NULL; + } + memset(concat, 0, size); + concat->subdev = (struct mtd_info **)(concat + 1); + + /* + * Set up the new "super" device's MTD object structure, check for + * incompatibilites between the subdevices. + */ + concat->mtd.type = subdev[0]->type; + concat->mtd.flags = subdev[0]->flags; + concat->mtd.size = subdev[0]->size; + concat->mtd.erasesize = subdev[0]->erasesize; + concat->mtd.oobblock = subdev[0]->oobblock; + concat->mtd.oobsize = subdev[0]->oobsize; + concat->mtd.ecctype = subdev[0]->ecctype; + concat->mtd.eccsize = subdev[0]->eccsize; + + concat->subdev[0] = subdev[0]; + + for(i = 1; i < num_devs; i++) + { + if(concat->mtd.type != subdev[i]->type) + { + kfree(concat); + printk ("Incompatible device type on \"%s\"\n", subdev[i]->name); + return NULL; + } + if(concat->mtd.flags != subdev[i]->flags) + { /* + * Expect all flags except MTD_WRITEABLE to be equal on + * all subdevices. + */ + if((concat->mtd.flags ^ subdev[i]->flags) & ~MTD_WRITEABLE) + { + kfree(concat); + printk ("Incompatible device flags on \"%s\"\n", subdev[i]->name); + return NULL; + } + else /* if writeable attribute differs, make super device writeable */ + concat->mtd.flags |= subdev[i]->flags & MTD_WRITEABLE; + } + concat->mtd.size += subdev[i]->size; + if(concat->mtd.oobblock != subdev[i]->oobblock || + concat->mtd.oobsize != subdev[i]->oobsize || + concat->mtd.ecctype != subdev[i]->ecctype || + concat->mtd.eccsize != subdev[i]->eccsize) + { + kfree(concat); + printk ("Incompatible OOB or ECC data on \"%s\"\n", subdev[i]->name); + return NULL; + } + concat->subdev[i] = subdev[i]; + + } + + concat->num_subdev = num_devs; + concat->mtd.name = name; + + /* + * NOTE: for now, we do not provide any readv()/writev() methods + * 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; + + + /* + * Combine the erase block size info of the subdevices: + * + * first, walk the map of the new device and see how + * many changes in erase size we have + */ + max_erasesize = curr_erasesize = subdev[0]->erasesize; + num_erase_region = 1; + for(i = 0; i < num_devs; i++) + { + if(subdev[i]->numeraseregions == 0) + { /* current subdevice has uniform erase size */ + if(subdev[i]->erasesize != curr_erasesize) + { /* if it differs from the last subdevice's erase size, count it */ + ++num_erase_region; + curr_erasesize = subdev[i]->erasesize; + if(curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + else + { /* current subdevice has variable erase size */ + int j; + for(j = 0; j < subdev[i]->numeraseregions; j++) + { /* walk the list of erase regions, count any changes */ + if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) + { + ++num_erase_region; + curr_erasesize = subdev[i]->eraseregions[j].erasesize; + if(curr_erasesize > max_erasesize) + max_erasesize = curr_erasesize; + } + } + } + } + + if(num_erase_region == 1) + { /* + * All subdevices have the same uniform erase size. + * This is easy: + */ + concat->mtd.erasesize = curr_erasesize; + concat->mtd.numeraseregions = 0; + } + else + { /* + * erase block size varies across the subdevices: allocate + * space to store the data describing the variable erase regions + */ + struct mtd_erase_region_info *erase_region_p; + u_int32_t begin, position; + + concat->mtd.erasesize = max_erasesize; + concat->mtd.numeraseregions = num_erase_region; + concat->mtd.eraseregions = erase_region_p = kmalloc ( + num_erase_region * sizeof(struct mtd_erase_region_info), GFP_KERNEL); + if(!erase_region_p) + { + kfree(concat); + printk ("memory allocation error while creating erase region list" + " for device \"%s\"\n", name); + return NULL; + } + + /* + * walk the map of the new device once more and fill in + * in erase region info: + */ + curr_erasesize = subdev[0]->erasesize; + begin = position = 0; + for(i = 0; i < num_devs; i++) + { + if(subdev[i]->numeraseregions == 0) + { /* current subdevice has uniform erase size */ + if(subdev[i]->erasesize != curr_erasesize) + { /* + * fill in an mtd_erase_region_info structure for the area + * we have walked so far: + */ + erase_region_p->offset = begin; + erase_region_p->erasesize = curr_erasesize; + erase_region_p->numblocks = (position - begin) / curr_erasesize; + begin = position; + + curr_erasesize = subdev[i]->erasesize; + ++erase_region_p; + } + position += subdev[i]->size; + } + else + { /* current subdevice has variable erase size */ + int j; + for(j = 0; j < subdev[i]->numeraseregions; j++) + { /* walk the list of erase regions, count any changes */ + if(subdev[i]->eraseregions[j].erasesize != curr_erasesize) + { + erase_region_p->offset = begin; + erase_region_p->erasesize = curr_erasesize; + erase_region_p->numblocks = (position - begin) / curr_erasesize; + begin = position; + + curr_erasesize = subdev[i]->eraseregions[j].erasesize; + ++erase_region_p; + } + position += subdev[i]->eraseregions[j].numblocks * curr_erasesize; + } + } + } + /* Now write the final entry */ + erase_region_p->offset = begin; + erase_region_p->erasesize = curr_erasesize; + erase_region_p->numblocks = (position - begin) / curr_erasesize; + } + + return &concat->mtd; +} + +/* + * This function destroys an MTD object obtained from concat_mtd_devs() + */ + +void mtd_concat_destroy(struct mtd_info *mtd) +{ + struct mtd_concat *concat = CONCAT(mtd); + if(concat->mtd.numeraseregions) + kfree(concat->mtd.eraseregions); + kfree(concat); +} + + +EXPORT_SYMBOL(mtd_concat_create); +EXPORT_SYMBOL(mtd_concat_destroy); + + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Robert Kaiser <rkaiser@sysgo.de>"); +MODULE_DESCRIPTION("Generic support for concatenating of MTD devices"); diff --git a/drivers/mtd/nftlcore.c b/drivers/mtd/nftlcore.c index 60d26b10740e..292894af8252 100644 --- a/drivers/mtd/nftlcore.c +++ b/drivers/mtd/nftlcore.c @@ -74,7 +74,7 @@ static void NFTL_setup(struct mtd_info *mtd) } nftl = kmalloc(sizeof(struct NFTLrecord), GFP_KERNEL); - gd = alloc_disk(); + gd = alloc_disk(1 << NFTL_PARTN_BITS); if (!nftl || !gd) { kfree(nftl); put_disk(gd); @@ -132,7 +132,6 @@ static void NFTL_setup(struct mtd_info *mtd) sprintf(gd->disk_name, "nftl%c", 'a' + firstfree); gd->major = MAJOR_NR; gd->first_minor = firstfree << NFTL_PARTN_BITS; - gd->minor_shift = NFTL_PARTN_BITS; set_capacity(gd, nftl->nr_sects); nftl->disk = gd; add_disk(gd); @@ -771,7 +770,6 @@ static int nftl_ioctl(struct inode * inode, struct file * file, unsigned int cmd return copy_to_user((void *)arg, &g, sizeof g) ? -EFAULT : 0; } case BLKFLSBUF: - if (!capable(CAP_SYS_ADMIN)) return -EACCES; fsync_bdev(inode->i_bdev); invalidate_bdev(inode->i_bdev, 0); if (nftl->mtd->sync) |
