Commit 4c34d455 authored by Linus Torvalds's avatar Linus Torvalds

Merge bk://bk.arm.linux.org.uk

into home.transmeta.com:/home/torvalds/v2.5/linux
parents 98378c38 0ebd6ce0
...@@ -21,11 +21,18 @@ CONFIG_MTD_PARTITIONS ...@@ -21,11 +21,18 @@ CONFIG_MTD_PARTITIONS
devices. Partitioning on NFTL 'devices' is a different - that's the devices. Partitioning on NFTL 'devices' is a different - that's the
'normal' form of partitioning used on a block device. '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 CONFIG_MTD_REDBOOT_PARTS
RedBoot is a ROM monitor and bootloader which deals with multiple RedBoot is a ROM monitor and bootloader which deals with multiple
'images' in flash devices by putting a table in the last erase block 'images' in flash devices by putting a table in the last erase
of the device, similar to a partition table, which gives the block of the device, similar to a partition table, which gives
offsets, lengths and names of all the images stored in the flash. 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 If you need code which can detect and parse this table, and register
MTD 'partitions' corresponding to each image in the table, enable MTD 'partitions' corresponding to each image in the table, enable
...@@ -36,21 +43,40 @@ CONFIG_MTD_REDBOOT_PARTS ...@@ -36,21 +43,40 @@ CONFIG_MTD_REDBOOT_PARTS
SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for
example. example.
CONFIG_MTD_BOOTLDR_PARTS CONFIG_MTD_CMDLINE_PARTS
The Compaq bootldr deals with multiple 'images' in flash devices Allow generic configuration of the MTD paritition tables via the kernel
by putting a table in one of the first erase blocks of the device, command line. Multiple flash resources are supported for hardware where
similar to a partition table, which gives the offsets, lengths and different kinds of flash memory are available.
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.
You will still need the parsing functions to be called by the driver 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
SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for SA1100 map driver (CONFIG_MTD_SA1100) has an option for this, for
example. 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 CONFIG_MTD_AFS_PARTS
The ARM Firmware Suite allows the user to divide flash devices into The ARM Firmware Suite allows the user to divide flash devices into
multiple 'images'. Each such image has a header containing its name multiple 'images'. Each such image has a header containing its name
......
# $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 mainmenu_option next_comment
comment 'Memory Technology Devices (MTD)' comment 'Memory Technology Devices (MTD)'
...@@ -12,9 +12,10 @@ if [ "$CONFIG_MTD" = "y" -o "$CONFIG_MTD" = "m" ]; then ...@@ -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 int ' Debugging verbosity (0 = quiet, 3 = noisy)' CONFIG_MTD_DEBUG_VERBOSE 0
fi fi
dep_tristate ' MTD partitioning support' CONFIG_MTD_PARTITIONS $CONFIG_MTD 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 ' 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 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 dep_tristate ' ARM Firmware Suite partition parsing' CONFIG_MTD_AFS_PARTS $CONFIG_MTD_PARTITIONS
fi fi
......
# #
# Makefile for the memory technology device drivers. # Makefile for the memory technology device drivers.
# #
# # Based on:
# $Id: Makefile,v 1.63 2001/06/13 09:43:07 dwmw2 Exp $ # $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/
...@@ -26,9 +26,10 @@ obj-y += chips/ maps/ devices/ nand/ ...@@ -26,9 +26,10 @@ obj-y += chips/ maps/ devices/ nand/
# Core functionality. # Core functionality.
obj-$(CONFIG_MTD) += mtdcore.o obj-$(CONFIG_MTD) += mtdcore.o
obj-$(CONFIG_MTD_CONCAT) += mtdconcat.o
obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o obj-$(CONFIG_MTD_PARTITIONS) += mtdpart.o
obj-$(CONFIG_MTD_REDBOOT_PARTS) += redboot.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 obj-$(CONFIG_MTD_AFS_PARTS) += afs.o
# 'Users' - code which presents functionality to userspace. # 'Users' - code which presents functionality to userspace.
......
/*
* 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");
/*
* $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");
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 CONFIG_MTD_SA1100
This enables access to the flash chips on most platforms based on This enables access to the flash chips on most platforms based on
the SA1100 and SA1110, including the Assabet and the Compaq iPAQ. the SA1100 and SA1110, including the Assabet and the Compaq iPAQ.
...@@ -39,6 +68,12 @@ CONFIG_MTD_SUN_UFLASH ...@@ -39,6 +68,12 @@ CONFIG_MTD_SUN_UFLASH
CONFIG_MTD_NORA CONFIG_MTD_NORA
If you had to ask, you don't have one. Say 'N'. 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 CONFIG_MTD_PNC2000
PNC-2000 is the name of Network Camera product from PHOTRON PNC-2000 is the name of Network Camera product from PHOTRON
Ltd. in Japan. It uses CFI-compliant flash. Ltd. in Japan. It uses CFI-compliant flash.
...@@ -50,6 +85,13 @@ CONFIG_MTD_RPXLITE ...@@ -50,6 +85,13 @@ CONFIG_MTD_RPXLITE
to communicate with the chips on the RPXLite board. More at to communicate with the chips on the RPXLite board. More at
<http://www.embeddedplanet.com/rpx_lite_specification_sheet.htm>. <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 CONFIG_MTD_SC520CDP
The SC520 CDP board has two banks of CFI-compliant chips and one The SC520 CDP board has two banks of CFI-compliant chips and one
Dual-in-line JEDEC chip. This 'mapping' driver supports that Dual-in-line JEDEC chip. This 'mapping' driver supports that
...@@ -78,6 +120,11 @@ CONFIG_MTD_NETSC520 ...@@ -78,6 +120,11 @@ CONFIG_MTD_NETSC520
demonstration board. If you have one of these boards and would like demonstration board. If you have one of these boards and would like
to use the flash chips on it, say 'Y'. 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 CONFIG_MTD_ELAN_104NC
This provides a driver for the on-board flash of the Arcom Control This provides a driver for the on-board flash of the Arcom Control
System's ELAN-104NC development board. By default the flash System's ELAN-104NC development board. By default the flash
...@@ -91,17 +138,17 @@ CONFIG_MTD_DC21285 ...@@ -91,17 +138,17 @@ CONFIG_MTD_DC21285
<http://developer.intel.com/design/bridge/quicklist/dsc-21285.htm>. <http://developer.intel.com/design/bridge/quicklist/dsc-21285.htm>.
CONFIG_MTD_CSTM_MIPS_IXX CONFIG_MTD_CSTM_MIPS_IXX
This provides a mapping driver for the Integrated Tecnology Express, This provides a mapping driver for the Integrated Tecnology
Inc (ITE) QED-4N-S01B eval board and the Globespan IVR Reference Express, Inc (ITE) QED-4N-S01B eval board and the Globespan IVR
Board. It provides the necessary addressing, length, buswidth, vpp Reference Board. It provides the necessary addressing, length,
code and addition setup of the flash device for these boards. In buswidth, vpp code and addition setup of the flash device for
addition, this mapping driver can be used for other boards via these boards. In addition, this mapping driver can be used for
setting of the CONFIG_MTD_CSTM_MIPS_IXX_START/LEN/BUSWIDTH other boards via setting of the CONFIG_MTD_CSTM_MIPS_IXX_START/
parameters. This mapping will provide one mtd device using one LEN/BUSWIDTH parameters. This mapping will provide one mtd device
partition. The start address can be offset from the beginning of using one partition. The start address can be offset from the
flash and the len can be less than the total flash device size to beginning of flash and the len can be less than the total flash
allow a window into the flash. Both CFI and JEDEC probes are device size to allow a window into the flash. Both CFI and JEDEC
called. probes are called.
CONFIG_MTD_CSTM_MIPS_IXX_START CONFIG_MTD_CSTM_MIPS_IXX_START
This is the physical memory location that the MTD driver will This is the physical memory location that the MTD driver will
...@@ -141,6 +188,11 @@ CONFIG_MTD_OCTAGON ...@@ -141,6 +188,11 @@ CONFIG_MTD_OCTAGON
Computer. More information on the board is available at Computer. More information on the board is available at
<http://www.octagonsystems.com/Products/5066/5066.html>. <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 CONFIG_MTD_VMAX
This provides a 'mapping' driver which supports the way in which This provides a 'mapping' driver which supports the way in which
the flash chips are connected in the Tempustech VMAX SBC301 Single the flash chips are connected in the Tempustech VMAX SBC301 Single
...@@ -151,29 +203,18 @@ CONFIG_MTD_CFI_FLAGADM ...@@ -151,29 +203,18 @@ 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. 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 CONFIG_MTD_SOLUTIONENGINE
This enables access to the flash chips on the Hitachi SolutionEngine and 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. similar boards. Say 'Y' if you are building a kernel for such a board.
CONFIG_MTD_TQM8XXL CONFIG_MTD_EPXA10DB
The TQM8xxL PowerPC board has up to two banks of CFI-compliant This enables support for the flash devices on the Altera
chips, currently uses AMD one. This 'mapping' driver supports Excalibur XA10 Development Board. If you are building a kernel
that arrangement, allowing the CFI probe and command set driver for on of these boards then you should say 'Y' otherwise say 'N'.
code to communicate with the chips on the TQM8xxL board. More at
<http://www.denx.de/embedded-ppc-en.html>. 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.
...@@ -56,8 +56,18 @@ if [ "$CONFIG_ARM" = "y" ]; then ...@@ -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 ' 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 ' 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 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 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 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 endmenu
...@@ -10,12 +10,15 @@ obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o ...@@ -10,12 +10,15 @@ obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o
obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o obj-$(CONFIG_MTD_CSTM_MIPS_IXX) += cstm_mips_ixx.o
obj-$(CONFIG_MTD_DC21285) += dc21285.o obj-$(CONFIG_MTD_DC21285) += dc21285.o
obj-$(CONFIG_MTD_ELAN_104NC) += elan-104nc.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_IQ80310) += iq80310.o
obj-$(CONFIG_MTD_L440GX) += l440gx.o obj-$(CONFIG_MTD_L440GX) += l440gx.o
obj-$(CONFIG_MTD_NORA) += nora.o obj-$(CONFIG_MTD_NORA) += nora.o
obj-$(CONFIG_MTD_CEIVA) += ceiva.o
obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o obj-$(CONFIG_MTD_OCTAGON) += octagon-5066.o
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
obj-$(CONFIG_MTD_PNC2000) += pnc2000.o obj-$(CONFIG_MTD_PNC2000) += pnc2000.o
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o obj-$(CONFIG_MTD_RPXLITE) += rpxlite.o
obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o obj-$(CONFIG_MTD_TQM8XXL) += tqm8xxl.o
obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o
...@@ -28,5 +31,10 @@ obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o ...@@ -28,5 +31,10 @@ obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o obj-$(CONFIG_MTD_DBOX2) += dbox2-flash.o
obj-$(CONFIG_MTD_OCELOT) += ocelot.o obj-$(CONFIG_MTD_OCELOT) += ocelot.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.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 include $(TOPDIR)/Rules.make
/*
* 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");
/*
* 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");
...@@ -5,9 +5,9 @@ ...@@ -5,9 +5,9 @@
* *
* This code is GPL * 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/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/kernel.h> #include <linux/kernel.h>
...@@ -44,15 +44,15 @@ void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize ...@@ -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) void dc21285_write8(struct map_info *map, __u8 d, unsigned long adr)
{ {
*CSR_ROMWRITEREG = adr; *CSR_ROMWRITEREG = adr & 3;
adr &= ~3; adr &= ~3;
*(__u8*)(map->map_priv_1 + adr) = d; *(__u8*)(map->map_priv_1 + adr) = d;
} }
void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr) void dc21285_write16(struct map_info *map, __u16 d, unsigned long adr)
{ {
*CSR_ROMWRITEREG = adr; *CSR_ROMWRITEREG = adr & 3;
adr &= ~1; adr &= ~3;
*(__u16*)(map->map_priv_1 + adr) = d; *(__u16*)(map->map_priv_1 + adr) = d;
} }
...@@ -131,7 +131,7 @@ int __init init_dc21285(void) ...@@ -131,7 +131,7 @@ int __init init_dc21285(void)
dc21285_map.buswidth*8); dc21285_map.buswidth*8);
/* Let's map the flash area */ /* 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) { if (!dc21285_map.map_priv_1) {
printk("Failed to ioremap\n"); printk("Failed to ioremap\n");
return -EIO; return -EIO;
...@@ -139,21 +139,22 @@ int __init init_dc21285(void) ...@@ -139,21 +139,22 @@ int __init init_dc21285(void)
mymtd = do_map_probe("cfi_probe", &dc21285_map); mymtd = do_map_probe("cfi_probe", &dc21285_map);
if (mymtd) { if (mymtd) {
int nrparts; int nrparts = 0;
mymtd->module = THIS_MODULE; mymtd->module = THIS_MODULE;
/* partition fixup */ /* partition fixup */
#ifdef CONFIG_MTD_REDBOOT_PARTS
nrparts = parse_redboot_partitions(mymtd, &dc21285_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"); printk(KERN_NOTICE "RedBoot partition table failed\n");
iounmap((void *)dc21285_map.map_priv_1); add_mtd_device(mymtd);
return -ENXIO;
} }
add_mtd_partitions(mymtd, dc21285_parts, nrparts);
/* /*
* Flash timing is determined with bits 19-16 of the * Flash timing is determined with bits 19-16 of the
* CSR_SA110_CNTL. The value is the number of wait cycles, or * CSR_SA110_CNTL. The value is the number of wait cycles, or
......
/*
* $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");
/*
* 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");
/* 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");
/*
* $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");
/* /*
* $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 * Mapping for the Intel XScale IQ80310 evaluation board
* *
...@@ -116,7 +116,7 @@ static int __init init_iq80310(void) ...@@ -116,7 +116,7 @@ static int __init init_iq80310(void)
int parsed_nr_parts = 0; int parsed_nr_parts = 0;
char *part_type = "static"; 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) { if (!iq80310_map.map_priv_1) {
printk("Failed to ioremap\n"); printk("Failed to ioremap\n");
return -EIO; return -EIO;
...@@ -161,7 +161,6 @@ static void __exit cleanup_iq80310(void) ...@@ -161,7 +161,6 @@ static void __exit cleanup_iq80310(void)
} }
if (iq80310_map.map_priv_1) if (iq80310_map.map_priv_1)
iounmap((void *)iq80310_map.map_priv_1); iounmap((void *)iq80310_map.map_priv_1);
return 0;
} }
module_init(init_iq80310); module_init(init_iq80310);
......
/*
* 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);
/*
* $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);
...@@ -3,28 +3,35 @@ ...@@ -3,28 +3,35 @@
* *
* (C) 2000 Nicolas Pitre <nico@cam.org> * (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/config.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/types.h> #include <linux/types.h>
#include <linux/ioport.h>
#include <linux/kernel.h> #include <linux/kernel.h>
#include <linux/mtd/mtd.h> #include <linux/mtd/mtd.h>
#include <linux/mtd/map.h> #include <linux/mtd/map.h>
#include <linux/mtd/partitions.h> #include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>
#include <asm/hardware.h> #include <asm/hardware.h>
#include <asm/mach-types.h>
#include <asm/io.h> #include <asm/io.h>
#include <asm/sizes.h>
#include <asm/arch/h3600.h>
#ifndef CONFIG_ARCH_SA1100 #ifndef CONFIG_ARCH_SA1100
#error This is for SA1100 architecture only #error This is for SA1100 architecture only
#endif #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) 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 ...@@ -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); memcpy((void *)(map->map_priv_1 + to), from, len);
} }
static struct map_info sa1100_map __initdata = {
#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 = {
name: "SA1100 flash", name: "SA1100 flash",
read8: sa1100_read8, read8: sa1100_read8,
read16: sa1100_read16, read16: sa1100_read16,
...@@ -102,609 +83,1232 @@ static struct map_info sa1100_map = { ...@@ -102,609 +83,1232 @@ static struct map_info sa1100_map = {
write16: sa1100_write16, write16: sa1100_write16,
write32: sa1100_write32, write32: sa1100_write32,
copy_to: sa1100_copy_to, 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. * Here are partition information for all known SA1100-based devices.
* See include/linux/mtd/partitions.h for definition of the mtd_partition * See include/linux/mtd/partitions.h for definition of the mtd_partition
* structure. * structure.
* *
* The *_max_flash_size is the maximum possible mapped flash size which * Please note:
* is not necessarily the actual flash size. It must correspond to the * 1. We no longer support static flash mappings via the machine io_desc
* value specified in the mapping definition defined by the * structure.
* "struct map_desc *_io_desc" for the corresponding machine. * 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: */ /* 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[] = { static struct mtd_partition assabet4_partitions[] = {
{ {
name: "bootloader", name: "bootloader",
size: 0x00020000, size: 0x00020000,
offset: 0, offset: 0,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "bootloader params", name: "bootloader params",
size: 0x00020000, size: 0x00020000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "jffs", name: "jffs",
size: MTDPART_SIZ_FULL, size: MTDPART_SIZ_FULL,
offset: MTDPART_OFS_APPEND offset: MTDPART_OFS_APPEND,
} }
}; };
/* Phase 5 Assabet has two 28F128J3A flash parts in bank 0: */ /* 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[] = { static struct mtd_partition assabet5_partitions[] = {
{ {
name: "bootloader", name: "bootloader",
size: 0x00040000, size: 0x00040000,
offset: 0, offset: 0,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "bootloader params", name: "bootloader params",
size: 0x00040000, size: 0x00040000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "jffs", name: "jffs",
size: MTDPART_SIZ_FULL, size: MTDPART_SIZ_FULL,
offset: MTDPART_OFS_APPEND 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_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_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,
}
};
#elif defined CONFIG_SA1100_CERF_FLASH_16MB
static struct mtd_partition cerf_partitions[] = {
{
name: "firmware",
size: 0x00020000,
offset: 0,
}, {
name: "params",
size: 0x00020000,
offset: 0x00020000,
}, {
name: "kernel",
size: 0x00100000,
offset: 0x00040000,
}, {
name: "rootdisk",
size: 0x00EC0000,
offset: 0x00140000,
}
};
#elif defined CONFIG_SA1100_CERF_FLASH_8MB
# error "Unwritten type definition"
#else
# error "Undefined memory orientation for CERF in sa1100-flash.c"
#endif
#endif
#ifdef CONFIG_SA1100_CONSUS
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 #endif
#ifdef CONFIG_SA1100_FLEXANET #ifdef CONFIG_SA1100_FLEXANET
/* Flexanet has two 28F128J3A flash parts in bank 0: */ /* Flexanet has two 28F128J3A flash parts in bank 0: */
static unsigned long flexanet_max_flash_size = 0x02000000; #define FLEXANET_FLASH_SIZE 0x02000000
static struct mtd_partition flexanet_partitions[] = { static struct mtd_partition flexanet_partitions[] = {
{ {
name: "bootloader", name: "bootloader",
size: 0x00040000, size: 0x00040000,
offset: 0, offset: 0,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "bootloader params", name: "bootloader params",
size: 0x00040000, size: 0x00040000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "kernel", name: "kernel",
size: 0x000C0000, size: 0x000C0000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "altkernel", name: "altkernel",
size: 0x000C0000, size: 0x000C0000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "root", name: "root",
size: 0x00400000, size: 0x00400000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "free1", name: "free1",
size: 0x00300000, size: 0x00300000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "free2", name: "free2",
size: 0x00300000, size: 0x00300000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
},{ }, {
name: "free3", name: "free3",
size: MTDPART_SIZ_FULL, size: MTDPART_SIZ_FULL,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
mask_flags: MTD_WRITEABLE mask_flags: MTD_WRITEABLE,
}
};
#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,
} }
}; };
#endif /* CONFIG_SA1100_HUW_WEBPANEL */
#ifdef CONFIG_SA1100_H3600
static unsigned long h3600_max_flash_size = 0x02000000;
static struct mtd_partition h3600_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
#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
#endif #endif
}
};
#endif
#ifdef CONFIG_SA1100_FREEBIRD #ifdef CONFIG_SA1100_FREEBIRD
static unsigned long freebird_max_flash_size = 0x02000000;
static struct mtd_partition freebird_partitions[] = { static struct mtd_partition freebird_partitions[] = {
#if CONFIG_SA1100_FREEBIRD_NEW #if CONFIG_SA1100_FREEBIRD_NEW
{ {
name: "firmware", name: "firmware",
size: 0x00040000, size: 0x00040000,
offset: 0, offset: 0,
mask_flags: MTD_WRITEABLE /* force read-only */ mask_flags: MTD_WRITEABLE, /* force read-only */
},{ }, {
name: "kernel", name: "kernel",
size: 0x00080000, size: 0x00080000,
offset: 0x40000 offset: 0x00040000,
},{ }, {
name: "params", name: "params",
size: 0x00040000, size: 0x00040000,
offset: 0xC0000 offset: 0x000C0000,
},{ }, {
name: "initrd", name: "initrd",
size: 0x00100000, size: 0x00100000,
offset: 0x00100000 offset: 0x00100000,
},{ }, {
name: "root cramfs", name: "root cramfs",
size: 0x00300000, size: 0x00300000,
offset: 0x00200000 offset: 0x00200000,
},{ }, {
name: "usr cramfs", name: "usr cramfs",
size: 0x00C00000, size: 0x00C00000,
offset: 0x00500000 offset: 0x00500000,
},{ }, {
name: "local", name: "local",
size: MTDPART_SIZ_FULL,
offset: 0x01100000, offset: 0x01100000,
size: MTDPART_SIZ_FULL
} }
#else #else
{ offset: 0, size: 0x00040000, }, {
{ offset: MTDPART_OFS_APPEND, size: 0x000c0000, }, size: 0x00040000,
{ offset: MTDPART_OFS_APPEND, size: 0x00400000, }, offset: 0,
{ offset: MTDPART_OFS_APPEND, size: MTDPART_SIZ_FULL } }, {
size: 0x000c0000,
offset: MTDPART_OFS_APPEND,
}, {
size: 0x00400000,
offset: MTDPART_OFS_APPEND,
}, {
size: MTDPART_SIZ_FULL,
offset: MTDPART_OFS_APPEND,
}
#endif #endif
}; };
#endif #endif
#ifdef CONFIG_SA1100_FRODO
#ifdef CONFIG_SA1100_CERF /* Frodo has 2 x 16M 28F128J3A flash chips in bank 0: */
static struct mtd_partition frodo_partitions[] =
static unsigned long cerf_max_flash_size = 0x01000000; {
static struct mtd_partition cerf_partitions[] = { {
{ offset: 0, size: 0x00800000 }, name: "bootloader",
{ offset: MTDPART_OFS_APPEND, size: 0x00800000 } 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 #endif
#ifdef CONFIG_SA1100_GRAPHICSCLIENT #ifdef CONFIG_SA1100_GRAPHICSCLIENT
static unsigned long graphicsclient_max_flash_size = 0x01000000;
static struct mtd_partition graphicsclient_partitions[] = { static struct mtd_partition graphicsclient_partitions[] = {
{ {
name: "zImage", name: "zImage",
size: 0x100000,
offset: 0, offset: 0,
size: 0x100000 mask_flags: MTD_WRITEABLE, /* force read-only */
}, }, {
{
name: "ramdisk.gz", name: "ramdisk.gz",
size: 0x300000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
size: 0x300000 mask_flags: MTD_WRITEABLE, /* force read-only */
}, }, {
{
name: "User FS", name: "User FS",
size: MTDPART_SIZ_FULL,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
size: MTDPART_SIZ_FULL
} }
}; };
#endif #endif
#ifdef CONFIG_SA1100_GRAPHICSMASTER #ifdef CONFIG_SA1100_GRAPHICSMASTER
static unsigned long graphicsmaster_max_flash_size = 0x01000000;
static struct mtd_partition graphicsmaster_partitions[] = { static struct mtd_partition graphicsmaster_partitions[] = {
{ {
name: "zImage", name: "zImage",
size: 0x100000,
offset: 0, offset: 0,
size: 0x100000 mask_flags: MTD_WRITEABLE, /* force read-only */
}, },
{ {
name: "ramdisk.gz", name: "ramdisk.gz",
size: 0x300000,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
size: 0x300000 mask_flags: MTD_WRITEABLE, /* force read-only */
}, },
{ {
name: "User FS", name: "User FS",
size: MTDPART_SIZ_FULL,
offset: MTDPART_OFS_APPEND, offset: MTDPART_OFS_APPEND,
size: MTDPART_SIZ_FULL
} }
}; };
#endif #endif
#ifdef CONFIG_SA1100_PANGOLIN #ifdef CONFIG_SA1100_H3XXX
static struct mtd_partition h3xxx_partitions[] = {
static unsigned long pangolin_max_flash_size = 0x04000000;
static struct mtd_partition pangolin_partitions[] = {
{ {
name: "boot firmware", name: "H3XXX boot firmware",
offset: 0x00000000, size: 0x00040000,
size: 0x00080000, offset: 0,
mask_flags: MTD_WRITEABLE, /* force read-only */ mask_flags: MTD_WRITEABLE, /* force read-only */
}, }, {
{ #ifdef CONFIG_MTD_2PARTS_IPAQ
name: "kernel", name: "H3XXX root jffs2",
offset: 0x00080000, 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, size: 0x00100000,
}, offset: 0x00100000,
{ }, {
name: "initrd", name: "H3XXX root cramfs",
offset: 0x00180000, size: 0x00300000,
size: 0x00280000, offset: 0x00200000,
}, }, {
{ name: "H3XXX usr cramfs",
name: "initrd-test", size: 0x00800000,
offset: 0x00400000, offset: 0x00500000,
size: 0x03C00000, }, {
name: "H3XXX usr local",
size: MTDPART_SIZ_FULL,
offset: 0x00d00000,
#endif
#endif
} }
}; };
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 #endif
#ifdef CONFIG_SA1100_YOPY #ifdef CONFIG_SA1100_HUW_WEBPANEL
static struct mtd_partition huw_webpanel_partitions[] = {
static unsigned long yopy_max_flash_size = 0x08000000;
static struct mtd_partition yopy_partitions[] = {
{ {
name: "boot firmware", name: "Loader",
offset: 0x00000000,
size: 0x00040000, size: 0x00040000,
mask_flags: MTD_WRITEABLE, /* force read-only */ offset: 0,
}, }, {
{ name: "Sector 1",
name: "kernel", size: 0x00040000,
offset: 0x00080000, offset: MTDPART_OFS_APPEND,
size: 0x00080000, }, {
}, size: MTDPART_SIZ_FULL,
{ offset: MTDPART_OFS_APPEND,
name: "initrd", }
offset: 0x00100000,
size: 0x00300000,
},
{
name: "root",
offset: 0x00400000,
size: 0x01000000,
},
}; };
#endif #endif
#ifdef CONFIG_SA1100_JORNADA720 #ifdef CONFIG_SA1100_JORNADA720
static unsigned long jornada720_max_flash_size = 0x02000000;
static struct mtd_partition jornada720_partitions[] = { static struct mtd_partition jornada720_partitions[] = {
{ {
name: "JORNADA720 boot firmware", name: "JORNADA720 boot firmware",
size: 0x00040000, size: 0x00040000,
offset: 0, offset: 0,
mask_flags: MTD_WRITEABLE /* force read-only */ mask_flags: MTD_WRITEABLE, /* force read-only */
},{ }, {
name: "JORNADA720 kernel", name: "JORNADA720 kernel",
size: 0x000c0000, size: 0x000c0000,
offset: 0x40000 offset: 0x00040000,
},{ }, {
name: "JORNADA720 params", name: "JORNADA720 params",
size: 0x00040000, size: 0x00040000,
offset: 0x100000 offset: 0x00100000,
},{ }, {
name: "JORNADA720 initrd", name: "JORNADA720 initrd",
size: 0x00100000, size: 0x00100000,
offset: 0x00140000 offset: 0x00140000,
},{ }, {
name: "JORNADA720 root cramfs", name: "JORNADA720 root cramfs",
size: 0x00300000, size: 0x00300000,
offset: 0x00240000 offset: 0x00240000,
},{ }, {
name: "JORNADA720 usr cramfs", name: "JORNADA720 usr cramfs",
size: 0x00800000, size: 0x00800000,
offset: 0x00540000 offset: 0x00540000,
},{ }, {
name: "JORNADA720 usr local", name: "JORNADA720 usr local",
offset: 0x00d00000,
size: 0 /* will expand to the end of the flash */ 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 #endif
#ifdef CONFIG_SA1100_SHERMAN #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 sherman_max_flash_size = 0x02000000; #ifdef CONFIG_SA1100_PT_SYSTEM3
/* erase size is 0x40000 == 256k partitions have to have this boundary */
static struct mtd_partition system3_partitions[] = {
{
name: "BLOB",
size: 0x00040000,
offset: 0x00000000,
mask_flags: MTD_WRITEABLE, /* force read-only */
}, {
name: "config",
size: 0x00040000,
offset: MTDPART_OFS_APPEND,
}, {
name: "kernel",
size: 0x00100000,
offset: MTDPART_OFS_APPEND,
}, {
name: "root",
size: MTDPART_SIZ_FULL,
offset: MTDPART_OFS_APPEND,
}
};
#endif
#ifdef CONFIG_SA1100_SHANNON
static struct mtd_partition shannon_partitions[] = {
{
name: "BLOB boot loader",
offset: 0,
size: 0x20000
},
{
name: "kernel",
offset: MTDPART_OFS_APPEND,
size: 0xe0000
},
{
name: "initrd",
offset: MTDPART_OFS_APPEND,
size: MTDPART_SIZ_FULL
}
};
#endif
#ifdef CONFIG_SA1100_SHERMAN
static struct mtd_partition sherman_partitions[] = { static struct mtd_partition sherman_partitions[] = {
{ offset: 0, size: 0x50000 }, {
{ offset: MTDPART_OFS_APPEND, size: 0x70000 }, size: 0x50000,
{ offset: MTDPART_OFS_APPEND, size: 0x600000 }, offset: 0,
{ offset: MTDPART_OFS_APPEND, size: 0xA0000 } }, {
size: 0x70000,
offset: MTDPART_OFS_APPEND,
}, {
size: 0x600000,
offset: MTDPART_OFS_APPEND,
}, {
size: 0xA0000,
offset: MTDPART_OFS_APPEND,
}
}; };
#endif
#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
}
};
#endif /* CONFIG_SA1100_SIMPAD */
#ifdef CONFIG_SA1100_STORK #ifdef CONFIG_SA1100_STORK
static unsigned long stork_max_flash_size = 0x02000000;
static struct mtd_partition stork_partitions[] = { static struct mtd_partition stork_partitions[] = {
{ {
name: "STORK boot firmware", name: "STORK boot firmware",
size: 0x00040000, size: 0x00040000,
offset: 0, offset: 0,
mask_flags: MTD_WRITEABLE /* force read-only */ mask_flags: MTD_WRITEABLE, /* force read-only */
},{ }, {
name: "STORK params", name: "STORK params",
size: 0x00040000, size: 0x00040000,
offset: 0x40000 offset: 0x00040000,
},{ }, {
name: "STORK kernel", name: "STORK kernel",
size: 0x00100000, size: 0x00100000,
offset: 0x80000 offset: 0x00080000,
},{ }, {
#ifdef CONFIG_JFFS2_FS #ifdef CONFIG_JFFS2_FS
name: "STORK root jffs2", name: "STORK root jffs2",
offset: 0x00180000, offset: 0x00180000,
size: MTDPART_SIZ_FULL size: MTDPART_SIZ_FULL,
#else #else
name: "STORK initrd", name: "STORK initrd",
size: 0x00100000, size: 0x00100000,
offset: 0x00180000 offset: 0x00180000,
},{ }, {
name: "STORK root cramfs", name: "STORK root cramfs",
size: 0x00300000, size: 0x00300000,
offset: 0x00280000 offset: 0x00280000,
},{ }, {
name: "STORK usr cramfs", name: "STORK usr cramfs",
size: 0x00800000, size: 0x00800000,
offset: 0x00580000 offset: 0x00580000,
},{ }, {
name: "STORK usr local", name: "STORK usr local",
offset: 0x00d80000, offset: 0x00d80000,
size: MTDPART_SIZ_FULL size: MTDPART_SIZ_FULL,
#endif #endif
} }
}; };
#endif #endif
#define NB_OF(x) (sizeof(x)/sizeof(x[0])) #ifdef CONFIG_SA1100_TRIZEPS
static struct mtd_partition trizeps_partitions[] = {
{
extern int parse_redboot_partitions(struct mtd_info *master, struct mtd_partition **pparts); name: "Bootloader & the kernel",
extern int parse_bootldr_partitions(struct mtd_info *master, struct mtd_partition **pparts); 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; #ifdef CONFIG_SA1100_YOPY
static struct mtd_info *mymtd; 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 nb_parts = 0;
int parsed_nr_parts = 0;
char *part_type;
/* Default flash buswidth */
sa1100_map.buswidth = (MSC0 & MSC_RBW) ? 2 : 4;
/* #ifdef CONFIG_SA1100_ADSBITSY
* Static partition definition selection if (machine_is_adsbitsy()) {
*/ *parts = adsbitsy_partitions;
part_type = "static"; nb_parts = ARRAY_SIZE(adsbitsy_partitions);
}
#endif
#ifdef CONFIG_SA1100_ASSABET #ifdef CONFIG_SA1100_ASSABET
if (machine_is_assabet()) { if (machine_is_assabet()) {
parts = assabet_partitions; *parts = assabet_partitions;
nb_parts = NB_OF(assabet_partitions); nb_parts = ARRAY_SIZE(assabet_partitions);
sa1100_map.size = assabet_max_flash_size;
} }
#endif #endif
#ifdef CONFIG_SA1100_BADGE4
#ifdef CONFIG_SA1100_HUW_WEBPANEL if (machine_is_badge4()) {
if (machine_is_huw_webpanel()) { *parts = badge4_partitions;
parts = huw_webpanel_partitions; nb_parts = ARRAY_SIZE(badge4_partitions);
nb_parts = NB_OF(huw_webpanel_partitions);
sa1100_map.size = huw_webpanel_max_flash_size;
} }
#endif #endif
#ifdef CONFIG_SA1100_CERF
#ifdef CONFIG_SA1100_H3600 if (machine_is_cerf()) {
if (machine_is_h3600()) { *parts = cerf_partitions;
parts = h3600_partitions; nb_parts = ARRAY_SIZE(cerf_partitions);
nb_parts = NB_OF(h3600_partitions); }
sa1100_map.size = h3600_max_flash_size; #endif
sa1100_map.set_vpp = h3600_set_vpp; #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 #endif
#ifdef CONFIG_SA1100_FREEBIRD #ifdef CONFIG_SA1100_FREEBIRD
if (machine_is_freebird()) { if (machine_is_freebird()) {
parts = freebird_partitions; *parts = freebird_partitions;
nb_parts = NB_OF(freebird_partitions); nb_parts = ARRAY_SIZE(freebird_partitions);
sa1100_map.size = freebird_max_flash_size;
} }
#endif #endif
#ifdef CONFIG_SA1100_CERF #ifdef CONFIG_SA1100_FRODO
if (machine_is_cerf()) { if (machine_is_frodo()) {
parts = cerf_partitions; *parts = frodo_partitions;
nb_parts = NB_OF(cerf_partitions); nb_parts = ARRAY_SIZE(frodo_partitions);
sa1100_map.size = cerf_max_flash_size;
} }
#endif #endif
#ifdef CONFIG_SA1100_GRAPHICSCLIENT #ifdef CONFIG_SA1100_GRAPHICSCLIENT
if (machine_is_graphicsclient()) { if (machine_is_graphicsclient()) {
parts = graphicsclient_partitions; *parts = graphicsclient_partitions;
nb_parts = NB_OF(graphicsclient_partitions); nb_parts = ARRAY_SIZE(graphicsclient_partitions);
sa1100_map.size = graphicsclient_max_flash_size;
sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
} }
#endif #endif
#ifdef CONFIG_SA1100_GRAPHICSMASTER #ifdef CONFIG_SA1100_GRAPHICSMASTER
if (machine_is_graphicsmaster()) { if (machine_is_graphicsmaster()) {
parts = graphicsmaster_partitions; *parts = graphicsmaster_partitions;
nb_parts = NB_OF(graphicsmaster_partitions); nb_parts = ARRAY_SIZE(graphicsmaster_partitions);
sa1100_map.size = graphicsmaster_max_flash_size;
sa1100_map.buswidth = (MSC1 & MSC_RBW) ? 2:4;
} }
#endif #endif
#ifdef CONFIG_SA1100_PANGOLIN #ifdef CONFIG_SA1100_H3XXX
if (machine_is_pangolin()) { if (machine_is_h3xxx()) {
parts = pangolin_partitions; *parts = h3xxx_partitions;
nb_parts = NB_OF(pangolin_partitions); nb_parts = ARRAY_SIZE(h3xxx_partitions);
sa1100_map.size = pangolin_max_flash_size; }
#endif
#ifdef CONFIG_SA1100_HUW_WEBPANEL
if (machine_is_huw_webpanel()) {
*parts = huw_webpanel_partitions;
nb_parts = ARRAY_SIZE(huw_webpanel_partitions);
} }
#endif #endif
#ifdef CONFIG_SA1100_JORNADA720 #ifdef CONFIG_SA1100_JORNADA720
if (machine_is_jornada720()) { if (machine_is_jornada720()) {
parts = jornada720_partitions; *parts = jornada720_partitions;
nb_parts = NB_OF(jornada720_partitions); nb_parts = ARRAY_SIZE(jornada720_partitions);
sa1100_map.size = jornada720_max_flash_size;
sa1100_map.set_vpp = jornada720_set_vpp;
} }
#endif #endif
#ifdef CONFIG_SA1100_YOPY #ifdef CONFIG_SA1100_PANGOLIN
if (machine_is_yopy()) { if (machine_is_pangolin()) {
parts = yopy_partitions; *parts = pangolin_partitions;
nb_parts = NB_OF(yopy_partitions); nb_parts = ARRAY_SIZE(pangolin_partitions);
sa1100_map.size = yopy_max_flash_size; }
#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 #endif
#ifdef CONFIG_SA1100_SHERMAN #ifdef CONFIG_SA1100_SHERMAN
if (machine_is_sherman()) { if (machine_is_sherman()) {
parts = sherman_partitions; *parts = sherman_partitions;
nb_parts = NB_OF(sherman_partitions); nb_parts = ARRAY_SIZE(sherman_partitions);
sa1100_map.size = sherman_max_flash_size;
} }
#endif #endif
#ifdef CONFIG_SA1100_FLEXANET #ifdef CONFIG_SA1100_SIMPAD
if (machine_is_flexanet()) { if (machine_is_simpad()) {
parts = flexanet_partitions; *parts = simpad_partitions;
nb_parts = NB_OF(flexanet_partitions); nb_parts = ARRAY_SIZE(simpad_partitions);
sa1100_map.size = flexanet_max_flash_size;
} }
#endif #endif
#ifdef CONFIG_SA1100_STORK #ifdef CONFIG_SA1100_STORK
if (machine_is_stork()) { if (machine_is_stork()) {
parts = stork_partitions; *parts = stork_partitions;
nb_parts = NB_OF(stork_partitions); nb_parts = ARRAY_SIZE(stork_partitions);
sa1100_map.size = stork_max_flash_size;
} }
#endif #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;
/*
* 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 (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 * Now let's probe for the actual flash. Do it here since
* specific machine settings might have been set above. * specific machine settings might have been set above.
*/ */
printk(KERN_NOTICE "SA1100 flash: probing %d-bit flash bus\n", sa1100_map.buswidth*8); sa[i].mtd = do_map_probe("cfi_probe", sa[i].map);
mymtd = do_map_probe("cfi_probe", &sa1100_map); if (sa[i].mtd == NULL) {
if (!mymtd) ret = -ENXIO;
return -ENXIO; break;
mymtd->module = THIS_MODULE; }
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;
}
/* /*
* Dynamic partition selection stuff (might override the static ones) * ENXIO is special. It means we didn't find a chip when
* we probed. We need to tear down the mapping, free the
* resource and mark it as such.
*/ */
#ifdef CONFIG_MTD_REDBOOT_PARTS if (ret == -ENXIO) {
if (parsed_nr_parts == 0) { iounmap(sa[i].vbase);
int ret = parse_redboot_partitions(mymtd, &parsed_parts); sa[i].vbase = NULL;
release_resource(sa[i].res);
sa[i].res = NULL;
}
if (ret > 0) { /*
part_type = "RedBoot"; * If we found one device, don't bother with concat support.
parsed_nr_parts = ret; * 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
} }
} }
#endif
#ifdef CONFIG_MTD_BOOTLDR_PARTS /*
if (parsed_nr_parts == 0) { * If we failed, clean up.
int ret = parse_bootldr_partitions(mymtd, &parsed_parts); */
if (ret > 0) { if (ret) {
part_type = "Compaq bootldr"; do {
parsed_nr_parts = ret; if (sa[i].mtd)
map_destroy(sa[i].mtd);
if (sa[i].vbase)
iounmap(sa[i].vbase);
if (sa[i].res)
release_resource(sa[i].res);
} while (i--);
kfree(maps);
} }
return ret;
}
static void __exit sa1100_destroy_mtd(struct sa_info *sa, struct mtd_info *mtd)
{
int i;
del_mtd_partitions(mtd);
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);
} }
#endif kfree(sa[0].map);
}
if (parsed_nr_parts > 0) { static int __init sa1100_locate_flash(void)
parts = parsed_parts; {
nb_parts = parsed_nr_parts; 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;
if (nb_parts == 0) { /*
printk(KERN_NOTICE "SA1100 flash: no partition info available, registering whole flash at once\n"); * Retrieve the buswidth from the MSC registers.
add_mtd_device(mymtd); * 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 { } else {
printk(KERN_NOTICE "Using %s partition definition\n", part_type); printk(KERN_NOTICE "SA1100 flash: using %s partition "
add_mtd_partitions(mymtd, parts, nb_parts); "definition\n", part_type);
add_mtd_partitions(mtd, parsed_parts, nr_parts);
} }
return 0;
/* Always succeeds. */
} }
static void __exit sa1100_mtd_cleanup(void) static void __exit sa1100_destroy_partitions(void)
{ {
if (mymtd) {
del_mtd_partitions(mymtd);
map_destroy(mymtd);
if (parsed_parts) if (parsed_parts)
kfree(parsed_parts); kfree(parsed_parts);
} }
static struct mtd_info *mymtd;
static int __init sa1100_mtd_init(void)
{
int ret;
int nr;
nr = sa1100_locate_flash();
if (nr < 0)
return nr;
ret = sa1100_setup_mtd(info, nr, &mymtd);
if (ret == 0)
sa1100_locate_partitions(mymtd);
return ret;
}
static void __exit sa1100_mtd_cleanup(void)
{
sa1100_destroy_mtd(info, mymtd);
sa1100_destroy_partitions();
} }
module_init(sa1100_mtd_init); module_init(sa1100_mtd_init);
......
/*
* 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");
/*
* MTD device concatenation layer definitions
*
* (C) 2002 Robert Kaiser <rkaiser@sysgo.de>
*
* This code is GPL
*
* $Id: concat.h,v 1.1 2002/03/08 16:34:36 rkaiser Exp $
*/
#ifndef MTD_CONCAT_H
#define MTD_CONCAT_H
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 */
void mtd_concat_destroy(struct mtd_info *mtd);
#endif
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment