Fixed MTP to work with TWRP

This commit is contained in:
awab228 2018-06-19 23:16:04 +02:00
commit f6dfaef42e
50820 changed files with 20846062 additions and 0 deletions

402
drivers/mtd/maps/Kconfig Normal file
View file

@ -0,0 +1,402 @@
menu "Mapping drivers for chip access"
depends on MTD!=n
depends on HAS_IOMEM
config MTD_COMPLEX_MAPPINGS
bool "Support non-linear mappings of flash chips"
help
This causes the chip drivers to allow for complicated
paged mappings of flash chips.
config MTD_PHYSMAP
tristate "Flash device in physical memory map"
depends on MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_LPDDR
help
This provides a 'mapping' driver which allows the NOR Flash and
ROM driver code to communicate with chips which are mapped
physically into the CPU's memory. You will need to configure
the physical address and size of the flash chips on your
particular board as well as the bus width, either statically
with config options or at run-time.
To compile this driver as a module, choose M here: the
module will be called physmap.
config MTD_PHYSMAP_COMPAT
bool "Physmap compat support"
depends on MTD_PHYSMAP
default n
help
Setup a simple mapping via the Kconfig options. Normally the
physmap configuration options are done via your board's
resource file.
If unsure, say N here.
config MTD_PHYSMAP_START
hex "Physical start address of flash mapping"
depends on MTD_PHYSMAP_COMPAT
default "0x8000000"
help
This is the physical memory location at which the flash chips
are mapped on your particular target board. Refer to the
memory map which should hopefully be in the documentation for
your board.
config MTD_PHYSMAP_LEN
hex "Physical length of flash mapping"
depends on MTD_PHYSMAP_COMPAT
default "0"
help
This is the total length of the mapping of the flash chips on
your particular board. If there is space, or aliases, in the
physical memory map between the chips, this could be larger
than the total amount of flash present. Refer to the memory
map which should hopefully be in the documentation for your
board.
config MTD_PHYSMAP_BANKWIDTH
int "Bank width in octets"
depends on MTD_PHYSMAP_COMPAT
default "2"
help
This is the total width of the data bus of the flash devices
in octets. For example, if you have a data bus width of 32
bits, you would set the bus width octet value to 4. This is
used internally by the CFI drivers.
config MTD_PHYSMAP_OF
tristate "Memory device in physical memory map based on OF description"
depends on OF && (MTD_CFI || MTD_JEDECPROBE || MTD_ROM || MTD_RAM)
help
This provides a 'mapping' driver which allows the NOR Flash, ROM
and RAM driver code to communicate with chips which are mapped
physically into the CPU's memory. The mapping description here is
taken from OF device tree.
config MTD_PMC_MSP_EVM
tristate "CFI Flash device mapped on PMC-Sierra MSP"
depends on PMC_MSP && MTD_CFI
help
This provides a 'mapping' driver which supports the way
in which user-programmable flash chips are connected on the
PMC-Sierra MSP eval/demo boards.
choice
prompt "Maximum mappable memory available for flash IO"
depends on MTD_PMC_MSP_EVM
default MSP_FLASH_MAP_LIMIT_32M
config MSP_FLASH_MAP_LIMIT_32M
bool "32M"
endchoice
config MSP_FLASH_MAP_LIMIT
hex
default "0x02000000"
depends on MSP_FLASH_MAP_LIMIT_32M
config MTD_SUN_UFLASH
tristate "Sun Microsystems userflash support"
depends on SPARC && MTD_CFI && PCI
help
This provides a 'mapping' driver which supports the way in
which user-programmable flash chips are connected on various
Sun Microsystems boardsets. This driver will require CFI support
in the kernel, so if you did not enable CFI previously, do that now.
config MTD_SC520CDP
tristate "CFI Flash device mapped on AMD SC520 CDP"
depends on (MELAN || COMPILE_TEST) && MTD_CFI
help
The SC520 CDP board has two banks of CFI-compliant chips and one
Dual-in-line JEDEC chip. This 'mapping' driver supports that
arrangement, implementing three MTD devices.
config MTD_NETSC520
tristate "CFI Flash device mapped on AMD NetSc520"
depends on (MELAN || COMPILE_TEST) && MTD_CFI
help
This enables access routines for the flash chips on the AMD NetSc520
demonstration board. If you have one of these boards and would like
to use the flash chips on it, say 'Y'.
config MTD_TS5500
tristate "JEDEC Flash device mapped on Technologic Systems TS-5500"
depends on TS5500 || COMPILE_TEST
select MTD_JEDECPROBE
select MTD_CFI_AMDSTD
help
This provides a driver for the on-board flash of the Technologic
System's TS-5500 board. The 2MB flash is split into 3 partitions
which are accessed as separate MTD devices.
mtd0 and mtd2 are the two BIOS drives, which use the resident
flash disk (RFD) flash translation layer.
mtd1 allows you to reprogram your BIOS. BE VERY CAREFUL.
Note that jumper 3 ("Write Enable Drive A") must be set
otherwise detection won't succeed.
config MTD_SBC_GXX
tristate "CFI Flash device mapped on Arcom SBC-GXx boards"
depends on X86 && MTD_CFI_INTELEXT && MTD_COMPLEX_MAPPINGS
help
This provides a driver for the on-board flash of Arcom Control
Systems' SBC-GXn family of boards, formerly known as SBC-MediaGX.
By default the flash is split into 3 partitions which are accessed
as separate MTD devices. This board utilizes Intel StrataFlash.
More info at
<http://www.arcomcontrols.com/products/icp/pc104/processors/SBC_GX1.htm>.
config MTD_PXA2XX
tristate "CFI Flash device mapped on Intel XScale PXA2xx based boards"
depends on (PXA25x || PXA27x) && MTD_CFI_INTELEXT
help
This provides a driver for the NOR flash attached to a PXA2xx chip.
config MTD_SCx200_DOCFLASH
tristate "Flash device mapped with DOCCS on NatSemi SCx200"
depends on SCx200 && MTD_CFI
help
Enable support for a flash chip mapped using the DOCCS signal on a
National Semiconductor SCx200 processor.
If you don't know what to do here, say N.
If compiled as a module, it will be called scx200_docflash.
config MTD_AMD76XROM
tristate "BIOS flash chip on AMD76x southbridge"
depends on X86 && MTD_JEDECPROBE
help
Support for treating the BIOS flash chip on AMD76x motherboards
as an MTD device - with this you can reprogram your BIOS.
BE VERY CAREFUL.
config MTD_ICHXROM
tristate "BIOS flash chip on Intel Controller Hub 2/3/4/5"
depends on X86 && MTD_JEDECPROBE
help
Support for treating the BIOS flash chip on ICHX motherboards
as an MTD device - with this you can reprogram your BIOS.
BE VERY CAREFUL.
config MTD_ESB2ROM
tristate "BIOS flash chip on Intel ESB Controller Hub 2"
depends on X86 && MTD_JEDECPROBE && PCI
help
Support for treating the BIOS flash chip on ESB2 motherboards
as an MTD device - with this you can reprogram your BIOS.
BE VERY CAREFUL.
config MTD_CK804XROM
tristate "BIOS flash chip on Nvidia CK804"
depends on X86 && MTD_JEDECPROBE && PCI
help
Support for treating the BIOS flash chip on nvidia motherboards
as an MTD device - with this you can reprogram your BIOS.
BE VERY CAREFUL.
config MTD_SCB2_FLASH
tristate "BIOS flash chip on Intel SCB2 boards"
depends on X86 && MTD_JEDECPROBE && PCI
help
Support for treating the BIOS flash chip on Intel SCB2 boards
as an MTD device - with this you can reprogram your BIOS.
BE VERY CAREFUL.
config MTD_TSUNAMI
tristate "Flash chips on Tsunami TIG bus"
depends on ALPHA_TSUNAMI && MTD_COMPLEX_MAPPINGS
help
Support for the flash chip on Tsunami TIG bus.
config MTD_NETtel
tristate "CFI flash device on SnapGear/SecureEdge"
depends on X86 && MTD_JEDECPROBE
help
Support for flash chips on NETtel/SecureEdge/SnapGear boards.
config MTD_LANTIQ
tristate "Lantiq SoC NOR support"
depends on LANTIQ
help
Support for NOR flash attached to the Lantiq SoC's External Bus Unit.
config MTD_L440GX
tristate "BIOS flash chip on Intel L440GX boards"
depends on X86 && MTD_JEDECPROBE
help
Support for treating the BIOS flash chip on Intel L440GX motherboards
as an MTD device - with this you can reprogram your BIOS.
BE VERY CAREFUL.
config MTD_CFI_FLAGADM
tristate "CFI Flash device mapping on FlagaDM"
depends on 8xx && MTD_CFI
help
Mapping for the Flaga digital module. If you don't have one, ignore
this setting.
config MTD_SOLUTIONENGINE
tristate "CFI Flash device mapped on Hitachi SolutionEngine"
depends on SOLUTION_ENGINE && MTD_CFI && MTD_REDBOOT_PARTS
help
This enables access to the flash chips on the Hitachi SolutionEngine and
similar boards. Say 'Y' if you are building a kernel for such a board.
config MTD_SA1100
tristate "CFI Flash device mapped on StrongARM SA11x0"
depends on MTD_CFI && ARCH_SA1100
help
This enables access to the flash chips on most platforms based on
the SA1100 and SA1110, including the Assabet and the Compaq iPAQ.
If you have such a board, say 'Y'.
config MTD_DC21285
tristate "CFI Flash device mapped on DC21285 Footbridge"
depends on MTD_CFI && ARCH_FOOTBRIDGE && MTD_COMPLEX_MAPPINGS
help
This provides a driver for the flash accessed using Intel's
21285 bridge used with Intel's StrongARM processors. More info at
<http://www.intel.com/design/bridge/docs/21285_documentation.htm>.
config MTD_IXP4XX
tristate "CFI Flash device mapped on Intel IXP4xx based systems"
depends on MTD_CFI && MTD_COMPLEX_MAPPINGS && ARCH_IXP4XX
help
This enables MTD access to flash devices on platforms based
on Intel's IXP4xx family of network processors such as the
IXDP425 and Coyote. If you have an IXP4xx based board and
would like to use the flash chips on it, say 'Y'.
config MTD_IMPA7
tristate "JEDEC Flash device mapped on impA7"
depends on ARM && MTD_JEDECPROBE
help
This enables access to the NOR Flash on the impA7 board of
implementa GmbH. If you have such a board, say 'Y' here.
# This needs CFI or JEDEC, depending on the cards found.
config MTD_PCI
tristate "PCI MTD driver"
depends on PCI && MTD_COMPLEX_MAPPINGS
help
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.
config MTD_PCMCIA
tristate "PCMCIA MTD driver"
depends on PCMCIA && MTD_COMPLEX_MAPPINGS
help
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_PCMCIA_ANONYMOUS
bool "Use PCMCIA MTD drivers for anonymous PCMCIA cards"
depends on MTD_PCMCIA
help
If this option is enabled, PCMCIA cards which do not report
anything about themselves are assumed to be MTD cards.
If unsure, say N.
config MTD_BFIN_ASYNC
tristate "Blackfin BF533-STAMP Flash Chip Support"
depends on BFIN533_STAMP && MTD_CFI && MTD_COMPLEX_MAPPINGS
default y
help
Map driver which allows for simultaneous utilization of
ethernet and CFI parallel flash.
If compiled as a module, it will be called bfin-async-flash.
config MTD_GPIO_ADDR
tristate "GPIO-assisted Flash Chip Support"
depends on GPIOLIB
depends on MTD_COMPLEX_MAPPINGS
help
Map driver which allows flashes to be partially physically addressed
and assisted by GPIOs.
If compiled as a module, it will be called gpio-addr-flash.
config MTD_UCLINUX
bool "Generic uClinux RAM/ROM filesystem support"
depends on (MTD_RAM=y || MTD_ROM=y) && (!MMU || COLDFIRE)
help
Map driver to support image based filesystems for uClinux.
config MTD_INTEL_VR_NOR
tristate "NOR flash on Intel Vermilion Range Expansion Bus CS0"
depends on PCI
help
Map driver for a NOR flash bank located on the Expansion Bus of the
Intel Vermilion Range chipset.
config MTD_RBTX4939
tristate "Map driver for RBTX4939 board"
depends on TOSHIBA_RBTX4939 && MTD_CFI && MTD_COMPLEX_MAPPINGS
help
Map driver for NOR flash chips on RBTX4939 board.
config MTD_PLATRAM
tristate "Map driver for platform device RAM (mtd-ram)"
select MTD_RAM
help
Map driver for RAM areas described via the platform device
system.
This selection automatically selects the map_ram driver.
config MTD_VMU
tristate "Map driver for Dreamcast VMU"
depends on MAPLE
help
This driver enables access to the Dreamcast Visual Memory Unit (VMU).
Most Dreamcast users will want to say Y here.
To build this as a module select M here, the module will be called
vmu-flash.
config MTD_PISMO
tristate "MTD discovery driver for PISMO modules"
depends on I2C
depends on ARCH_VERSATILE
help
This driver allows for discovery of PISMO modules - see
<http://www.pismoworld.org/>. These are small modules containing
up to five memory devices (eg, SRAM, flash, DOC) described by an
I2C EEPROM.
This driver does not create any MTD maps itself; instead it
creates MTD physmap and MTD SRAM platform devices. If you
enable this option, you should consider enabling MTD_PHYSMAP
and/or MTD_PLATRAM according to the devices on your module.
When built as a module, it will be called pismo.ko
config MTD_LATCH_ADDR
tristate "Latch-assisted Flash Chip Support"
depends on MTD_COMPLEX_MAPPINGS
help
Map driver which allows flashes to be partially physically addressed
and have the upper address lines set by a board specific code.
If compiled as a module, it will be called latch-addr-flash.
endmenu

45
drivers/mtd/maps/Makefile Normal file
View file

@ -0,0 +1,45 @@
#
# linux/drivers/maps/Makefile
#
ifeq ($(CONFIG_MTD_COMPLEX_MAPPINGS),y)
obj-$(CONFIG_MTD) += map_funcs.o
endif
# Chip mappings
obj-$(CONFIG_MTD_CFI_FLAGADM) += cfi_flagadm.o
obj-$(CONFIG_MTD_DC21285) += dc21285.o
obj-$(CONFIG_MTD_L440GX) += l440gx.o
obj-$(CONFIG_MTD_AMD76XROM) += amd76xrom.o
obj-$(CONFIG_MTD_ESB2ROM) += esb2rom.o
obj-$(CONFIG_MTD_ICHXROM) += ichxrom.o
obj-$(CONFIG_MTD_CK804XROM) += ck804xrom.o
obj-$(CONFIG_MTD_TSUNAMI) += tsunami_flash.o
obj-$(CONFIG_MTD_PXA2XX) += pxa2xx-flash.o
obj-$(CONFIG_MTD_PHYSMAP) += physmap.o
obj-$(CONFIG_MTD_PHYSMAP_OF) += physmap_of.o
obj-$(CONFIG_MTD_PISMO) += pismo.o
obj-$(CONFIG_MTD_PMC_MSP_EVM) += pmcmsp-flash.o
obj-$(CONFIG_MTD_PCMCIA) += pcmciamtd.o
obj-$(CONFIG_MTD_SA1100) += sa1100-flash.o
obj-$(CONFIG_MTD_SBC_GXX) += sbc_gxx.o
obj-$(CONFIG_MTD_SC520CDP) += sc520cdp.o
obj-$(CONFIG_MTD_NETSC520) += netsc520.o
obj-$(CONFIG_MTD_TS5500) += ts5500_flash.o
obj-$(CONFIG_MTD_SUN_UFLASH) += sun_uflash.o
obj-$(CONFIG_MTD_SCx200_DOCFLASH)+= scx200_docflash.o
obj-$(CONFIG_MTD_SOLUTIONENGINE)+= solutionengine.o
obj-$(CONFIG_MTD_PCI) += pci.o
obj-$(CONFIG_MTD_IMPA7) += impa7.o
obj-$(CONFIG_MTD_UCLINUX) += uclinux.o
obj-$(CONFIG_MTD_NETtel) += nettel.o
obj-$(CONFIG_MTD_SCB2_FLASH) += scb2_flash.o
obj-$(CONFIG_MTD_IXP4XX) += ixp4xx.o
obj-$(CONFIG_MTD_PLATRAM) += plat-ram.o
obj-$(CONFIG_MTD_INTEL_VR_NOR) += intel_vr_nor.o
obj-$(CONFIG_MTD_BFIN_ASYNC) += bfin-async-flash.o
obj-$(CONFIG_MTD_RBTX4939) += rbtx4939-flash.o
obj-$(CONFIG_MTD_VMU) += vmu-flash.o
obj-$(CONFIG_MTD_GPIO_ADDR) += gpio-addr-flash.o
obj-$(CONFIG_MTD_LATCH_ADDR) += latch-addr-flash.o
obj-$(CONFIG_MTD_LANTIQ) += lantiq-flash.o

View file

@ -0,0 +1,349 @@
/*
* amd76xrom.c
*
* Normal mappings of chips in physical memory
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/flashchip.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/list.h>
#define xstr(s) str(s)
#define str(s) #s
#define MOD_NAME xstr(KBUILD_BASENAME)
#define ADDRESS_NAME_LEN 18
#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
struct amd76xrom_window {
void __iomem *virt;
unsigned long phys;
unsigned long size;
struct list_head maps;
struct resource rsrc;
struct pci_dev *pdev;
};
struct amd76xrom_map_info {
struct list_head list;
struct map_info map;
struct mtd_info *mtd;
struct resource rsrc;
char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
};
/* The 2 bits controlling the window size are often set to allow reading
* the BIOS, but too small to allow writing, since the lock registers are
* 4MiB lower in the address space than the data.
*
* This is intended to prevent flashing the bios, perhaps accidentally.
*
* This parameter allows the normal driver to over-ride the BIOS settings.
*
* The bits are 6 and 7. If both bits are set, it is a 5MiB window.
* If only the 7 Bit is set, it is a 4MiB window. Otherwise, a
* 64KiB window.
*
*/
static uint win_size_bits;
module_param(win_size_bits, uint, 0);
MODULE_PARM_DESC(win_size_bits, "ROM window size bits override for 0x43 byte, normally set by BIOS.");
static struct amd76xrom_window amd76xrom_window = {
.maps = LIST_HEAD_INIT(amd76xrom_window.maps),
};
static void amd76xrom_cleanup(struct amd76xrom_window *window)
{
struct amd76xrom_map_info *map, *scratch;
u8 byte;
if (window->pdev) {
/* Disable writes through the rom window */
pci_read_config_byte(window->pdev, 0x40, &byte);
pci_write_config_byte(window->pdev, 0x40, byte & ~1);
pci_dev_put(window->pdev);
}
/* Free all of the mtd devices */
list_for_each_entry_safe(map, scratch, &window->maps, list) {
if (map->rsrc.parent) {
release_resource(&map->rsrc);
}
mtd_device_unregister(map->mtd);
map_destroy(map->mtd);
list_del(&map->list);
kfree(map);
}
if (window->rsrc.parent)
release_resource(&window->rsrc);
if (window->virt) {
iounmap(window->virt);
window->virt = NULL;
window->phys = 0;
window->size = 0;
window->pdev = NULL;
}
}
static int amd76xrom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
u8 byte;
struct amd76xrom_window *window = &amd76xrom_window;
struct amd76xrom_map_info *map = NULL;
unsigned long map_top;
/* Remember the pci dev I find the window in - already have a ref */
window->pdev = pdev;
/* Enable the selected rom window. This is often incorrectly
* set up by the BIOS, and the 4MiB offset for the lock registers
* requires the full 5MiB of window space.
*
* This 'write, then read' approach leaves the bits for
* other uses of the hardware info.
*/
pci_read_config_byte(pdev, 0x43, &byte);
pci_write_config_byte(pdev, 0x43, byte | win_size_bits );
/* Assume the rom window is properly setup, and find it's size */
pci_read_config_byte(pdev, 0x43, &byte);
if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6))) {
window->phys = 0xffb00000; /* 5MiB */
}
else if ((byte & (1<<7)) == (1<<7)) {
window->phys = 0xffc00000; /* 4MiB */
}
else {
window->phys = 0xffff0000; /* 64KiB */
}
window->size = 0xffffffffUL - window->phys + 1UL;
/*
* Try to reserve the window mem region. If this fails then
* it is likely due to a fragment of the window being
* "reseved" by the BIOS. In the case that the
* request_mem_region() fails then once the rom size is
* discovered we will try to reserve the unreserved fragment.
*/
window->rsrc.name = MOD_NAME;
window->rsrc.start = window->phys;
window->rsrc.end = window->phys + window->size - 1;
window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&iomem_resource, &window->rsrc)) {
window->rsrc.parent = NULL;
printk(KERN_ERR MOD_NAME
" %s(): Unable to register resource %pR - kernel bug?\n",
__func__, &window->rsrc);
return -EBUSY;
}
/* Enable writes through the rom window */
pci_read_config_byte(pdev, 0x40, &byte);
pci_write_config_byte(pdev, 0x40, byte | 1);
/* FIXME handle registers 0x80 - 0x8C the bios region locks */
/* For write accesses caches are useless */
window->virt = ioremap_nocache(window->phys, window->size);
if (!window->virt) {
printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
window->phys, window->size);
goto out;
}
/* Get the first address to look for an rom chip at */
map_top = window->phys;
#if 1
/* The probe sequence run over the firmware hub lock
* registers sets them to 0x7 (no access).
* Probe at most the last 4M of the address space.
*/
if (map_top < 0xffc00000) {
map_top = 0xffc00000;
}
#endif
/* Loop through and look for rom chips */
while((map_top - 1) < 0xffffffffUL) {
struct cfi_private *cfi;
unsigned long offset;
int i;
if (!map) {
map = kmalloc(sizeof(*map), GFP_KERNEL);
}
if (!map) {
printk(KERN_ERR MOD_NAME ": kmalloc failed");
goto out;
}
memset(map, 0, sizeof(*map));
INIT_LIST_HEAD(&map->list);
map->map.name = map->map_name;
map->map.phys = map_top;
offset = map_top - window->phys;
map->map.virt = (void __iomem *)
(((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08Lx",
MOD_NAME, (unsigned long long)map->map.phys);
/* There is no generic VPP support */
for(map->map.bankwidth = 32; map->map.bankwidth;
map->map.bankwidth >>= 1)
{
char **probe_type;
/* Skip bankwidths that are not supported */
if (!map_bankwidth_supported(map->map.bankwidth))
continue;
/* Setup the map methods */
simple_map_init(&map->map);
/* Try all of the probe methods */
probe_type = rom_probe_types;
for(; *probe_type; probe_type++) {
map->mtd = do_map_probe(*probe_type, &map->map);
if (map->mtd)
goto found;
}
}
map_top += ROM_PROBE_STEP_SIZE;
continue;
found:
/* Trim the size if we are larger than the map */
if (map->mtd->size > map->map.size) {
printk(KERN_WARNING MOD_NAME
" rom(%llu) larger than window(%lu). fixing...\n",
(unsigned long long)map->mtd->size, map->map.size);
map->mtd->size = map->map.size;
}
if (window->rsrc.parent) {
/*
* Registering the MTD device in iomem may not be possible
* if there is a BIOS "reserved" and BUSY range. If this
* fails then continue anyway.
*/
map->rsrc.name = map->map_name;
map->rsrc.start = map->map.phys;
map->rsrc.end = map->map.phys + map->mtd->size - 1;
map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&window->rsrc, &map->rsrc)) {
printk(KERN_ERR MOD_NAME
": cannot reserve MTD resource\n");
map->rsrc.parent = NULL;
}
}
/* Make the whole region visible in the map */
map->map.virt = window->virt;
map->map.phys = window->phys;
cfi = map->map.fldrv_priv;
for(i = 0; i < cfi->numchips; i++) {
cfi->chips[i].start += offset;
}
/* Now that the mtd devices is complete claim and export it */
map->mtd->owner = THIS_MODULE;
if (mtd_device_register(map->mtd, NULL, 0)) {
map_destroy(map->mtd);
map->mtd = NULL;
goto out;
}
/* Calculate the new value of map_top */
map_top += map->mtd->size;
/* File away the map structure */
list_add(&map->list, &window->maps);
map = NULL;
}
out:
/* Free any left over map structures */
kfree(map);
/* See if I have any map structures */
if (list_empty(&window->maps)) {
amd76xrom_cleanup(window);
return -ENODEV;
}
return 0;
}
static void amd76xrom_remove_one(struct pci_dev *pdev)
{
struct amd76xrom_window *window = &amd76xrom_window;
amd76xrom_cleanup(window);
}
static struct pci_device_id amd76xrom_pci_tbl[] = {
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7410,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_VIPER_7440,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_AMD, 0x7468 }, /* amd8111 support */
{ 0, }
};
MODULE_DEVICE_TABLE(pci, amd76xrom_pci_tbl);
#if 0
static struct pci_driver amd76xrom_driver = {
.name = MOD_NAME,
.id_table = amd76xrom_pci_tbl,
.probe = amd76xrom_init_one,
.remove = amd76xrom_remove_one,
};
#endif
static int __init init_amd76xrom(void)
{
struct pci_dev *pdev;
struct pci_device_id *id;
pdev = NULL;
for(id = amd76xrom_pci_tbl; id->vendor; id++) {
pdev = pci_get_device(id->vendor, id->device, NULL);
if (pdev) {
break;
}
}
if (pdev) {
return amd76xrom_init_one(pdev, &amd76xrom_pci_tbl[0]);
}
return -ENXIO;
#if 0
return pci_register_driver(&amd76xrom_driver);
#endif
}
static void __exit cleanup_amd76xrom(void)
{
amd76xrom_remove_one(amd76xrom_window.pdev);
}
module_init(init_amd76xrom);
module_exit(cleanup_amd76xrom);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>");
MODULE_DESCRIPTION("MTD map driver for BIOS chips on the AMD76X southbridge");

View file

@ -0,0 +1,197 @@
/*
* drivers/mtd/maps/bfin-async-flash.c
*
* Handle the case where flash memory and ethernet mac/phy are
* mapped onto the same async bank. The BF533-STAMP does this
* for example. All board-specific configuration goes in your
* board resources file.
*
* Copyright 2000 Nicolas Pitre <nico@fluxnic.net>
* Copyright 2005-2008 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#include <asm/blackfin.h>
#include <linux/gpio.h>
#include <linux/io.h>
#include <asm/unaligned.h>
#define pr_devinit(fmt, args...) \
({ static const char __fmt[] = fmt; printk(__fmt, ## args); })
#define DRIVER_NAME "bfin-async-flash"
struct async_state {
struct mtd_info *mtd;
struct map_info map;
int enet_flash_pin;
uint32_t flash_ambctl0, flash_ambctl1;
uint32_t save_ambctl0, save_ambctl1;
unsigned long irq_flags;
};
static void switch_to_flash(struct async_state *state)
{
local_irq_save(state->irq_flags);
gpio_set_value(state->enet_flash_pin, 0);
state->save_ambctl0 = bfin_read_EBIU_AMBCTL0();
state->save_ambctl1 = bfin_read_EBIU_AMBCTL1();
bfin_write_EBIU_AMBCTL0(state->flash_ambctl0);
bfin_write_EBIU_AMBCTL1(state->flash_ambctl1);
SSYNC();
}
static void switch_back(struct async_state *state)
{
bfin_write_EBIU_AMBCTL0(state->save_ambctl0);
bfin_write_EBIU_AMBCTL1(state->save_ambctl1);
SSYNC();
gpio_set_value(state->enet_flash_pin, 1);
local_irq_restore(state->irq_flags);
}
static map_word bfin_flash_read(struct map_info *map, unsigned long ofs)
{
struct async_state *state = (struct async_state *)map->map_priv_1;
uint16_t word;
map_word test;
switch_to_flash(state);
word = readw(map->virt + ofs);
switch_back(state);
test.x[0] = word;
return test;
}
static void bfin_flash_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
struct async_state *state = (struct async_state *)map->map_priv_1;
switch_to_flash(state);
memcpy(to, map->virt + from, len);
switch_back(state);
}
static void bfin_flash_write(struct map_info *map, map_word d1, unsigned long ofs)
{
struct async_state *state = (struct async_state *)map->map_priv_1;
uint16_t d;
d = d1.x[0];
switch_to_flash(state);
writew(d, map->virt + ofs);
SSYNC();
switch_back(state);
}
static void bfin_flash_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
struct async_state *state = (struct async_state *)map->map_priv_1;
switch_to_flash(state);
memcpy(map->virt + to, from, len);
SSYNC();
switch_back(state);
}
static const char * const part_probe_types[] = {
"cmdlinepart", "RedBoot", NULL };
static int bfin_flash_probe(struct platform_device *pdev)
{
int ret;
struct physmap_flash_data *pdata = dev_get_platdata(&pdev->dev);
struct resource *memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
struct resource *flash_ambctl = platform_get_resource(pdev, IORESOURCE_MEM, 1);
struct async_state *state;
state = kzalloc(sizeof(*state), GFP_KERNEL);
if (!state)
return -ENOMEM;
state->map.name = DRIVER_NAME;
state->map.read = bfin_flash_read;
state->map.copy_from = bfin_flash_copy_from;
state->map.write = bfin_flash_write;
state->map.copy_to = bfin_flash_copy_to;
state->map.bankwidth = pdata->width;
state->map.size = resource_size(memory);
state->map.virt = (void __iomem *)memory->start;
state->map.phys = memory->start;
state->map.map_priv_1 = (unsigned long)state;
state->enet_flash_pin = platform_get_irq(pdev, 0);
state->flash_ambctl0 = flash_ambctl->start;
state->flash_ambctl1 = flash_ambctl->end;
if (gpio_request(state->enet_flash_pin, DRIVER_NAME)) {
pr_devinit(KERN_ERR DRIVER_NAME ": Failed to request gpio %d\n", state->enet_flash_pin);
kfree(state);
return -EBUSY;
}
gpio_direction_output(state->enet_flash_pin, 1);
pr_devinit(KERN_NOTICE DRIVER_NAME ": probing %d-bit flash bus\n", state->map.bankwidth * 8);
state->mtd = do_map_probe(memory->name, &state->map);
if (!state->mtd) {
gpio_free(state->enet_flash_pin);
kfree(state);
return -ENXIO;
}
mtd_device_parse_register(state->mtd, part_probe_types, NULL,
pdata->parts, pdata->nr_parts);
platform_set_drvdata(pdev, state);
return 0;
}
static int bfin_flash_remove(struct platform_device *pdev)
{
struct async_state *state = platform_get_drvdata(pdev);
gpio_free(state->enet_flash_pin);
mtd_device_unregister(state->mtd);
map_destroy(state->mtd);
kfree(state);
return 0;
}
static struct platform_driver bfin_flash_driver = {
.probe = bfin_flash_probe,
.remove = bfin_flash_remove,
.driver = {
.name = DRIVER_NAME,
},
};
module_platform_driver(bfin_flash_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MTD map driver for Blackfins with flash/ethernet on same async bank");

View file

@ -0,0 +1,137 @@
/*
* Copyright © 2001 Flaga hf. Medical Devices, Kári Davíðsson <kd@flaga.is>
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
/* We split the flash chip up into four parts.
* 1: bootloader first 128k (0x00000000 - 0x0001FFFF) size 0x020000
* 2: kernel 640k (0x00020000 - 0x000BFFFF) size 0x0A0000
* 3: compressed 1536k root ramdisk (0x000C0000 - 0x0023FFFF) size 0x180000
* 4: writeable diskpartition (jffs)(0x00240000 - 0x003FFFFF) size 0x1C0000
*/
#define FLASH_PHYS_ADDR 0x40000000
#define FLASH_SIZE 0x400000
#define FLASH_PARTITION0_ADDR 0x00000000
#define FLASH_PARTITION0_SIZE 0x00020000
#define FLASH_PARTITION1_ADDR 0x00020000
#define FLASH_PARTITION1_SIZE 0x000A0000
#define FLASH_PARTITION2_ADDR 0x000C0000
#define FLASH_PARTITION2_SIZE 0x00180000
#define FLASH_PARTITION3_ADDR 0x00240000
#define FLASH_PARTITION3_SIZE 0x001C0000
static struct map_info flagadm_map = {
.name = "FlagaDM flash device",
.size = FLASH_SIZE,
.bankwidth = 2,
};
static struct mtd_partition flagadm_parts[] = {
{
.name = "Bootloader",
.offset = FLASH_PARTITION0_ADDR,
.size = FLASH_PARTITION0_SIZE
},
{
.name = "Kernel image",
.offset = FLASH_PARTITION1_ADDR,
.size = FLASH_PARTITION1_SIZE
},
{
.name = "Initial ramdisk image",
.offset = FLASH_PARTITION2_ADDR,
.size = FLASH_PARTITION2_SIZE
},
{
.name = "Persistent storage",
.offset = FLASH_PARTITION3_ADDR,
.size = FLASH_PARTITION3_SIZE
}
};
#define PARTITION_COUNT ARRAY_SIZE(flagadm_parts)
static struct mtd_info *mymtd;
static int __init init_flagadm(void)
{
printk(KERN_NOTICE "FlagaDM flash device: %x at %x\n",
FLASH_SIZE, FLASH_PHYS_ADDR);
flagadm_map.phys = FLASH_PHYS_ADDR;
flagadm_map.virt = ioremap(FLASH_PHYS_ADDR,
FLASH_SIZE);
if (!flagadm_map.virt) {
printk("Failed to ioremap\n");
return -EIO;
}
simple_map_init(&flagadm_map);
mymtd = do_map_probe("cfi_probe", &flagadm_map);
if (mymtd) {
mymtd->owner = THIS_MODULE;
mtd_device_register(mymtd, flagadm_parts, PARTITION_COUNT);
printk(KERN_NOTICE "FlagaDM flash device initialized\n");
return 0;
}
iounmap((void __iomem *)flagadm_map.virt);
return -ENXIO;
}
static void __exit cleanup_flagadm(void)
{
if (mymtd) {
mtd_device_unregister(mymtd);
map_destroy(mymtd);
}
if (flagadm_map.virt) {
iounmap((void __iomem *)flagadm_map.virt);
flagadm_map.virt = NULL;
}
}
module_init(init_flagadm);
module_exit(cleanup_flagadm);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Kári Davíðsson <kd@flaga.is>");
MODULE_DESCRIPTION("MTD map driver for Flaga digital module");

View file

@ -0,0 +1,387 @@
/*
* ck804xrom.c
*
* Normal mappings of chips in physical memory
*
* Dave Olsen <dolsen@lnxi.com>
* Ryan Jackson <rjackson@lnxi.com>
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/flashchip.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/list.h>
#define MOD_NAME KBUILD_BASENAME
#define ADDRESS_NAME_LEN 18
#define ROM_PROBE_STEP_SIZE (64*1024)
#define DEV_CK804 1
#define DEV_MCP55 2
struct ck804xrom_window {
void __iomem *virt;
unsigned long phys;
unsigned long size;
struct list_head maps;
struct resource rsrc;
struct pci_dev *pdev;
};
struct ck804xrom_map_info {
struct list_head list;
struct map_info map;
struct mtd_info *mtd;
struct resource rsrc;
char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
};
/*
* The following applies to ck804 only:
* The 2 bits controlling the window size are often set to allow reading
* the BIOS, but too small to allow writing, since the lock registers are
* 4MiB lower in the address space than the data.
*
* This is intended to prevent flashing the bios, perhaps accidentally.
*
* This parameter allows the normal driver to override the BIOS settings.
*
* The bits are 6 and 7. If both bits are set, it is a 5MiB window.
* If only the 7 Bit is set, it is a 4MiB window. Otherwise, a
* 64KiB window.
*
* The following applies to mcp55 only:
* The 15 bits controlling the window size are distributed as follows:
* byte @0x88: bit 0..7
* byte @0x8c: bit 8..15
* word @0x90: bit 16..30
* If all bits are enabled, we have a 16? MiB window
* Please set win_size_bits to 0x7fffffff if you actually want to do something
*/
static uint win_size_bits = 0;
module_param(win_size_bits, uint, 0);
MODULE_PARM_DESC(win_size_bits, "ROM window size bits override, normally set by BIOS.");
static struct ck804xrom_window ck804xrom_window = {
.maps = LIST_HEAD_INIT(ck804xrom_window.maps),
};
static void ck804xrom_cleanup(struct ck804xrom_window *window)
{
struct ck804xrom_map_info *map, *scratch;
u8 byte;
if (window->pdev) {
/* Disable writes through the rom window */
pci_read_config_byte(window->pdev, 0x6d, &byte);
pci_write_config_byte(window->pdev, 0x6d, byte & ~1);
}
/* Free all of the mtd devices */
list_for_each_entry_safe(map, scratch, &window->maps, list) {
if (map->rsrc.parent)
release_resource(&map->rsrc);
mtd_device_unregister(map->mtd);
map_destroy(map->mtd);
list_del(&map->list);
kfree(map);
}
if (window->rsrc.parent)
release_resource(&window->rsrc);
if (window->virt) {
iounmap(window->virt);
window->virt = NULL;
window->phys = 0;
window->size = 0;
}
pci_dev_put(window->pdev);
}
static int ck804xrom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
u8 byte;
u16 word;
struct ck804xrom_window *window = &ck804xrom_window;
struct ck804xrom_map_info *map = NULL;
unsigned long map_top;
/* Remember the pci dev I find the window in */
window->pdev = pci_dev_get(pdev);
switch (ent->driver_data) {
case DEV_CK804:
/* Enable the selected rom window. This is often incorrectly
* set up by the BIOS, and the 4MiB offset for the lock registers
* requires the full 5MiB of window space.
*
* This 'write, then read' approach leaves the bits for
* other uses of the hardware info.
*/
pci_read_config_byte(pdev, 0x88, &byte);
pci_write_config_byte(pdev, 0x88, byte | win_size_bits );
/* Assume the rom window is properly setup, and find it's size */
pci_read_config_byte(pdev, 0x88, &byte);
if ((byte & ((1<<7)|(1<<6))) == ((1<<7)|(1<<6)))
window->phys = 0xffb00000; /* 5MiB */
else if ((byte & (1<<7)) == (1<<7))
window->phys = 0xffc00000; /* 4MiB */
else
window->phys = 0xffff0000; /* 64KiB */
break;
case DEV_MCP55:
pci_read_config_byte(pdev, 0x88, &byte);
pci_write_config_byte(pdev, 0x88, byte | (win_size_bits & 0xff));
pci_read_config_byte(pdev, 0x8c, &byte);
pci_write_config_byte(pdev, 0x8c, byte | ((win_size_bits & 0xff00) >> 8));
pci_read_config_word(pdev, 0x90, &word);
pci_write_config_word(pdev, 0x90, word | ((win_size_bits & 0x7fff0000) >> 16));
window->phys = 0xff000000; /* 16MiB, hardcoded for now */
break;
}
window->size = 0xffffffffUL - window->phys + 1UL;
/*
* Try to reserve the window mem region. If this fails then
* it is likely due to a fragment of the window being
* "reserved" by the BIOS. In the case that the
* request_mem_region() fails then once the rom size is
* discovered we will try to reserve the unreserved fragment.
*/
window->rsrc.name = MOD_NAME;
window->rsrc.start = window->phys;
window->rsrc.end = window->phys + window->size - 1;
window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&iomem_resource, &window->rsrc)) {
window->rsrc.parent = NULL;
printk(KERN_ERR MOD_NAME
" %s(): Unable to register resource %pR - kernel bug?\n",
__func__, &window->rsrc);
}
/* Enable writes through the rom window */
pci_read_config_byte(pdev, 0x6d, &byte);
pci_write_config_byte(pdev, 0x6d, byte | 1);
/* FIXME handle registers 0x80 - 0x8C the bios region locks */
/* For write accesses caches are useless */
window->virt = ioremap_nocache(window->phys, window->size);
if (!window->virt) {
printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
window->phys, window->size);
goto out;
}
/* Get the first address to look for a rom chip at */
map_top = window->phys;
#if 1
/* The probe sequence run over the firmware hub lock
* registers sets them to 0x7 (no access).
* Probe at most the last 4MiB of the address space.
*/
if (map_top < 0xffc00000)
map_top = 0xffc00000;
#endif
/* Loop through and look for rom chips. Since we don't know the
* starting address for each chip, probe every ROM_PROBE_STEP_SIZE
* bytes from the starting address of the window.
*/
while((map_top - 1) < 0xffffffffUL) {
struct cfi_private *cfi;
unsigned long offset;
int i;
if (!map)
map = kmalloc(sizeof(*map), GFP_KERNEL);
if (!map) {
printk(KERN_ERR MOD_NAME ": kmalloc failed");
goto out;
}
memset(map, 0, sizeof(*map));
INIT_LIST_HEAD(&map->list);
map->map.name = map->map_name;
map->map.phys = map_top;
offset = map_top - window->phys;
map->map.virt = (void __iomem *)
(((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08Lx",
MOD_NAME, (unsigned long long)map->map.phys);
/* There is no generic VPP support */
for(map->map.bankwidth = 32; map->map.bankwidth;
map->map.bankwidth >>= 1)
{
char **probe_type;
/* Skip bankwidths that are not supported */
if (!map_bankwidth_supported(map->map.bankwidth))
continue;
/* Setup the map methods */
simple_map_init(&map->map);
/* Try all of the probe methods */
probe_type = rom_probe_types;
for(; *probe_type; probe_type++) {
map->mtd = do_map_probe(*probe_type, &map->map);
if (map->mtd)
goto found;
}
}
map_top += ROM_PROBE_STEP_SIZE;
continue;
found:
/* Trim the size if we are larger than the map */
if (map->mtd->size > map->map.size) {
printk(KERN_WARNING MOD_NAME
" rom(%llu) larger than window(%lu). fixing...\n",
(unsigned long long)map->mtd->size, map->map.size);
map->mtd->size = map->map.size;
}
if (window->rsrc.parent) {
/*
* Registering the MTD device in iomem may not be possible
* if there is a BIOS "reserved" and BUSY range. If this
* fails then continue anyway.
*/
map->rsrc.name = map->map_name;
map->rsrc.start = map->map.phys;
map->rsrc.end = map->map.phys + map->mtd->size - 1;
map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&window->rsrc, &map->rsrc)) {
printk(KERN_ERR MOD_NAME
": cannot reserve MTD resource\n");
map->rsrc.parent = NULL;
}
}
/* Make the whole region visible in the map */
map->map.virt = window->virt;
map->map.phys = window->phys;
cfi = map->map.fldrv_priv;
for(i = 0; i < cfi->numchips; i++)
cfi->chips[i].start += offset;
/* Now that the mtd devices is complete claim and export it */
map->mtd->owner = THIS_MODULE;
if (mtd_device_register(map->mtd, NULL, 0)) {
map_destroy(map->mtd);
map->mtd = NULL;
goto out;
}
/* Calculate the new value of map_top */
map_top += map->mtd->size;
/* File away the map structure */
list_add(&map->list, &window->maps);
map = NULL;
}
out:
/* Free any left over map structures */
kfree(map);
/* See if I have any map structures */
if (list_empty(&window->maps)) {
ck804xrom_cleanup(window);
return -ENODEV;
}
return 0;
}
static void ck804xrom_remove_one(struct pci_dev *pdev)
{
struct ck804xrom_window *window = &ck804xrom_window;
ck804xrom_cleanup(window);
}
static struct pci_device_id ck804xrom_pci_tbl[] = {
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0051), .driver_data = DEV_CK804 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0360), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0361), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0362), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0363), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0364), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0365), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0366), .driver_data = DEV_MCP55 },
{ PCI_DEVICE(PCI_VENDOR_ID_NVIDIA, 0x0367), .driver_data = DEV_MCP55 },
{ 0, }
};
#if 0
MODULE_DEVICE_TABLE(pci, ck804xrom_pci_tbl);
static struct pci_driver ck804xrom_driver = {
.name = MOD_NAME,
.id_table = ck804xrom_pci_tbl,
.probe = ck804xrom_init_one,
.remove = ck804xrom_remove_one,
};
#endif
static int __init init_ck804xrom(void)
{
struct pci_dev *pdev;
struct pci_device_id *id;
int retVal;
pdev = NULL;
for(id = ck804xrom_pci_tbl; id->vendor; id++) {
pdev = pci_get_device(id->vendor, id->device, NULL);
if (pdev)
break;
}
if (pdev) {
retVal = ck804xrom_init_one(pdev, id);
pci_dev_put(pdev);
return retVal;
}
return -ENXIO;
#if 0
return pci_register_driver(&ck804xrom_driver);
#endif
}
static void __exit cleanup_ck804xrom(void)
{
ck804xrom_remove_one(ck804xrom_window.pdev);
}
module_init(init_ck804xrom);
module_exit(cleanup_ck804xrom);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>, Dave Olsen <dolsen@lnxi.com>");
MODULE_DESCRIPTION("MTD map driver for BIOS chips on the Nvidia ck804 southbridge");

231
drivers/mtd/maps/dc21285.c Normal file
View file

@ -0,0 +1,231 @@
/*
* MTD map driver for flash on the DC21285 (the StrongARM-110 companion chip)
*
* (C) 2000 Nicolas Pitre <nico@fluxnic.net>
*
* This code is GPL
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/hardware/dec21285.h>
#include <asm/mach-types.h>
static struct mtd_info *dc21285_mtd;
#ifdef CONFIG_ARCH_NETWINDER
/*
* This is really ugly, but it seams to be the only
* realiable way to do it, as the cpld state machine
* is unpredictible. So we have a 25us penalty per
* write access.
*/
static void nw_en_write(void)
{
unsigned long flags;
/*
* we want to write a bit pattern XXX1 to Xilinx to enable
* the write gate, which will be open for about the next 2ms.
*/
spin_lock_irqsave(&nw_gpio_lock, flags);
nw_cpld_modify(CPLD_FLASH_WR_ENABLE, CPLD_FLASH_WR_ENABLE);
spin_unlock_irqrestore(&nw_gpio_lock, flags);
/*
* let the ISA bus to catch on...
*/
udelay(25);
}
#else
#define nw_en_write() do { } while (0)
#endif
static map_word dc21285_read8(struct map_info *map, unsigned long ofs)
{
map_word val;
val.x[0] = *(uint8_t*)(map->virt + ofs);
return val;
}
static map_word dc21285_read16(struct map_info *map, unsigned long ofs)
{
map_word val;
val.x[0] = *(uint16_t*)(map->virt + ofs);
return val;
}
static map_word dc21285_read32(struct map_info *map, unsigned long ofs)
{
map_word val;
val.x[0] = *(uint32_t*)(map->virt + ofs);
return val;
}
static void dc21285_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
memcpy(to, (void*)(map->virt + from), len);
}
static void dc21285_write8(struct map_info *map, const map_word d, unsigned long adr)
{
if (machine_is_netwinder())
nw_en_write();
*CSR_ROMWRITEREG = adr & 3;
adr &= ~3;
*(uint8_t*)(map->virt + adr) = d.x[0];
}
static void dc21285_write16(struct map_info *map, const map_word d, unsigned long adr)
{
if (machine_is_netwinder())
nw_en_write();
*CSR_ROMWRITEREG = adr & 3;
adr &= ~3;
*(uint16_t*)(map->virt + adr) = d.x[0];
}
static void dc21285_write32(struct map_info *map, const map_word d, unsigned long adr)
{
if (machine_is_netwinder())
nw_en_write();
*(uint32_t*)(map->virt + adr) = d.x[0];
}
static void dc21285_copy_to_32(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
while (len > 0) {
map_word d;
d.x[0] = *((uint32_t*)from);
dc21285_write32(map, d, to);
from += 4;
to += 4;
len -= 4;
}
}
static void dc21285_copy_to_16(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
while (len > 0) {
map_word d;
d.x[0] = *((uint16_t*)from);
dc21285_write16(map, d, to);
from += 2;
to += 2;
len -= 2;
}
}
static void dc21285_copy_to_8(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
map_word d;
d.x[0] = *((uint8_t*)from);
dc21285_write8(map, d, to);
from++;
to++;
len--;
}
static struct map_info dc21285_map = {
.name = "DC21285 flash",
.phys = NO_XIP,
.size = 16*1024*1024,
.copy_from = dc21285_copy_from,
};
/* Partition stuff */
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
static int __init init_dc21285(void)
{
/* Determine bankwidth */
switch (*CSR_SA110_CNTL & (3<<14)) {
case SA110_CNTL_ROMWIDTH_8:
dc21285_map.bankwidth = 1;
dc21285_map.read = dc21285_read8;
dc21285_map.write = dc21285_write8;
dc21285_map.copy_to = dc21285_copy_to_8;
break;
case SA110_CNTL_ROMWIDTH_16:
dc21285_map.bankwidth = 2;
dc21285_map.read = dc21285_read16;
dc21285_map.write = dc21285_write16;
dc21285_map.copy_to = dc21285_copy_to_16;
break;
case SA110_CNTL_ROMWIDTH_32:
dc21285_map.bankwidth = 4;
dc21285_map.read = dc21285_read32;
dc21285_map.write = dc21285_write32;
dc21285_map.copy_to = dc21285_copy_to_32;
break;
default:
printk (KERN_ERR "DC21285 flash: undefined bankwidth\n");
return -ENXIO;
}
printk (KERN_NOTICE "DC21285 flash support (%d-bit bankwidth)\n",
dc21285_map.bankwidth*8);
/* Let's map the flash area */
dc21285_map.virt = ioremap(DC21285_FLASH, 16*1024*1024);
if (!dc21285_map.virt) {
printk("Failed to ioremap\n");
return -EIO;
}
if (machine_is_ebsa285()) {
dc21285_mtd = do_map_probe("cfi_probe", &dc21285_map);
} else {
dc21285_mtd = do_map_probe("jedec_probe", &dc21285_map);
}
if (!dc21285_mtd) {
iounmap(dc21285_map.virt);
return -ENXIO;
}
dc21285_mtd->owner = THIS_MODULE;
mtd_device_parse_register(dc21285_mtd, probes, NULL, NULL, 0);
if(machine_is_ebsa285()) {
/*
* Flash timing is determined with bits 19-16 of the
* CSR_SA110_CNTL. The value is the number of wait cycles, or
* 0 for 16 cycles (the default). Cycles are 20 ns.
* Here we use 7 for 140 ns flash chips.
*/
/* access time */
*CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x000f0000) | (7 << 16));
/* burst time */
*CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x00f00000) | (7 << 20));
/* tristate time */
*CSR_SA110_CNTL = ((*CSR_SA110_CNTL & ~0x0f000000) | (7 << 24));
}
return 0;
}
static void __exit cleanup_dc21285(void)
{
mtd_device_unregister(dc21285_mtd);
map_destroy(dc21285_mtd);
iounmap(dc21285_map.virt);
}
module_init(init_dc21285);
module_exit(cleanup_dc21285);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net>");
MODULE_DESCRIPTION("MTD map driver for DC21285 boards");

452
drivers/mtd/maps/esb2rom.c Normal file
View file

@ -0,0 +1,452 @@
/*
* esb2rom.c
*
* Normal mappings of flash chips in physical memory
* through the Intel ESB2 Southbridge.
*
* This was derived from ichxrom.c in May 2006 by
* Lew Glendenning <lglendenning@lnxi.com>
*
* Eric Biederman, of course, was a major help in this effort.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/flashchip.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/list.h>
#define MOD_NAME KBUILD_BASENAME
#define ADDRESS_NAME_LEN 18
#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
#define BIOS_CNTL 0xDC
#define BIOS_LOCK_ENABLE 0x02
#define BIOS_WRITE_ENABLE 0x01
/* This became a 16-bit register, and EN2 has disappeared */
#define FWH_DEC_EN1 0xD8
#define FWH_F8_EN 0x8000
#define FWH_F0_EN 0x4000
#define FWH_E8_EN 0x2000
#define FWH_E0_EN 0x1000
#define FWH_D8_EN 0x0800
#define FWH_D0_EN 0x0400
#define FWH_C8_EN 0x0200
#define FWH_C0_EN 0x0100
#define FWH_LEGACY_F_EN 0x0080
#define FWH_LEGACY_E_EN 0x0040
/* reserved 0x0020 and 0x0010 */
#define FWH_70_EN 0x0008
#define FWH_60_EN 0x0004
#define FWH_50_EN 0x0002
#define FWH_40_EN 0x0001
/* these are 32-bit values */
#define FWH_SEL1 0xD0
#define FWH_SEL2 0xD4
#define FWH_8MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
FWH_70_EN | FWH_60_EN | FWH_50_EN | FWH_40_EN)
#define FWH_7MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
FWH_70_EN | FWH_60_EN | FWH_50_EN)
#define FWH_6MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
FWH_70_EN | FWH_60_EN)
#define FWH_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN | \
FWH_70_EN)
#define FWH_4MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
FWH_D8_EN | FWH_D0_EN | FWH_C8_EN | FWH_C0_EN)
#define FWH_3_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
FWH_D8_EN | FWH_D0_EN | FWH_C8_EN)
#define FWH_3MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
FWH_D8_EN | FWH_D0_EN)
#define FWH_2_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN | \
FWH_D8_EN)
#define FWH_2MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN | FWH_E0_EN)
#define FWH_1_5MiB (FWH_F8_EN | FWH_F0_EN | FWH_E8_EN)
#define FWH_1MiB (FWH_F8_EN | FWH_F0_EN)
#define FWH_0_5MiB (FWH_F8_EN)
struct esb2rom_window {
void __iomem* virt;
unsigned long phys;
unsigned long size;
struct list_head maps;
struct resource rsrc;
struct pci_dev *pdev;
};
struct esb2rom_map_info {
struct list_head list;
struct map_info map;
struct mtd_info *mtd;
struct resource rsrc;
char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
};
static struct esb2rom_window esb2rom_window = {
.maps = LIST_HEAD_INIT(esb2rom_window.maps),
};
static void esb2rom_cleanup(struct esb2rom_window *window)
{
struct esb2rom_map_info *map, *scratch;
u8 byte;
/* Disable writes through the rom window */
pci_read_config_byte(window->pdev, BIOS_CNTL, &byte);
pci_write_config_byte(window->pdev, BIOS_CNTL,
byte & ~BIOS_WRITE_ENABLE);
/* Free all of the mtd devices */
list_for_each_entry_safe(map, scratch, &window->maps, list) {
if (map->rsrc.parent)
release_resource(&map->rsrc);
mtd_device_unregister(map->mtd);
map_destroy(map->mtd);
list_del(&map->list);
kfree(map);
}
if (window->rsrc.parent)
release_resource(&window->rsrc);
if (window->virt) {
iounmap(window->virt);
window->virt = NULL;
window->phys = 0;
window->size = 0;
}
pci_dev_put(window->pdev);
}
static int esb2rom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
struct esb2rom_window *window = &esb2rom_window;
struct esb2rom_map_info *map = NULL;
unsigned long map_top;
u8 byte;
u16 word;
/* For now I just handle the ecb2 and I assume there
* are not a lot of resources up at the top of the address
* space. It is possible to handle other devices in the
* top 16MiB but it is very painful. Also since
* you can only really attach a FWH to an ICHX there
* a number of simplifications you can make.
*
* Also you can page firmware hubs if an 8MiB window isn't enough
* but don't currently handle that case either.
*/
window->pdev = pci_dev_get(pdev);
/* RLG: experiment 2. Force the window registers to the widest values */
/*
pci_read_config_word(pdev, FWH_DEC_EN1, &word);
printk(KERN_DEBUG "Original FWH_DEC_EN1 : %x\n", word);
pci_write_config_byte(pdev, FWH_DEC_EN1, 0xff);
pci_read_config_byte(pdev, FWH_DEC_EN1, &byte);
printk(KERN_DEBUG "New FWH_DEC_EN1 : %x\n", byte);
pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
printk(KERN_DEBUG "Original FWH_DEC_EN2 : %x\n", byte);
pci_write_config_byte(pdev, FWH_DEC_EN2, 0x0f);
pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
printk(KERN_DEBUG "New FWH_DEC_EN2 : %x\n", byte);
*/
/* Find a region continuous to the end of the ROM window */
window->phys = 0;
pci_read_config_word(pdev, FWH_DEC_EN1, &word);
printk(KERN_DEBUG "pci_read_config_word : %x\n", word);
if ((word & FWH_8MiB) == FWH_8MiB)
window->phys = 0xff400000;
else if ((word & FWH_7MiB) == FWH_7MiB)
window->phys = 0xff500000;
else if ((word & FWH_6MiB) == FWH_6MiB)
window->phys = 0xff600000;
else if ((word & FWH_5MiB) == FWH_5MiB)
window->phys = 0xFF700000;
else if ((word & FWH_4MiB) == FWH_4MiB)
window->phys = 0xffc00000;
else if ((word & FWH_3_5MiB) == FWH_3_5MiB)
window->phys = 0xffc80000;
else if ((word & FWH_3MiB) == FWH_3MiB)
window->phys = 0xffd00000;
else if ((word & FWH_2_5MiB) == FWH_2_5MiB)
window->phys = 0xffd80000;
else if ((word & FWH_2MiB) == FWH_2MiB)
window->phys = 0xffe00000;
else if ((word & FWH_1_5MiB) == FWH_1_5MiB)
window->phys = 0xffe80000;
else if ((word & FWH_1MiB) == FWH_1MiB)
window->phys = 0xfff00000;
else if ((word & FWH_0_5MiB) == FWH_0_5MiB)
window->phys = 0xfff80000;
if (window->phys == 0) {
printk(KERN_ERR MOD_NAME ": Rom window is closed\n");
goto out;
}
/* reserved 0x0020 and 0x0010 */
window->phys -= 0x400000UL;
window->size = (0xffffffffUL - window->phys) + 1UL;
/* Enable writes through the rom window */
pci_read_config_byte(pdev, BIOS_CNTL, &byte);
if (!(byte & BIOS_WRITE_ENABLE) && (byte & (BIOS_LOCK_ENABLE))) {
/* The BIOS will generate an error if I enable
* this device, so don't even try.
*/
printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
goto out;
}
pci_write_config_byte(pdev, BIOS_CNTL, byte | BIOS_WRITE_ENABLE);
/*
* Try to reserve the window mem region. If this fails then
* it is likely due to the window being "reseved" by the BIOS.
*/
window->rsrc.name = MOD_NAME;
window->rsrc.start = window->phys;
window->rsrc.end = window->phys + window->size - 1;
window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&iomem_resource, &window->rsrc)) {
window->rsrc.parent = NULL;
printk(KERN_DEBUG MOD_NAME ": "
"%s(): Unable to register resource %pR - kernel bug?\n",
__func__, &window->rsrc);
}
/* Map the firmware hub into my address space. */
window->virt = ioremap_nocache(window->phys, window->size);
if (!window->virt) {
printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
window->phys, window->size);
goto out;
}
/* Get the first address to look for an rom chip at */
map_top = window->phys;
if ((window->phys & 0x3fffff) != 0) {
/* if not aligned on 4MiB, look 4MiB lower in address space */
map_top = window->phys + 0x400000;
}
#if 1
/* The probe sequence run over the firmware hub lock
* registers sets them to 0x7 (no access).
* (Insane hardware design, but most copied Intel's.)
* ==> Probe at most the last 4M of the address space.
*/
if (map_top < 0xffc00000)
map_top = 0xffc00000;
#endif
/* Loop through and look for rom chips */
while ((map_top - 1) < 0xffffffffUL) {
struct cfi_private *cfi;
unsigned long offset;
int i;
if (!map)
map = kmalloc(sizeof(*map), GFP_KERNEL);
if (!map) {
printk(KERN_ERR MOD_NAME ": kmalloc failed");
goto out;
}
memset(map, 0, sizeof(*map));
INIT_LIST_HEAD(&map->list);
map->map.name = map->map_name;
map->map.phys = map_top;
offset = map_top - window->phys;
map->map.virt = (void __iomem *)
(((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08Lx",
MOD_NAME, (unsigned long long)map->map.phys);
/* Firmware hubs only use vpp when being programmed
* in a factory setting. So in-place programming
* needs to use a different method.
*/
for(map->map.bankwidth = 32; map->map.bankwidth;
map->map.bankwidth >>= 1) {
char **probe_type;
/* Skip bankwidths that are not supported */
if (!map_bankwidth_supported(map->map.bankwidth))
continue;
/* Setup the map methods */
simple_map_init(&map->map);
/* Try all of the probe methods */
probe_type = rom_probe_types;
for(; *probe_type; probe_type++) {
map->mtd = do_map_probe(*probe_type, &map->map);
if (map->mtd)
goto found;
}
}
map_top += ROM_PROBE_STEP_SIZE;
continue;
found:
/* Trim the size if we are larger than the map */
if (map->mtd->size > map->map.size) {
printk(KERN_WARNING MOD_NAME
" rom(%llu) larger than window(%lu). fixing...\n",
(unsigned long long)map->mtd->size, map->map.size);
map->mtd->size = map->map.size;
}
if (window->rsrc.parent) {
/*
* Registering the MTD device in iomem may not be possible
* if there is a BIOS "reserved" and BUSY range. If this
* fails then continue anyway.
*/
map->rsrc.name = map->map_name;
map->rsrc.start = map->map.phys;
map->rsrc.end = map->map.phys + map->mtd->size - 1;
map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&window->rsrc, &map->rsrc)) {
printk(KERN_ERR MOD_NAME
": cannot reserve MTD resource\n");
map->rsrc.parent = NULL;
}
}
/* Make the whole region visible in the map */
map->map.virt = window->virt;
map->map.phys = window->phys;
cfi = map->map.fldrv_priv;
for(i = 0; i < cfi->numchips; i++)
cfi->chips[i].start += offset;
/* Now that the mtd devices is complete claim and export it */
map->mtd->owner = THIS_MODULE;
if (mtd_device_register(map->mtd, NULL, 0)) {
map_destroy(map->mtd);
map->mtd = NULL;
goto out;
}
/* Calculate the new value of map_top */
map_top += map->mtd->size;
/* File away the map structure */
list_add(&map->list, &window->maps);
map = NULL;
}
out:
/* Free any left over map structures */
kfree(map);
/* See if I have any map structures */
if (list_empty(&window->maps)) {
esb2rom_cleanup(window);
return -ENODEV;
}
return 0;
}
static void esb2rom_remove_one(struct pci_dev *pdev)
{
struct esb2rom_window *window = &esb2rom_window;
esb2rom_cleanup(window);
}
static struct pci_device_id esb2rom_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB2_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ 0, },
};
#if 0
MODULE_DEVICE_TABLE(pci, esb2rom_pci_tbl);
static struct pci_driver esb2rom_driver = {
.name = MOD_NAME,
.id_table = esb2rom_pci_tbl,
.probe = esb2rom_init_one,
.remove = esb2rom_remove_one,
};
#endif
static int __init init_esb2rom(void)
{
struct pci_dev *pdev;
struct pci_device_id *id;
int retVal;
pdev = NULL;
for (id = esb2rom_pci_tbl; id->vendor; id++) {
printk(KERN_DEBUG "device id = %x\n", id->device);
pdev = pci_get_device(id->vendor, id->device, NULL);
if (pdev) {
printk(KERN_DEBUG "matched device = %x\n", id->device);
break;
}
}
if (pdev) {
printk(KERN_DEBUG "matched device id %x\n", id->device);
retVal = esb2rom_init_one(pdev, &esb2rom_pci_tbl[0]);
pci_dev_put(pdev);
printk(KERN_DEBUG "retVal = %d\n", retVal);
return retVal;
}
return -ENXIO;
#if 0
return pci_register_driver(&esb2rom_driver);
#endif
}
static void __exit cleanup_esb2rom(void)
{
esb2rom_remove_one(esb2rom_window.pdev);
}
module_init(init_esb2rom);
module_exit(cleanup_esb2rom);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Lew Glendenning <lglendenning@lnxi.com>");
MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ESB2 southbridge");

View file

@ -0,0 +1,302 @@
/*
* drivers/mtd/maps/gpio-addr-flash.c
*
* Handle the case where a flash device is mostly addressed using physical
* line and supplemented by GPIOs. This way you can hook up say a 8MiB flash
* to a 2MiB memory range and use the GPIOs to select a particular range.
*
* Copyright © 2000 Nicolas Pitre <nico@cam.org>
* Copyright © 2005-2009 Analog Devices Inc.
*
* Enter bugs at http://blackfin.uclinux.org/
*
* Licensed under the GPL-2 or later.
*/
#include <linux/gpio.h>
#include <linux/io.h>
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/platform_device.h>
#include <linux/slab.h>
#include <linux/types.h>
#define pr_devinit(fmt, args...) \
({ static const char __fmt[] = fmt; printk(__fmt, ## args); })
#define DRIVER_NAME "gpio-addr-flash"
#define PFX DRIVER_NAME ": "
/**
* struct async_state - keep GPIO flash state
* @mtd: MTD state for this mapping
* @map: MTD map state for this flash
* @gpio_count: number of GPIOs used to address
* @gpio_addrs: array of GPIOs to twiddle
* @gpio_values: cached GPIO values
* @win_size: dedicated memory size (if no GPIOs)
*/
struct async_state {
struct mtd_info *mtd;
struct map_info map;
size_t gpio_count;
unsigned *gpio_addrs;
int *gpio_values;
unsigned long win_size;
};
#define gf_map_info_to_state(mi) ((struct async_state *)(mi)->map_priv_1)
/**
* gf_set_gpios() - set GPIO address lines to access specified flash offset
* @state: GPIO flash state
* @ofs: desired offset to access
*
* Rather than call the GPIO framework every time, cache the last-programmed
* value. This speeds up sequential accesses (which are by far the most common
* type). We rely on the GPIO framework to treat non-zero value as high so
* that we don't have to normalize the bits.
*/
static void gf_set_gpios(struct async_state *state, unsigned long ofs)
{
size_t i = 0;
int value;
ofs /= state->win_size;
do {
value = ofs & (1 << i);
if (state->gpio_values[i] != value) {
gpio_set_value(state->gpio_addrs[i], value);
state->gpio_values[i] = value;
}
} while (++i < state->gpio_count);
}
/**
* gf_read() - read a word at the specified offset
* @map: MTD map state
* @ofs: desired offset to read
*/
static map_word gf_read(struct map_info *map, unsigned long ofs)
{
struct async_state *state = gf_map_info_to_state(map);
uint16_t word;
map_word test;
gf_set_gpios(state, ofs);
word = readw(map->virt + (ofs % state->win_size));
test.x[0] = word;
return test;
}
/**
* gf_copy_from() - copy a chunk of data from the flash
* @map: MTD map state
* @to: memory to copy to
* @from: flash offset to copy from
* @len: how much to copy
*
* The "from" region may straddle more than one window, so toggle the GPIOs for
* each window region before reading its data.
*/
static void gf_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
struct async_state *state = gf_map_info_to_state(map);
int this_len;
while (len) {
if ((from % state->win_size) + len > state->win_size)
this_len = state->win_size - (from % state->win_size);
else
this_len = len;
gf_set_gpios(state, from);
memcpy_fromio(to, map->virt + (from % state->win_size),
this_len);
len -= this_len;
from += this_len;
to += this_len;
}
}
/**
* gf_write() - write a word at the specified offset
* @map: MTD map state
* @ofs: desired offset to write
*/
static void gf_write(struct map_info *map, map_word d1, unsigned long ofs)
{
struct async_state *state = gf_map_info_to_state(map);
uint16_t d;
gf_set_gpios(state, ofs);
d = d1.x[0];
writew(d, map->virt + (ofs % state->win_size));
}
/**
* gf_copy_to() - copy a chunk of data to the flash
* @map: MTD map state
* @to: flash offset to copy to
* @from: memory to copy from
* @len: how much to copy
*
* See gf_copy_from() caveat.
*/
static void gf_copy_to(struct map_info *map, unsigned long to,
const void *from, ssize_t len)
{
struct async_state *state = gf_map_info_to_state(map);
int this_len;
while (len) {
if ((to % state->win_size) + len > state->win_size)
this_len = state->win_size - (to % state->win_size);
else
this_len = len;
gf_set_gpios(state, to);
memcpy_toio(map->virt + (to % state->win_size), from, len);
len -= this_len;
to += this_len;
from += this_len;
}
}
static const char * const part_probe_types[] = {
"cmdlinepart", "RedBoot", NULL };
/**
* gpio_flash_probe() - setup a mapping for a GPIO assisted flash
* @pdev: platform device
*
* The platform resource layout expected looks something like:
* struct mtd_partition partitions[] = { ... };
* struct physmap_flash_data flash_data = { ... };
* unsigned flash_gpios[] = { GPIO_XX, GPIO_XX, ... };
* struct resource flash_resource[] = {
* {
* .name = "cfi_probe",
* .start = 0x20000000,
* .end = 0x201fffff,
* .flags = IORESOURCE_MEM,
* }, {
* .start = (unsigned long)flash_gpios,
* .end = ARRAY_SIZE(flash_gpios),
* .flags = IORESOURCE_IRQ,
* }
* };
* struct platform_device flash_device = {
* .name = "gpio-addr-flash",
* .dev = { .platform_data = &flash_data, },
* .num_resources = ARRAY_SIZE(flash_resource),
* .resource = flash_resource,
* ...
* };
*/
static int gpio_flash_probe(struct platform_device *pdev)
{
size_t i, arr_size;
struct physmap_flash_data *pdata;
struct resource *memory;
struct resource *gpios;
struct async_state *state;
pdata = dev_get_platdata(&pdev->dev);
memory = platform_get_resource(pdev, IORESOURCE_MEM, 0);
gpios = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
if (!memory || !gpios || !gpios->end)
return -EINVAL;
arr_size = sizeof(int) * gpios->end;
state = kzalloc(sizeof(*state) + arr_size, GFP_KERNEL);
if (!state)
return -ENOMEM;
/*
* We cast start/end to known types in the boards file, so cast
* away their pointer types here to the known types (gpios->xxx).
*/
state->gpio_count = gpios->end;
state->gpio_addrs = (void *)(unsigned long)gpios->start;
state->gpio_values = (void *)(state + 1);
state->win_size = resource_size(memory);
memset(state->gpio_values, 0xff, arr_size);
state->map.name = DRIVER_NAME;
state->map.read = gf_read;
state->map.copy_from = gf_copy_from;
state->map.write = gf_write;
state->map.copy_to = gf_copy_to;
state->map.bankwidth = pdata->width;
state->map.size = state->win_size * (1 << state->gpio_count);
state->map.virt = ioremap_nocache(memory->start, state->map.size);
state->map.phys = NO_XIP;
state->map.map_priv_1 = (unsigned long)state;
platform_set_drvdata(pdev, state);
i = 0;
do {
if (gpio_request(state->gpio_addrs[i], DRIVER_NAME)) {
pr_devinit(KERN_ERR PFX "failed to request gpio %d\n",
state->gpio_addrs[i]);
while (i--)
gpio_free(state->gpio_addrs[i]);
kfree(state);
return -EBUSY;
}
gpio_direction_output(state->gpio_addrs[i], 0);
} while (++i < state->gpio_count);
pr_devinit(KERN_NOTICE PFX "probing %d-bit flash bus\n",
state->map.bankwidth * 8);
state->mtd = do_map_probe(memory->name, &state->map);
if (!state->mtd) {
for (i = 0; i < state->gpio_count; ++i)
gpio_free(state->gpio_addrs[i]);
kfree(state);
return -ENXIO;
}
mtd_device_parse_register(state->mtd, part_probe_types, NULL,
pdata->parts, pdata->nr_parts);
return 0;
}
static int gpio_flash_remove(struct platform_device *pdev)
{
struct async_state *state = platform_get_drvdata(pdev);
size_t i = 0;
do {
gpio_free(state->gpio_addrs[i]);
} while (++i < state->gpio_count);
mtd_device_unregister(state->mtd);
map_destroy(state->mtd);
kfree(state);
return 0;
}
static struct platform_driver gpio_flash_driver = {
.probe = gpio_flash_probe,
.remove = gpio_flash_remove,
.driver = {
.name = DRIVER_NAME,
},
};
module_platform_driver(gpio_flash_driver);
MODULE_AUTHOR("Mike Frysinger <vapier@gentoo.org>");
MODULE_DESCRIPTION("MTD map driver for flashes addressed physically and with gpios");
MODULE_LICENSE("GPL");

380
drivers/mtd/maps/ichxrom.c Normal file
View file

@ -0,0 +1,380 @@
/*
* ichxrom.c
*
* Normal mappings of chips in physical memory
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/flashchip.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#include <linux/list.h>
#define xstr(s) str(s)
#define str(s) #s
#define MOD_NAME xstr(KBUILD_BASENAME)
#define ADDRESS_NAME_LEN 18
#define ROM_PROBE_STEP_SIZE (64*1024) /* 64KiB */
#define BIOS_CNTL 0x4e
#define FWH_DEC_EN1 0xE3
#define FWH_DEC_EN2 0xF0
#define FWH_SEL1 0xE8
#define FWH_SEL2 0xEE
struct ichxrom_window {
void __iomem* virt;
unsigned long phys;
unsigned long size;
struct list_head maps;
struct resource rsrc;
struct pci_dev *pdev;
};
struct ichxrom_map_info {
struct list_head list;
struct map_info map;
struct mtd_info *mtd;
struct resource rsrc;
char map_name[sizeof(MOD_NAME) + 2 + ADDRESS_NAME_LEN];
};
static struct ichxrom_window ichxrom_window = {
.maps = LIST_HEAD_INIT(ichxrom_window.maps),
};
static void ichxrom_cleanup(struct ichxrom_window *window)
{
struct ichxrom_map_info *map, *scratch;
u16 word;
/* Disable writes through the rom window */
pci_read_config_word(window->pdev, BIOS_CNTL, &word);
pci_write_config_word(window->pdev, BIOS_CNTL, word & ~1);
pci_dev_put(window->pdev);
/* Free all of the mtd devices */
list_for_each_entry_safe(map, scratch, &window->maps, list) {
if (map->rsrc.parent)
release_resource(&map->rsrc);
mtd_device_unregister(map->mtd);
map_destroy(map->mtd);
list_del(&map->list);
kfree(map);
}
if (window->rsrc.parent)
release_resource(&window->rsrc);
if (window->virt) {
iounmap(window->virt);
window->virt = NULL;
window->phys = 0;
window->size = 0;
window->pdev = NULL;
}
}
static int ichxrom_init_one(struct pci_dev *pdev,
const struct pci_device_id *ent)
{
static char *rom_probe_types[] = { "cfi_probe", "jedec_probe", NULL };
struct ichxrom_window *window = &ichxrom_window;
struct ichxrom_map_info *map = NULL;
unsigned long map_top;
u8 byte;
u16 word;
/* For now I just handle the ichx and I assume there
* are not a lot of resources up at the top of the address
* space. It is possible to handle other devices in the
* top 16MB but it is very painful. Also since
* you can only really attach a FWH to an ICHX there
* a number of simplifications you can make.
*
* Also you can page firmware hubs if an 8MB window isn't enough
* but don't currently handle that case either.
*/
window->pdev = pdev;
/* Find a region continuous to the end of the ROM window */
window->phys = 0;
pci_read_config_byte(pdev, FWH_DEC_EN1, &byte);
if (byte == 0xff) {
window->phys = 0xffc00000;
pci_read_config_byte(pdev, FWH_DEC_EN2, &byte);
if ((byte & 0x0f) == 0x0f) {
window->phys = 0xff400000;
}
else if ((byte & 0x0e) == 0x0e) {
window->phys = 0xff500000;
}
else if ((byte & 0x0c) == 0x0c) {
window->phys = 0xff600000;
}
else if ((byte & 0x08) == 0x08) {
window->phys = 0xff700000;
}
}
else if ((byte & 0xfe) == 0xfe) {
window->phys = 0xffc80000;
}
else if ((byte & 0xfc) == 0xfc) {
window->phys = 0xffd00000;
}
else if ((byte & 0xf8) == 0xf8) {
window->phys = 0xffd80000;
}
else if ((byte & 0xf0) == 0xf0) {
window->phys = 0xffe00000;
}
else if ((byte & 0xe0) == 0xe0) {
window->phys = 0xffe80000;
}
else if ((byte & 0xc0) == 0xc0) {
window->phys = 0xfff00000;
}
else if ((byte & 0x80) == 0x80) {
window->phys = 0xfff80000;
}
if (window->phys == 0) {
printk(KERN_ERR MOD_NAME ": Rom window is closed\n");
goto out;
}
window->phys -= 0x400000UL;
window->size = (0xffffffffUL - window->phys) + 1UL;
/* Enable writes through the rom window */
pci_read_config_word(pdev, BIOS_CNTL, &word);
if (!(word & 1) && (word & (1<<1))) {
/* The BIOS will generate an error if I enable
* this device, so don't even try.
*/
printk(KERN_ERR MOD_NAME ": firmware access control, I can't enable writes\n");
goto out;
}
pci_write_config_word(pdev, BIOS_CNTL, word | 1);
/*
* Try to reserve the window mem region. If this fails then
* it is likely due to the window being "reseved" by the BIOS.
*/
window->rsrc.name = MOD_NAME;
window->rsrc.start = window->phys;
window->rsrc.end = window->phys + window->size - 1;
window->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&iomem_resource, &window->rsrc)) {
window->rsrc.parent = NULL;
printk(KERN_DEBUG MOD_NAME ": "
"%s(): Unable to register resource %pR - kernel bug?\n",
__func__, &window->rsrc);
}
/* Map the firmware hub into my address space. */
window->virt = ioremap_nocache(window->phys, window->size);
if (!window->virt) {
printk(KERN_ERR MOD_NAME ": ioremap(%08lx, %08lx) failed\n",
window->phys, window->size);
goto out;
}
/* Get the first address to look for an rom chip at */
map_top = window->phys;
if ((window->phys & 0x3fffff) != 0) {
map_top = window->phys + 0x400000;
}
#if 1
/* The probe sequence run over the firmware hub lock
* registers sets them to 0x7 (no access).
* Probe at most the last 4M of the address space.
*/
if (map_top < 0xffc00000) {
map_top = 0xffc00000;
}
#endif
/* Loop through and look for rom chips */
while((map_top - 1) < 0xffffffffUL) {
struct cfi_private *cfi;
unsigned long offset;
int i;
if (!map) {
map = kmalloc(sizeof(*map), GFP_KERNEL);
}
if (!map) {
printk(KERN_ERR MOD_NAME ": kmalloc failed");
goto out;
}
memset(map, 0, sizeof(*map));
INIT_LIST_HEAD(&map->list);
map->map.name = map->map_name;
map->map.phys = map_top;
offset = map_top - window->phys;
map->map.virt = (void __iomem *)
(((unsigned long)(window->virt)) + offset);
map->map.size = 0xffffffffUL - map_top + 1UL;
/* Set the name of the map to the address I am trying */
sprintf(map->map_name, "%s @%08Lx",
MOD_NAME, (unsigned long long)map->map.phys);
/* Firmware hubs only use vpp when being programmed
* in a factory setting. So in-place programming
* needs to use a different method.
*/
for(map->map.bankwidth = 32; map->map.bankwidth;
map->map.bankwidth >>= 1)
{
char **probe_type;
/* Skip bankwidths that are not supported */
if (!map_bankwidth_supported(map->map.bankwidth))
continue;
/* Setup the map methods */
simple_map_init(&map->map);
/* Try all of the probe methods */
probe_type = rom_probe_types;
for(; *probe_type; probe_type++) {
map->mtd = do_map_probe(*probe_type, &map->map);
if (map->mtd)
goto found;
}
}
map_top += ROM_PROBE_STEP_SIZE;
continue;
found:
/* Trim the size if we are larger than the map */
if (map->mtd->size > map->map.size) {
printk(KERN_WARNING MOD_NAME
" rom(%llu) larger than window(%lu). fixing...\n",
(unsigned long long)map->mtd->size, map->map.size);
map->mtd->size = map->map.size;
}
if (window->rsrc.parent) {
/*
* Registering the MTD device in iomem may not be possible
* if there is a BIOS "reserved" and BUSY range. If this
* fails then continue anyway.
*/
map->rsrc.name = map->map_name;
map->rsrc.start = map->map.phys;
map->rsrc.end = map->map.phys + map->mtd->size - 1;
map->rsrc.flags = IORESOURCE_MEM | IORESOURCE_BUSY;
if (request_resource(&window->rsrc, &map->rsrc)) {
printk(KERN_ERR MOD_NAME
": cannot reserve MTD resource\n");
map->rsrc.parent = NULL;
}
}
/* Make the whole region visible in the map */
map->map.virt = window->virt;
map->map.phys = window->phys;
cfi = map->map.fldrv_priv;
for(i = 0; i < cfi->numchips; i++) {
cfi->chips[i].start += offset;
}
/* Now that the mtd devices is complete claim and export it */
map->mtd->owner = THIS_MODULE;
if (mtd_device_register(map->mtd, NULL, 0)) {
map_destroy(map->mtd);
map->mtd = NULL;
goto out;
}
/* Calculate the new value of map_top */
map_top += map->mtd->size;
/* File away the map structure */
list_add(&map->list, &window->maps);
map = NULL;
}
out:
/* Free any left over map structures */
kfree(map);
/* See if I have any map structures */
if (list_empty(&window->maps)) {
ichxrom_cleanup(window);
return -ENODEV;
}
return 0;
}
static void ichxrom_remove_one(struct pci_dev *pdev)
{
struct ichxrom_window *window = &ichxrom_window;
ichxrom_cleanup(window);
}
static struct pci_device_id ichxrom_pci_tbl[] = {
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801BA_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801CA_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801DB_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82801EB_0,
PCI_ANY_ID, PCI_ANY_ID, },
{ PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_ESB_1,
PCI_ANY_ID, PCI_ANY_ID, },
{ 0, },
};
#if 0
MODULE_DEVICE_TABLE(pci, ichxrom_pci_tbl);
static struct pci_driver ichxrom_driver = {
.name = MOD_NAME,
.id_table = ichxrom_pci_tbl,
.probe = ichxrom_init_one,
.remove = ichxrom_remove_one,
};
#endif
static int __init init_ichxrom(void)
{
struct pci_dev *pdev;
struct pci_device_id *id;
pdev = NULL;
for (id = ichxrom_pci_tbl; id->vendor; id++) {
pdev = pci_get_device(id->vendor, id->device, NULL);
if (pdev) {
break;
}
}
if (pdev) {
return ichxrom_init_one(pdev, &ichxrom_pci_tbl[0]);
}
return -ENXIO;
#if 0
return pci_register_driver(&ichxrom_driver);
#endif
}
static void __exit cleanup_ichxrom(void)
{
ichxrom_remove_one(ichxrom_window.pdev);
}
module_init(init_ichxrom);
module_exit(cleanup_ichxrom);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Eric Biederman <ebiederman@lnxi.com>");
MODULE_DESCRIPTION("MTD map driver for BIOS chips on the ICHX southbridge");

119
drivers/mtd/maps/impa7.c Normal file
View file

@ -0,0 +1,119 @@
/*
* 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 <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#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
#define MSG_PREFIX "impA7:" /* prefix for our printk()'s */
#define MTDID "impa7-%d" /* for mtdparts= partitioning */
static struct mtd_info *impa7_mtd[NUM_FLASHBANKS];
static const char * const rom_probe_types[] = { "jedec_probe", NULL };
static struct map_info impa7_map[NUM_FLASHBANKS] = {
{
.name = "impA7 NOR Flash Bank #0",
.size = WINDOW_SIZE0,
.bankwidth = BUSWIDTH,
},
{
.name = "impA7 NOR Flash Bank #1",
.size = WINDOW_SIZE1,
.bankwidth = BUSWIDTH,
},
};
/*
* MTD partitioning stuff
*/
static struct mtd_partition partitions[] =
{
{
.name = "FileSystem",
.size = 0x800000,
.offset = 0x00000000
},
};
static int __init init_impa7(void)
{
const char * const *type;
int i;
static struct { u_long addr; u_long size; } pt[NUM_FLASHBANKS] = {
{ WINDOW_ADDR0, WINDOW_SIZE0 },
{ WINDOW_ADDR1, WINDOW_SIZE1 },
};
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].phys = pt[i].addr;
impa7_map[i].virt = ioremap(pt[i].addr, pt[i].size);
if (!impa7_map[i].virt) {
printk(MSG_PREFIX "failed to ioremap\n");
return -EIO;
}
simple_map_init(&impa7_map[i]);
impa7_mtd[i] = NULL;
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]->owner = THIS_MODULE;
devicesfound++;
mtd_device_parse_register(impa7_mtd[i], NULL, NULL,
partitions,
ARRAY_SIZE(partitions));
} else {
iounmap((void __iomem *)impa7_map[i].virt);
}
}
return devicesfound == 0 ? -ENXIO : 0;
}
static void __exit cleanup_impa7(void)
{
int i;
for (i=0; i<NUM_FLASHBANKS; i++) {
if (impa7_mtd[i]) {
mtd_device_unregister(impa7_mtd[i]);
map_destroy(impa7_mtd[i]);
iounmap((void __iomem *)impa7_map[i].virt);
impa7_map[i].virt = NULL;
}
}
}
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");

View file

@ -0,0 +1,265 @@
/*
* drivers/mtd/maps/intel_vr_nor.c
*
* An MTD map driver for a NOR flash bank on the Expansion Bus of the Intel
* Vermilion Range chipset.
*
* The Vermilion Range Expansion Bus supports four chip selects, each of which
* has 64MiB of address space. The 2nd BAR of the Expansion Bus PCI Device
* is a 256MiB memory region containing the address spaces for all four of the
* chip selects, with start addresses hardcoded on 64MiB boundaries.
*
* This map driver only supports NOR flash on chip select 0. The buswidth
* (either 8 bits or 16 bits) is determined by reading the Expansion Bus Timing
* and Control Register for Chip Select 0 (EXP_TIMING_CS0). This driver does
* not modify the value in the EXP_TIMING_CS0 register except to enable writing
* and disable boot acceleration. The timing parameters in the register are
* assumed to have been properly initialized by the BIOS. The reset default
* timing parameters are maximally conservative (slow), so access to the flash
* will be slower than it should be if the BIOS has not initialized the timing
* parameters.
*
* Author: Andy Lowe <alowe@mvista.com>
*
* 2006 (c) MontaVista Software, Inc. This file is licensed under
* the terms of the GNU General Public License version 2. This program
* is licensed "as is" without any warranty of any kind, whether express
* or implied.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/pci.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/cfi.h>
#include <linux/mtd/flashchip.h>
#define DRV_NAME "vr_nor"
struct vr_nor_mtd {
void __iomem *csr_base;
struct map_info map;
struct mtd_info *info;
struct pci_dev *dev;
};
/* Expansion Bus Configuration and Status Registers are in BAR 0 */
#define EXP_CSR_MBAR 0
/* Expansion Bus Memory Window is BAR 1 */
#define EXP_WIN_MBAR 1
/* Maximum address space for Chip Select 0 is 64MiB */
#define CS0_SIZE 0x04000000
/* Chip Select 0 is at offset 0 in the Memory Window */
#define CS0_START 0x0
/* Chip Select 0 Timing Register is at offset 0 in CSR */
#define EXP_TIMING_CS0 0x00
#define TIMING_CS_EN (1 << 31) /* Chip Select Enable */
#define TIMING_BOOT_ACCEL_DIS (1 << 8) /* Boot Acceleration Disable */
#define TIMING_WR_EN (1 << 1) /* Write Enable */
#define TIMING_BYTE_EN (1 << 0) /* 8-bit vs 16-bit bus */
#define TIMING_MASK 0x3FFF0000
static void vr_nor_destroy_partitions(struct vr_nor_mtd *p)
{
mtd_device_unregister(p->info);
}
static int vr_nor_init_partitions(struct vr_nor_mtd *p)
{
/* register the flash bank */
/* partition the flash bank */
return mtd_device_parse_register(p->info, NULL, NULL, NULL, 0);
}
static void vr_nor_destroy_mtd_setup(struct vr_nor_mtd *p)
{
map_destroy(p->info);
}
static int vr_nor_mtd_setup(struct vr_nor_mtd *p)
{
static const char * const probe_types[] =
{ "cfi_probe", "jedec_probe", NULL };
const char * const *type;
for (type = probe_types; !p->info && *type; type++)
p->info = do_map_probe(*type, &p->map);
if (!p->info)
return -ENODEV;
p->info->owner = THIS_MODULE;
return 0;
}
static void vr_nor_destroy_maps(struct vr_nor_mtd *p)
{
unsigned int exp_timing_cs0;
/* write-protect the flash bank */
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
exp_timing_cs0 &= ~TIMING_WR_EN;
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
/* unmap the flash window */
iounmap(p->map.virt);
/* unmap the csr window */
iounmap(p->csr_base);
}
/*
* Initialize the map_info structure and map the flash.
* Returns 0 on success, nonzero otherwise.
*/
static int vr_nor_init_maps(struct vr_nor_mtd *p)
{
unsigned long csr_phys, csr_len;
unsigned long win_phys, win_len;
unsigned int exp_timing_cs0;
int err;
csr_phys = pci_resource_start(p->dev, EXP_CSR_MBAR);
csr_len = pci_resource_len(p->dev, EXP_CSR_MBAR);
win_phys = pci_resource_start(p->dev, EXP_WIN_MBAR);
win_len = pci_resource_len(p->dev, EXP_WIN_MBAR);
if (!csr_phys || !csr_len || !win_phys || !win_len)
return -ENODEV;
if (win_len < (CS0_START + CS0_SIZE))
return -ENXIO;
p->csr_base = ioremap_nocache(csr_phys, csr_len);
if (!p->csr_base)
return -ENOMEM;
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
if (!(exp_timing_cs0 & TIMING_CS_EN)) {
dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
"is disabled.\n");
err = -ENODEV;
goto release;
}
if ((exp_timing_cs0 & TIMING_MASK) == TIMING_MASK) {
dev_warn(&p->dev->dev, "Expansion Bus Chip Select 0 "
"is configured for maximally slow access times.\n");
}
p->map.name = DRV_NAME;
p->map.bankwidth = (exp_timing_cs0 & TIMING_BYTE_EN) ? 1 : 2;
p->map.phys = win_phys + CS0_START;
p->map.size = CS0_SIZE;
p->map.virt = ioremap_nocache(p->map.phys, p->map.size);
if (!p->map.virt) {
err = -ENOMEM;
goto release;
}
simple_map_init(&p->map);
/* Enable writes to flash bank */
exp_timing_cs0 |= TIMING_BOOT_ACCEL_DIS | TIMING_WR_EN;
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
return 0;
release:
iounmap(p->csr_base);
return err;
}
static struct pci_device_id vr_nor_pci_ids[] = {
{PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x500D)},
{0,}
};
static void vr_nor_pci_remove(struct pci_dev *dev)
{
struct vr_nor_mtd *p = pci_get_drvdata(dev);
vr_nor_destroy_partitions(p);
vr_nor_destroy_mtd_setup(p);
vr_nor_destroy_maps(p);
kfree(p);
pci_release_regions(dev);
pci_disable_device(dev);
}
static int vr_nor_pci_probe(struct pci_dev *dev, const struct pci_device_id *id)
{
struct vr_nor_mtd *p = NULL;
unsigned int exp_timing_cs0;
int err;
err = pci_enable_device(dev);
if (err)
goto out;
err = pci_request_regions(dev, DRV_NAME);
if (err)
goto disable_dev;
p = kzalloc(sizeof(*p), GFP_KERNEL);
err = -ENOMEM;
if (!p)
goto release;
p->dev = dev;
err = vr_nor_init_maps(p);
if (err)
goto release;
err = vr_nor_mtd_setup(p);
if (err)
goto destroy_maps;
err = vr_nor_init_partitions(p);
if (err)
goto destroy_mtd_setup;
pci_set_drvdata(dev, p);
return 0;
destroy_mtd_setup:
map_destroy(p->info);
destroy_maps:
/* write-protect the flash bank */
exp_timing_cs0 = readl(p->csr_base + EXP_TIMING_CS0);
exp_timing_cs0 &= ~TIMING_WR_EN;
writel(exp_timing_cs0, p->csr_base + EXP_TIMING_CS0);
/* unmap the flash window */
iounmap(p->map.virt);
/* unmap the csr window */
iounmap(p->csr_base);
release:
kfree(p);
pci_release_regions(dev);
disable_dev:
pci_disable_device(dev);
out:
return err;
}
static struct pci_driver vr_nor_pci_driver = {
.name = DRV_NAME,
.probe = vr_nor_pci_probe,
.remove = vr_nor_pci_remove,
.id_table = vr_nor_pci_ids,
};
module_pci_driver(vr_nor_pci_driver);
MODULE_AUTHOR("Andy Lowe");
MODULE_DESCRIPTION("MTD map driver for NOR flash on Intel Vermilion Range");
MODULE_LICENSE("GPL");
MODULE_DEVICE_TABLE(pci, vr_nor_pci_ids);

262
drivers/mtd/maps/ixp4xx.c Normal file
View file

@ -0,0 +1,262 @@
/*
* drivers/mtd/maps/ixp4xx.c
*
* MTD Map file for IXP4XX based systems. Please do not make per-board
* changes in here. If your board needs special setup, do it in your
* platform level code in arch/arm/mach-ixp4xx/board-setup.c
*
* Original Author: Intel Corporation
* Maintainer: Deepak Saxena <dsaxena@mvista.com>
*
* Copyright (C) 2002 Intel Corporation
* Copyright (C) 2003-2004 MontaVista Software, Inc.
*
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/slab.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/mach/flash.h>
#include <linux/reboot.h>
/*
* Read/write a 16 bit word from flash address 'addr'.
*
* When the cpu is in little-endian mode it swizzles the address lines
* ('address coherency') so we need to undo the swizzling to ensure commands
* and the like end up on the correct flash address.
*
* To further complicate matters, due to the way the expansion bus controller
* handles 32 bit reads, the byte stream ABCD is stored on the flash as:
* D15 D0
* +---+---+
* | A | B | 0
* +---+---+
* | C | D | 2
* +---+---+
* This means that on LE systems each 16 bit word must be swapped. Note that
* this requires CONFIG_MTD_CFI_BE_BYTE_SWAP to be enabled to 'unswap' the CFI
* data and other flash commands which are always in D7-D0.
*/
#ifndef __ARMEB__
#ifndef CONFIG_MTD_CFI_BE_BYTE_SWAP
# error CONFIG_MTD_CFI_BE_BYTE_SWAP required
#endif
static inline u16 flash_read16(void __iomem *addr)
{
return be16_to_cpu(__raw_readw((void __iomem *)((unsigned long)addr ^ 0x2)));
}
static inline void flash_write16(u16 d, void __iomem *addr)
{
__raw_writew(cpu_to_be16(d), (void __iomem *)((unsigned long)addr ^ 0x2));
}
#define BYTE0(h) ((h) & 0xFF)
#define BYTE1(h) (((h) >> 8) & 0xFF)
#else
static inline u16 flash_read16(const void __iomem *addr)
{
return __raw_readw(addr);
}
static inline void flash_write16(u16 d, void __iomem *addr)
{
__raw_writew(d, addr);
}
#define BYTE0(h) (((h) >> 8) & 0xFF)
#define BYTE1(h) ((h) & 0xFF)
#endif
static map_word ixp4xx_read16(struct map_info *map, unsigned long ofs)
{
map_word val;
val.x[0] = flash_read16(map->virt + ofs);
return val;
}
/*
* The IXP4xx expansion bus only allows 16-bit wide acceses
* when attached to a 16-bit wide device (such as the 28F128J3A),
* so we can't just memcpy_fromio().
*/
static void ixp4xx_copy_from(struct map_info *map, void *to,
unsigned long from, ssize_t len)
{
u8 *dest = (u8 *) to;
void __iomem *src = map->virt + from;
if (len <= 0)
return;
if (from & 1) {
*dest++ = BYTE1(flash_read16(src-1));
src++;
--len;
}
while (len >= 2) {
u16 data = flash_read16(src);
*dest++ = BYTE0(data);
*dest++ = BYTE1(data);
src += 2;
len -= 2;
}
if (len > 0)
*dest++ = BYTE0(flash_read16(src));
}
/*
* Unaligned writes are ignored, causing the 8-bit
* probe to fail and proceed to the 16-bit probe (which succeeds).
*/
static void ixp4xx_probe_write16(struct map_info *map, map_word d, unsigned long adr)
{
if (!(adr & 1))
flash_write16(d.x[0], map->virt + adr);
}
/*
* Fast write16 function without the probing check above
*/
static void ixp4xx_write16(struct map_info *map, map_word d, unsigned long adr)
{
flash_write16(d.x[0], map->virt + adr);
}
struct ixp4xx_flash_info {
struct mtd_info *mtd;
struct map_info map;
struct resource *res;
};
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
static int ixp4xx_flash_remove(struct platform_device *dev)
{
struct flash_platform_data *plat = dev_get_platdata(&dev->dev);
struct ixp4xx_flash_info *info = platform_get_drvdata(dev);
if(!info)
return 0;
if (info->mtd) {
mtd_device_unregister(info->mtd);
map_destroy(info->mtd);
}
if (plat->exit)
plat->exit();
return 0;
}
static int ixp4xx_flash_probe(struct platform_device *dev)
{
struct flash_platform_data *plat = dev_get_platdata(&dev->dev);
struct ixp4xx_flash_info *info;
struct mtd_part_parser_data ppdata = {
.origin = dev->resource->start,
};
int err = -1;
if (!plat)
return -ENODEV;
if (plat->init) {
err = plat->init();
if (err)
return err;
}
info = devm_kzalloc(&dev->dev, sizeof(struct ixp4xx_flash_info),
GFP_KERNEL);
if(!info) {
err = -ENOMEM;
goto Error;
}
platform_set_drvdata(dev, info);
/*
* Tell the MTD layer we're not 1:1 mapped so that it does
* not attempt to do a direct access on us.
*/
info->map.phys = NO_XIP;
info->map.size = resource_size(dev->resource);
/*
* We only support 16-bit accesses for now. If and when
* any board use 8-bit access, we'll fixup the driver to
* handle that.
*/
info->map.bankwidth = 2;
info->map.name = dev_name(&dev->dev);
info->map.read = ixp4xx_read16;
info->map.write = ixp4xx_probe_write16;
info->map.copy_from = ixp4xx_copy_from;
info->map.virt = devm_ioremap_resource(&dev->dev, dev->resource);
if (IS_ERR(info->map.virt)) {
err = PTR_ERR(info->map.virt);
goto Error;
}
info->mtd = do_map_probe(plat->map_name, &info->map);
if (!info->mtd) {
printk(KERN_ERR "IXP4XXFlash: map_probe failed\n");
err = -ENXIO;
goto Error;
}
info->mtd->owner = THIS_MODULE;
/* Use the fast version */
info->map.write = ixp4xx_write16;
err = mtd_device_parse_register(info->mtd, probes, &ppdata,
plat->parts, plat->nr_parts);
if (err) {
printk(KERN_ERR "Could not parse partitions\n");
goto Error;
}
return 0;
Error:
ixp4xx_flash_remove(dev);
return err;
}
static struct platform_driver ixp4xx_flash_driver = {
.probe = ixp4xx_flash_probe,
.remove = ixp4xx_flash_remove,
.driver = {
.name = "IXP4XX-Flash",
.owner = THIS_MODULE,
},
};
module_platform_driver(ixp4xx_flash_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("MTD map driver for Intel IXP4xx systems");
MODULE_AUTHOR("Deepak Saxena");
MODULE_ALIAS("platform:IXP4XX-Flash");

166
drivers/mtd/maps/l440gx.c Normal file
View file

@ -0,0 +1,166 @@
/*
* BIOS Flash chip on Intel 440GX board.
*
* Bugs this currently does not work under linuxBIOS.
*/
#include <linux/module.h>
#include <linux/pci.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#define PIIXE_IOBASE_RESOURCE 11
#define WINDOW_ADDR 0xfff00000
#define WINDOW_SIZE 0x00100000
#define BUSWIDTH 1
static u32 iobase;
#define IOBASE iobase
#define TRIBUF_PORT (IOBASE+0x37)
#define VPP_PORT (IOBASE+0x28)
static struct mtd_info *mymtd;
/* Is this really the vpp port? */
static DEFINE_SPINLOCK(l440gx_vpp_lock);
static int l440gx_vpp_refcnt;
static void l440gx_set_vpp(struct map_info *map, int vpp)
{
unsigned long flags;
spin_lock_irqsave(&l440gx_vpp_lock, flags);
if (vpp) {
if (++l440gx_vpp_refcnt == 1) /* first nested 'on' */
outl(inl(VPP_PORT) | 1, VPP_PORT);
} else {
if (--l440gx_vpp_refcnt == 0) /* last nested 'off' */
outl(inl(VPP_PORT) & ~1, VPP_PORT);
}
spin_unlock_irqrestore(&l440gx_vpp_lock, flags);
}
static struct map_info l440gx_map = {
.name = "L440GX BIOS",
.size = WINDOW_SIZE,
.bankwidth = BUSWIDTH,
.phys = WINDOW_ADDR,
#if 0
/* FIXME verify that this is the
* appripriate code for vpp enable/disable
*/
.set_vpp = l440gx_set_vpp
#endif
};
static int __init init_l440gx(void)
{
struct pci_dev *dev, *pm_dev;
struct resource *pm_iobase;
__u16 word;
dev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82371AB_0, NULL);
pm_dev = pci_get_device(PCI_VENDOR_ID_INTEL,
PCI_DEVICE_ID_INTEL_82371AB_3, NULL);
pci_dev_put(dev);
if (!dev || !pm_dev) {
printk(KERN_NOTICE "L440GX flash mapping: failed to find PIIX4 ISA bridge, cannot continue\n");
pci_dev_put(pm_dev);
return -ENODEV;
}
l440gx_map.virt = ioremap_nocache(WINDOW_ADDR, WINDOW_SIZE);
if (!l440gx_map.virt) {
printk(KERN_WARNING "Failed to ioremap L440GX flash region\n");
pci_dev_put(pm_dev);
return -ENOMEM;
}
simple_map_init(&l440gx_map);
printk(KERN_NOTICE "window_addr = 0x%08lx\n", (unsigned long)l440gx_map.virt);
/* Setup the pm iobase resource
* This code should move into some kind of generic bridge
* driver but for the moment I'm content with getting the
* allocation correct.
*/
pm_iobase = &pm_dev->resource[PIIXE_IOBASE_RESOURCE];
if (!(pm_iobase->flags & IORESOURCE_IO)) {
pm_iobase->name = "pm iobase";
pm_iobase->start = 0;
pm_iobase->end = 63;
pm_iobase->flags = IORESOURCE_IO;
/* Put the current value in the resource */
pci_read_config_dword(pm_dev, 0x40, &iobase);
iobase &= ~1;
pm_iobase->start += iobase & ~1;
pm_iobase->end += iobase & ~1;
pci_dev_put(pm_dev);
/* Allocate the resource region */
if (pci_assign_resource(pm_dev, PIIXE_IOBASE_RESOURCE) != 0) {
pci_dev_put(dev);
pci_dev_put(pm_dev);
printk(KERN_WARNING "Could not allocate pm iobase resource\n");
iounmap(l440gx_map.virt);
return -ENXIO;
}
}
/* Set the iobase */
iobase = pm_iobase->start;
pci_write_config_dword(pm_dev, 0x40, iobase | 1);
/* Set XBCS# */
pci_read_config_word(dev, 0x4e, &word);
word |= 0x4;
pci_write_config_word(dev, 0x4e, word);
/* Supply write voltage to the chip */
l440gx_set_vpp(&l440gx_map, 1);
/* Enable the gate on the WE line */
outb(inb(TRIBUF_PORT) & ~1, TRIBUF_PORT);
printk(KERN_NOTICE "Enabled WE line to L440GX BIOS flash chip.\n");
mymtd = do_map_probe("jedec_probe", &l440gx_map);
if (!mymtd) {
printk(KERN_NOTICE "JEDEC probe on BIOS chip failed. Using ROM\n");
mymtd = do_map_probe("map_rom", &l440gx_map);
}
if (mymtd) {
mymtd->owner = THIS_MODULE;
mtd_device_register(mymtd, NULL, 0);
return 0;
}
iounmap(l440gx_map.virt);
return -ENXIO;
}
static void __exit cleanup_l440gx(void)
{
mtd_device_unregister(mymtd);
map_destroy(mymtd);
iounmap(l440gx_map.virt);
}
module_init(init_l440gx);
module_exit(cleanup_l440gx);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("MTD map driver for BIOS chips on Intel L440GX motherboards");

View file

@ -0,0 +1,217 @@
/*
* 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.
*
* Copyright (C) 2004 Liu Peng Infineon IFAP DC COM CPE
* Copyright (C) 2010 John Crispin <blogic@openwrt.org>
*/
#include <linux/err.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/io.h>
#include <linux/slab.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/cfi.h>
#include <linux/platform_device.h>
#include <linux/mtd/physmap.h>
#include <linux/of.h>
#include <lantiq_soc.h>
/*
* The NOR flash is connected to the same external bus unit (EBU) as PCI.
* To make PCI work we need to enable the endianness swapping for the address
* written to the EBU. This endianness swapping works for PCI correctly but
* fails for attached NOR devices. To workaround this we need to use a complex
* map. The workaround involves swapping all addresses whilst probing the chip.
* Once probing is complete we stop swapping the addresses but swizzle the
* unlock addresses to ensure that access to the NOR device works correctly.
*/
enum {
LTQ_NOR_PROBING,
LTQ_NOR_NORMAL
};
struct ltq_mtd {
struct resource *res;
struct mtd_info *mtd;
struct map_info *map;
};
static const char ltq_map_name[] = "ltq_nor";
static const char * const ltq_probe_types[] = { "cmdlinepart", "ofpart", NULL };
static map_word
ltq_read16(struct map_info *map, unsigned long adr)
{
unsigned long flags;
map_word temp;
if (map->map_priv_1 == LTQ_NOR_PROBING)
adr ^= 2;
spin_lock_irqsave(&ebu_lock, flags);
temp.x[0] = *(u16 *)(map->virt + adr);
spin_unlock_irqrestore(&ebu_lock, flags);
return temp;
}
static void
ltq_write16(struct map_info *map, map_word d, unsigned long adr)
{
unsigned long flags;
if (map->map_priv_1 == LTQ_NOR_PROBING)
adr ^= 2;
spin_lock_irqsave(&ebu_lock, flags);
*(u16 *)(map->virt + adr) = d.x[0];
spin_unlock_irqrestore(&ebu_lock, flags);
}
/*
* The following 2 functions copy data between iomem and a cached memory
* section. As memcpy() makes use of pre-fetching we cannot use it here.
* The normal alternative of using memcpy_{to,from}io also makes use of
* memcpy() on MIPS so it is not applicable either. We are therefore stuck
* with having to use our own loop.
*/
static void
ltq_copy_from(struct map_info *map, void *to,
unsigned long from, ssize_t len)
{
unsigned char *f = (unsigned char *)map->virt + from;
unsigned char *t = (unsigned char *)to;
unsigned long flags;
spin_lock_irqsave(&ebu_lock, flags);
while (len--)
*t++ = *f++;
spin_unlock_irqrestore(&ebu_lock, flags);
}
static void
ltq_copy_to(struct map_info *map, unsigned long to,
const void *from, ssize_t len)
{
unsigned char *f = (unsigned char *)from;
unsigned char *t = (unsigned char *)map->virt + to;
unsigned long flags;
spin_lock_irqsave(&ebu_lock, flags);
while (len--)
*t++ = *f++;
spin_unlock_irqrestore(&ebu_lock, flags);
}
static int
ltq_mtd_probe(struct platform_device *pdev)
{
struct mtd_part_parser_data ppdata;
struct ltq_mtd *ltq_mtd;
struct cfi_private *cfi;
int err;
if (of_machine_is_compatible("lantiq,falcon") &&
(ltq_boot_select() != BS_FLASH)) {
dev_err(&pdev->dev, "invalid bootstrap options\n");
return -ENODEV;
}
ltq_mtd = devm_kzalloc(&pdev->dev, sizeof(struct ltq_mtd), GFP_KERNEL);
if (!ltq_mtd)
return -ENOMEM;
platform_set_drvdata(pdev, ltq_mtd);
ltq_mtd->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!ltq_mtd->res) {
dev_err(&pdev->dev, "failed to get memory resource\n");
return -ENOENT;
}
ltq_mtd->map = devm_kzalloc(&pdev->dev, sizeof(struct map_info),
GFP_KERNEL);
if (!ltq_mtd->map)
return -ENOMEM;
ltq_mtd->map->phys = ltq_mtd->res->start;
ltq_mtd->map->size = resource_size(ltq_mtd->res);
ltq_mtd->map->virt = devm_ioremap_resource(&pdev->dev, ltq_mtd->res);
if (IS_ERR(ltq_mtd->map->virt))
return PTR_ERR(ltq_mtd->map->virt);
ltq_mtd->map->name = ltq_map_name;
ltq_mtd->map->bankwidth = 2;
ltq_mtd->map->read = ltq_read16;
ltq_mtd->map->write = ltq_write16;
ltq_mtd->map->copy_from = ltq_copy_from;
ltq_mtd->map->copy_to = ltq_copy_to;
ltq_mtd->map->map_priv_1 = LTQ_NOR_PROBING;
ltq_mtd->mtd = do_map_probe("cfi_probe", ltq_mtd->map);
ltq_mtd->map->map_priv_1 = LTQ_NOR_NORMAL;
if (!ltq_mtd->mtd) {
dev_err(&pdev->dev, "probing failed\n");
return -ENXIO;
}
ltq_mtd->mtd->owner = THIS_MODULE;
cfi = ltq_mtd->map->fldrv_priv;
cfi->addr_unlock1 ^= 1;
cfi->addr_unlock2 ^= 1;
ppdata.of_node = pdev->dev.of_node;
err = mtd_device_parse_register(ltq_mtd->mtd, ltq_probe_types,
&ppdata, NULL, 0);
if (err) {
dev_err(&pdev->dev, "failed to add partitions\n");
goto err_destroy;
}
return 0;
err_destroy:
map_destroy(ltq_mtd->mtd);
return err;
}
static int
ltq_mtd_remove(struct platform_device *pdev)
{
struct ltq_mtd *ltq_mtd = platform_get_drvdata(pdev);
if (ltq_mtd && ltq_mtd->mtd) {
mtd_device_unregister(ltq_mtd->mtd);
map_destroy(ltq_mtd->mtd);
}
return 0;
}
static const struct of_device_id ltq_mtd_match[] = {
{ .compatible = "lantiq,nor" },
{},
};
MODULE_DEVICE_TABLE(of, ltq_mtd_match);
static struct platform_driver ltq_mtd_driver = {
.probe = ltq_mtd_probe,
.remove = ltq_mtd_remove,
.driver = {
.name = "ltq-nor",
.owner = THIS_MODULE,
.of_match_table = ltq_mtd_match,
},
};
module_platform_driver(ltq_mtd_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("John Crispin <blogic@openwrt.org>");
MODULE_DESCRIPTION("Lantiq SoC NOR");

View file

@ -0,0 +1,230 @@
/*
* Interface for NOR flash driver whose high address lines are latched
*
* Copyright © 2000 Nicolas Pitre <nico@cam.org>
* Copyright © 2005-2008 Analog Devices Inc.
* Copyright © 2008 MontaVista Software, Inc. <source@mvista.com>
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed "as is" without any warranty of any
* kind, whether express or implied.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/platform_device.h>
#include <linux/mtd/latch-addr-flash.h>
#include <linux/slab.h>
#define DRIVER_NAME "latch-addr-flash"
struct latch_addr_flash_info {
struct mtd_info *mtd;
struct map_info map;
struct resource *res;
void (*set_window)(unsigned long offset, void *data);
void *data;
/* cache; could be found out of res */
unsigned long win_mask;
spinlock_t lock;
};
static map_word lf_read(struct map_info *map, unsigned long ofs)
{
struct latch_addr_flash_info *info;
map_word datum;
info = (struct latch_addr_flash_info *)map->map_priv_1;
spin_lock(&info->lock);
info->set_window(ofs, info->data);
datum = inline_map_read(map, info->win_mask & ofs);
spin_unlock(&info->lock);
return datum;
}
static void lf_write(struct map_info *map, map_word datum, unsigned long ofs)
{
struct latch_addr_flash_info *info;
info = (struct latch_addr_flash_info *)map->map_priv_1;
spin_lock(&info->lock);
info->set_window(ofs, info->data);
inline_map_write(map, datum, info->win_mask & ofs);
spin_unlock(&info->lock);
}
static void lf_copy_from(struct map_info *map, void *to,
unsigned long from, ssize_t len)
{
struct latch_addr_flash_info *info =
(struct latch_addr_flash_info *) map->map_priv_1;
unsigned n;
while (len > 0) {
n = info->win_mask + 1 - (from & info->win_mask);
if (n > len)
n = len;
spin_lock(&info->lock);
info->set_window(from, info->data);
memcpy_fromio(to, map->virt + (from & info->win_mask), n);
spin_unlock(&info->lock);
to += n;
from += n;
len -= n;
}
}
static char *rom_probe_types[] = { "cfi_probe", NULL };
static int latch_addr_flash_remove(struct platform_device *dev)
{
struct latch_addr_flash_info *info;
struct latch_addr_flash_data *latch_addr_data;
info = platform_get_drvdata(dev);
if (info == NULL)
return 0;
latch_addr_data = dev_get_platdata(&dev->dev);
if (info->mtd != NULL) {
mtd_device_unregister(info->mtd);
map_destroy(info->mtd);
}
if (info->map.virt != NULL)
iounmap(info->map.virt);
if (info->res != NULL)
release_mem_region(info->res->start, resource_size(info->res));
kfree(info);
if (latch_addr_data->done)
latch_addr_data->done(latch_addr_data->data);
return 0;
}
static int latch_addr_flash_probe(struct platform_device *dev)
{
struct latch_addr_flash_data *latch_addr_data;
struct latch_addr_flash_info *info;
resource_size_t win_base = dev->resource->start;
resource_size_t win_size = resource_size(dev->resource);
char **probe_type;
int chipsel;
int err;
latch_addr_data = dev_get_platdata(&dev->dev);
if (latch_addr_data == NULL)
return -ENODEV;
pr_notice("latch-addr platform flash device: %#llx byte "
"window at %#.8llx\n",
(unsigned long long)win_size, (unsigned long long)win_base);
chipsel = dev->id;
if (latch_addr_data->init) {
err = latch_addr_data->init(latch_addr_data->data, chipsel);
if (err != 0)
return err;
}
info = kzalloc(sizeof(struct latch_addr_flash_info), GFP_KERNEL);
if (info == NULL) {
err = -ENOMEM;
goto done;
}
platform_set_drvdata(dev, info);
info->res = request_mem_region(win_base, win_size, DRIVER_NAME);
if (info->res == NULL) {
dev_err(&dev->dev, "Could not reserve memory region\n");
err = -EBUSY;
goto free_info;
}
info->map.name = DRIVER_NAME;
info->map.size = latch_addr_data->size;
info->map.bankwidth = latch_addr_data->width;
info->map.phys = NO_XIP;
info->map.virt = ioremap(win_base, win_size);
if (!info->map.virt) {
err = -ENOMEM;
goto free_res;
}
info->map.map_priv_1 = (unsigned long)info;
info->map.read = lf_read;
info->map.copy_from = lf_copy_from;
info->map.write = lf_write;
info->set_window = latch_addr_data->set_window;
info->data = latch_addr_data->data;
info->win_mask = win_size - 1;
spin_lock_init(&info->lock);
for (probe_type = rom_probe_types; !info->mtd && *probe_type;
probe_type++)
info->mtd = do_map_probe(*probe_type, &info->map);
if (info->mtd == NULL) {
dev_err(&dev->dev, "map_probe failed\n");
err = -ENODEV;
goto iounmap;
}
info->mtd->owner = THIS_MODULE;
mtd_device_parse_register(info->mtd, NULL, NULL,
latch_addr_data->parts,
latch_addr_data->nr_parts);
return 0;
iounmap:
iounmap(info->map.virt);
free_res:
release_mem_region(info->res->start, resource_size(info->res));
free_info:
kfree(info);
done:
if (latch_addr_data->done)
latch_addr_data->done(latch_addr_data->data);
return err;
}
static struct platform_driver latch_addr_flash_driver = {
.probe = latch_addr_flash_probe,
.remove = latch_addr_flash_remove,
.driver = {
.name = DRIVER_NAME,
},
};
module_platform_driver(latch_addr_flash_driver);
MODULE_AUTHOR("David Griego <dgriego@mvista.com>");
MODULE_DESCRIPTION("MTD map driver for flashes addressed physically with upper "
"address lines being set board specifically");
MODULE_LICENSE("GPL v2");

View file

@ -0,0 +1,43 @@
/*
* Out-of-line map I/O functions for simple maps when CONFIG_COMPLEX_MAPPINGS
* is enabled.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/mtd/map.h>
#include <linux/mtd/xip.h>
static map_word __xipram simple_map_read(struct map_info *map, unsigned long ofs)
{
return inline_map_read(map, ofs);
}
static void __xipram simple_map_write(struct map_info *map, const map_word datum, unsigned long ofs)
{
inline_map_write(map, datum, ofs);
}
static void __xipram simple_map_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
inline_map_copy_from(map, to, from, len);
}
static void __xipram simple_map_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
inline_map_copy_to(map, to, from, len);
}
void simple_map_init(struct map_info *map)
{
BUG_ON(!map_bankwidth_supported(map->bankwidth));
map->read = simple_map_read;
map->write = simple_map_write;
map->copy_from = simple_map_copy_from;
map->copy_to = simple_map_copy_to;
}
EXPORT_SYMBOL(simple_map_init);
MODULE_LICENSE("GPL");

140
drivers/mtd/maps/netsc520.c Normal file
View file

@ -0,0 +1,140 @@
/* netsc520.c -- MTD map driver for AMD NetSc520 Demonstration Board
*
* Copyright (C) 2001 Mark Langsdorf (mark.langsdorf@amd.com)
* based on sc520cdp.c by 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 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
*
* The NetSc520 is a demonstration board for the Elan Sc520 processor available
* from AMD. It has a single back of 16 megs of 32-bit Flash ROM and another
* 16 megs of SDRAM.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
/*
** The single, 16 megabyte flash bank is divided into four virtual
** partitions. The first partition is 768 KiB and is intended to
** store the kernel image loaded by the bootstrap loader. The second
** partition is 256 KiB and holds the BIOS image. The third
** partition is 14.5 MiB and is intended for the flash file system
** image. The last partition is 512 KiB and contains another copy
** of the BIOS image and the reset vector.
**
** Only the third partition should be mounted. The first partition
** should not be mounted, but it can erased and written to using the
** MTD character routines. The second and fourth partitions should
** not be touched - it is possible to corrupt the BIOS image by
** mounting these partitions, and potentially the board will not be
** recoverable afterwards.
*/
/* partition_info gives details on the logical partitions that the split the
* single flash device into. If the size if zero we use up to the end of the
* device. */
static struct mtd_partition partition_info[]={
{
.name = "NetSc520 boot kernel",
.offset = 0,
.size = 0xc0000
},
{
.name = "NetSc520 Low BIOS",
.offset = 0xc0000,
.size = 0x40000
},
{
.name = "NetSc520 file system",
.offset = 0x100000,
.size = 0xe80000
},
{
.name = "NetSc520 High BIOS",
.offset = 0xf80000,
.size = 0x80000
},
};
#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
#define WINDOW_SIZE 0x00100000
#define WINDOW_ADDR 0x00200000
static struct map_info netsc520_map = {
.name = "netsc520 Flash Bank",
.size = WINDOW_SIZE,
.bankwidth = 4,
.phys = WINDOW_ADDR,
};
#define NUM_FLASH_BANKS ARRAY_SIZE(netsc520_map)
static struct mtd_info *mymtd;
static int __init init_netsc520(void)
{
printk(KERN_NOTICE "NetSc520 flash device: 0x%Lx at 0x%Lx\n",
(unsigned long long)netsc520_map.size,
(unsigned long long)netsc520_map.phys);
netsc520_map.virt = ioremap_nocache(netsc520_map.phys, netsc520_map.size);
if (!netsc520_map.virt) {
printk("Failed to ioremap_nocache\n");
return -EIO;
}
simple_map_init(&netsc520_map);
mymtd = do_map_probe("cfi_probe", &netsc520_map);
if(!mymtd)
mymtd = do_map_probe("map_ram", &netsc520_map);
if(!mymtd)
mymtd = do_map_probe("map_rom", &netsc520_map);
if (!mymtd) {
iounmap(netsc520_map.virt);
return -ENXIO;
}
mymtd->owner = THIS_MODULE;
mtd_device_register(mymtd, partition_info, NUM_PARTITIONS);
return 0;
}
static void __exit cleanup_netsc520(void)
{
if (mymtd) {
mtd_device_unregister(mymtd);
map_destroy(mymtd);
}
if (netsc520_map.virt) {
iounmap(netsc520_map.virt);
netsc520_map.virt = NULL;
}
}
module_init(init_netsc520);
module_exit(cleanup_netsc520);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Mark Langsdorf <mark.langsdorf@amd.com>");
MODULE_DESCRIPTION("MTD map driver for AMD NetSc520 Demonstration Board");

454
drivers/mtd/maps/nettel.c Normal file
View file

@ -0,0 +1,454 @@
/****************************************************************************/
/*
* nettel.c -- mappings for NETtel/SecureEdge/SnapGear (x86) boards.
*
* (C) Copyright 2000-2001, Greg Ungerer (gerg@snapgear.com)
* (C) Copyright 2001-2002, SnapGear (www.snapgear.com)
*/
/****************************************************************************/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/cfi.h>
#include <linux/reboot.h>
#include <linux/err.h>
#include <linux/kdev_t.h>
#include <linux/root_dev.h>
#include <asm/io.h>
/****************************************************************************/
#define INTEL_BUSWIDTH 1
#define AMD_WINDOW_MAXSIZE 0x00200000
#define AMD_BUSWIDTH 1
/*
* PAR masks and shifts, assuming 64K pages.
*/
#define SC520_PAR_ADDR_MASK 0x00003fff
#define SC520_PAR_ADDR_SHIFT 16
#define SC520_PAR_TO_ADDR(par) \
(((par)&SC520_PAR_ADDR_MASK) << SC520_PAR_ADDR_SHIFT)
#define SC520_PAR_SIZE_MASK 0x01ffc000
#define SC520_PAR_SIZE_SHIFT 2
#define SC520_PAR_TO_SIZE(par) \
((((par)&SC520_PAR_SIZE_MASK) << SC520_PAR_SIZE_SHIFT) + (64*1024))
#define SC520_PAR(cs, addr, size) \
((cs) | \
((((size)-(64*1024)) >> SC520_PAR_SIZE_SHIFT) & SC520_PAR_SIZE_MASK) | \
(((addr) >> SC520_PAR_ADDR_SHIFT) & SC520_PAR_ADDR_MASK))
#define SC520_PAR_BOOTCS 0x8a000000
#define SC520_PAR_ROMCS1 0xaa000000
#define SC520_PAR_ROMCS2 0xca000000 /* Cache disabled, 64K page */
static void *nettel_mmcrp = NULL;
#ifdef CONFIG_MTD_CFI_INTELEXT
static struct mtd_info *intel_mtd;
#endif
static struct mtd_info *amd_mtd;
/****************************************************************************/
/****************************************************************************/
#ifdef CONFIG_MTD_CFI_INTELEXT
static struct map_info nettel_intel_map = {
.name = "SnapGear Intel",
.size = 0,
.bankwidth = INTEL_BUSWIDTH,
};
static struct mtd_partition nettel_intel_partitions[] = {
{
.name = "SnapGear kernel",
.offset = 0,
.size = 0x000e0000
},
{
.name = "SnapGear filesystem",
.offset = 0x00100000,
},
{
.name = "SnapGear config",
.offset = 0x000e0000,
.size = 0x00020000
},
{
.name = "SnapGear Intel",
.offset = 0
},
{
.name = "SnapGear BIOS Config",
.offset = 0x007e0000,
.size = 0x00020000
},
{
.name = "SnapGear BIOS",
.offset = 0x007e0000,
.size = 0x00020000
},
};
#endif
static struct map_info nettel_amd_map = {
.name = "SnapGear AMD",
.size = AMD_WINDOW_MAXSIZE,
.bankwidth = AMD_BUSWIDTH,
};
static struct mtd_partition nettel_amd_partitions[] = {
{
.name = "SnapGear BIOS config",
.offset = 0x000e0000,
.size = 0x00010000
},
{
.name = "SnapGear BIOS",
.offset = 0x000f0000,
.size = 0x00010000
},
{
.name = "SnapGear AMD",
.offset = 0
},
{
.name = "SnapGear high BIOS",
.offset = 0x001f0000,
.size = 0x00010000
}
};
#define NUM_AMD_PARTITIONS ARRAY_SIZE(nettel_amd_partitions)
/****************************************************************************/
#ifdef CONFIG_MTD_CFI_INTELEXT
/*
* Set the Intel flash back to read mode since some old boot
* loaders don't.
*/
static int nettel_reboot_notifier(struct notifier_block *nb, unsigned long val, void *v)
{
struct cfi_private *cfi = nettel_intel_map.fldrv_priv;
unsigned long b;
/* Make sure all FLASH chips are put back into read mode */
for (b = 0; (b < nettel_intel_partitions[3].size); b += 0x100000) {
cfi_send_gen_cmd(0xff, 0x55, b, &nettel_intel_map, cfi,
cfi->device_type, NULL);
}
return(NOTIFY_OK);
}
static struct notifier_block nettel_notifier_block = {
nettel_reboot_notifier, NULL, 0
};
#endif
/****************************************************************************/
static int __init nettel_init(void)
{
volatile unsigned long *amdpar;
unsigned long amdaddr, maxsize;
int num_amd_partitions=0;
#ifdef CONFIG_MTD_CFI_INTELEXT
volatile unsigned long *intel0par, *intel1par;
unsigned long orig_bootcspar, orig_romcs1par;
unsigned long intel0addr, intel0size;
unsigned long intel1addr, intel1size;
int intelboot, intel0cs, intel1cs;
int num_intel_partitions;
#endif
int rc = 0;
nettel_mmcrp = (void *) ioremap_nocache(0xfffef000, 4096);
if (nettel_mmcrp == NULL) {
printk("SNAPGEAR: failed to disable MMCR cache??\n");
return(-EIO);
}
/* Set CPU clock to be 33.000MHz */
*((unsigned char *) (nettel_mmcrp + 0xc64)) = 0x01;
amdpar = (volatile unsigned long *) (nettel_mmcrp + 0xc4);
#ifdef CONFIG_MTD_CFI_INTELEXT
intelboot = 0;
intel0cs = SC520_PAR_ROMCS1;
intel0par = (volatile unsigned long *) (nettel_mmcrp + 0xc0);
intel1cs = SC520_PAR_ROMCS2;
intel1par = (volatile unsigned long *) (nettel_mmcrp + 0xbc);
/*
* Save the CS settings then ensure ROMCS1 and ROMCS2 are off,
* otherwise they might clash with where we try to map BOOTCS.
*/
orig_bootcspar = *amdpar;
orig_romcs1par = *intel0par;
*intel0par = 0;
*intel1par = 0;
#endif
/*
* The first thing to do is determine if we have a separate
* boot FLASH device. Typically this is a small (1 to 2MB)
* AMD FLASH part. It seems that device size is about the
* only way to tell if this is the case...
*/
amdaddr = 0x20000000;
maxsize = AMD_WINDOW_MAXSIZE;
*amdpar = SC520_PAR(SC520_PAR_BOOTCS, amdaddr, maxsize);
__asm__ ("wbinvd");
nettel_amd_map.phys = amdaddr;
nettel_amd_map.virt = ioremap_nocache(amdaddr, maxsize);
if (!nettel_amd_map.virt) {
printk("SNAPGEAR: failed to ioremap() BOOTCS\n");
iounmap(nettel_mmcrp);
return(-EIO);
}
simple_map_init(&nettel_amd_map);
if ((amd_mtd = do_map_probe("jedec_probe", &nettel_amd_map))) {
printk(KERN_NOTICE "SNAPGEAR: AMD flash device size = %dK\n",
(int)(amd_mtd->size>>10));
amd_mtd->owner = THIS_MODULE;
/* The high BIOS partition is only present for 2MB units */
num_amd_partitions = NUM_AMD_PARTITIONS;
if (amd_mtd->size < AMD_WINDOW_MAXSIZE)
num_amd_partitions--;
/* Don't add the partition until after the primary INTEL's */
#ifdef CONFIG_MTD_CFI_INTELEXT
/*
* Map the Intel flash into memory after the AMD
* It has to start on a multiple of maxsize.
*/
maxsize = SC520_PAR_TO_SIZE(orig_romcs1par);
if (maxsize < (32 * 1024 * 1024))
maxsize = (32 * 1024 * 1024);
intel0addr = amdaddr + maxsize;
#endif
} else {
#ifdef CONFIG_MTD_CFI_INTELEXT
/* INTEL boot FLASH */
intelboot++;
if (!orig_romcs1par) {
intel0cs = SC520_PAR_BOOTCS;
intel0par = (volatile unsigned long *)
(nettel_mmcrp + 0xc4);
intel1cs = SC520_PAR_ROMCS1;
intel1par = (volatile unsigned long *)
(nettel_mmcrp + 0xc0);
intel0addr = SC520_PAR_TO_ADDR(orig_bootcspar);
maxsize = SC520_PAR_TO_SIZE(orig_bootcspar);
} else {
/* Kernel base is on ROMCS1, not BOOTCS */
intel0cs = SC520_PAR_ROMCS1;
intel0par = (volatile unsigned long *)
(nettel_mmcrp + 0xc0);
intel1cs = SC520_PAR_BOOTCS;
intel1par = (volatile unsigned long *)
(nettel_mmcrp + 0xc4);
intel0addr = SC520_PAR_TO_ADDR(orig_romcs1par);
maxsize = SC520_PAR_TO_SIZE(orig_romcs1par);
}
/* Destroy useless AMD MTD mapping */
amd_mtd = NULL;
iounmap(nettel_amd_map.virt);
nettel_amd_map.virt = NULL;
#else
/* Only AMD flash supported */
rc = -ENXIO;
goto out_unmap2;
#endif
}
#ifdef CONFIG_MTD_CFI_INTELEXT
/*
* We have determined the INTEL FLASH configuration, so lets
* go ahead and probe for them now.
*/
/* Set PAR to the maximum size */
if (maxsize < (32 * 1024 * 1024))
maxsize = (32 * 1024 * 1024);
*intel0par = SC520_PAR(intel0cs, intel0addr, maxsize);
/* Turn other PAR off so the first probe doesn't find it */
*intel1par = 0;
/* Probe for the size of the first Intel flash */
nettel_intel_map.size = maxsize;
nettel_intel_map.phys = intel0addr;
nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize);
if (!nettel_intel_map.virt) {
printk("SNAPGEAR: failed to ioremap() ROMCS1\n");
rc = -EIO;
goto out_unmap2;
}
simple_map_init(&nettel_intel_map);
intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
if (!intel_mtd) {
rc = -ENXIO;
goto out_unmap1;
}
/* Set PAR to the detected size */
intel0size = intel_mtd->size;
*intel0par = SC520_PAR(intel0cs, intel0addr, intel0size);
/*
* Map second Intel FLASH right after first. Set its size to the
* same maxsize used for the first Intel FLASH.
*/
intel1addr = intel0addr + intel0size;
*intel1par = SC520_PAR(intel1cs, intel1addr, maxsize);
__asm__ ("wbinvd");
maxsize += intel0size;
/* Delete the old map and probe again to do both chips */
map_destroy(intel_mtd);
intel_mtd = NULL;
iounmap(nettel_intel_map.virt);
nettel_intel_map.size = maxsize;
nettel_intel_map.virt = ioremap_nocache(intel0addr, maxsize);
if (!nettel_intel_map.virt) {
printk("SNAPGEAR: failed to ioremap() ROMCS1/2\n");
rc = -EIO;
goto out_unmap2;
}
intel_mtd = do_map_probe("cfi_probe", &nettel_intel_map);
if (! intel_mtd) {
rc = -ENXIO;
goto out_unmap1;
}
intel1size = intel_mtd->size - intel0size;
if (intel1size > 0) {
*intel1par = SC520_PAR(intel1cs, intel1addr, intel1size);
__asm__ ("wbinvd");
} else {
*intel1par = 0;
}
printk(KERN_NOTICE "SNAPGEAR: Intel flash device size = %lldKiB\n",
(unsigned long long)(intel_mtd->size >> 10));
intel_mtd->owner = THIS_MODULE;
num_intel_partitions = ARRAY_SIZE(nettel_intel_partitions);
if (intelboot) {
/*
* Adjust offset and size of last boot partition.
* Must allow for BIOS region at end of FLASH.
*/
nettel_intel_partitions[1].size = (intel0size + intel1size) -
(1024*1024 + intel_mtd->erasesize);
nettel_intel_partitions[3].size = intel0size + intel1size;
nettel_intel_partitions[4].offset =
(intel0size + intel1size) - intel_mtd->erasesize;
nettel_intel_partitions[4].size = intel_mtd->erasesize;
nettel_intel_partitions[5].offset =
nettel_intel_partitions[4].offset;
nettel_intel_partitions[5].size =
nettel_intel_partitions[4].size;
} else {
/* No BIOS regions when AMD boot */
num_intel_partitions -= 2;
}
rc = mtd_device_register(intel_mtd, nettel_intel_partitions,
num_intel_partitions);
#endif
if (amd_mtd) {
rc = mtd_device_register(amd_mtd, nettel_amd_partitions,
num_amd_partitions);
}
#ifdef CONFIG_MTD_CFI_INTELEXT
register_reboot_notifier(&nettel_notifier_block);
#endif
return(rc);
#ifdef CONFIG_MTD_CFI_INTELEXT
out_unmap1:
iounmap(nettel_intel_map.virt);
#endif
out_unmap2:
iounmap(nettel_mmcrp);
iounmap(nettel_amd_map.virt);
return(rc);
}
/****************************************************************************/
static void __exit nettel_cleanup(void)
{
#ifdef CONFIG_MTD_CFI_INTELEXT
unregister_reboot_notifier(&nettel_notifier_block);
#endif
if (amd_mtd) {
mtd_device_unregister(amd_mtd);
map_destroy(amd_mtd);
}
if (nettel_mmcrp) {
iounmap(nettel_mmcrp);
nettel_mmcrp = NULL;
}
if (nettel_amd_map.virt) {
iounmap(nettel_amd_map.virt);
nettel_amd_map.virt = NULL;
}
#ifdef CONFIG_MTD_CFI_INTELEXT
if (intel_mtd) {
mtd_device_unregister(intel_mtd);
map_destroy(intel_mtd);
}
if (nettel_intel_map.virt) {
iounmap(nettel_intel_map.virt);
nettel_intel_map.virt = NULL;
}
#endif
}
/****************************************************************************/
module_init(nettel_init);
module_exit(nettel_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
MODULE_DESCRIPTION("SnapGear/SecureEdge FLASH support");
/****************************************************************************/

333
drivers/mtd/maps/pci.c Normal file
View file

@ -0,0 +1,333 @@
/*
* 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.
*
* 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/slab.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 __iomem *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;
};
static map_word mtd_pci_read8(struct map_info *_map, unsigned long ofs)
{
struct map_pci_info *map = (struct map_pci_info *)_map;
map_word val;
val.x[0]= readb(map->base + map->translate(map, ofs));
return val;
}
static map_word mtd_pci_read32(struct map_info *_map, unsigned long ofs)
{
struct map_pci_info *map = (struct map_pci_info *)_map;
map_word val;
val.x[0] = readl(map->base + map->translate(map, ofs));
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, map_word val, unsigned long ofs)
{
struct map_pci_info *map = (struct map_pci_info *)_map;
writeb(val.x[0], map->base + map->translate(map, ofs));
}
static void mtd_pci_write32(struct map_info *_map, map_word val, unsigned long ofs)
{
struct map_pci_info *map = (struct map_pci_info *)_map;
writel(val.x[0], 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 const struct map_info mtd_pci_map = {
.phys = NO_XIP,
.copy_from = mtd_pci_copyfrom,
.copy_to = mtd_pci_copyto,
};
/*
* Intel IOP80310 Flash driver
*/
static int
intel_iq80310_init(struct pci_dev *dev, struct map_pci_info *map)
{
u32 win_base;
map->map.bankwidth = 1;
map->map.read = mtd_pci_read8,
map->map.write = mtd_pci_write8,
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(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?
*/
pci_enable_rom(dev);
printk("%s: enabling expansion ROM\n", pci_name(dev));
}
if (!len || !base)
return -ENXIO;
map->map.bankwidth = 4;
map->map.read = mtd_pci_read32,
map->map.write = mtd_pci_write32,
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)
{
if (map->base)
iounmap(map->base);
/*
* We need to undo the PCI BAR2/PCI ROM BAR address alteration.
*/
pci_disable_rom(dev);
}
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[] = {
{
.vendor = PCI_VENDOR_ID_INTEL,
.device = 0x530d,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID,
.class = PCI_CLASS_MEMORY_OTHER << 8,
.class_mask = 0xffff00,
.driver_data = (unsigned long)&intel_iq80310_info,
},
{
.vendor = PCI_VENDOR_ID_DEC,
.device = PCI_DEVICE_ID_DEC_21285,
.subvendor = 0, /* DC21285 defaults to 0 on reset */
.subdevice = 0, /* DC21285 defaults to 0 on reset */
.driver_data = (unsigned long)&intel_dc21285_info,
},
{ 0, }
};
/*
* Generic code follows.
*/
static int 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 = pci_name(dev);
map->dev = dev;
map->exit = info->exit;
map->translate = info->translate;
err = info->init(dev, map);
if (err)
goto release;
mtd = do_map_probe(info->map_name, &map->map);
err = -ENODEV;
if (!mtd)
goto release;
mtd->owner = THIS_MODULE;
mtd_device_register(mtd, NULL, 0);
pci_set_drvdata(dev, mtd);
return 0;
release:
if (map) {
map->exit(dev, map);
kfree(map);
}
pci_release_regions(dev);
out:
return err;
}
static void mtd_pci_remove(struct pci_dev *dev)
{
struct mtd_info *mtd = pci_get_drvdata(dev);
struct map_pci_info *map = mtd->priv;
mtd_device_unregister(mtd);
map_destroy(mtd);
map->exit(dev, map);
kfree(map);
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,
};
module_pci_driver(mtd_pci_driver);
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);

View file

@ -0,0 +1,753 @@
/*
* 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 <linux/init.h>
#include <asm/io.h>
#include <pcmcia/cistpl.h>
#include <pcmcia/ds.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#define info(format, arg...) printk(KERN_INFO "pcmciamtd: " format "\n" , ## arg)
#define DRIVER_DESC "PCMCIA Flash memory card driver"
/* Size of the PCMCIA address space: 26 bits = 64 MB */
#define MAX_PCMCIA_ADDR 0x4000000
struct pcmciamtd_dev {
struct pcmcia_device *p_dev;
caddr_t win_base; /* ioremapped address of PCMCIA window */
unsigned int win_size; /* size of window */
unsigned int offset; /* offset into card the window currently points at */
struct map_info pcmcia_map;
struct mtd_info *mtd_info;
int vpp;
char mtd_name[sizeof(struct cistpl_vers_1_t)];
};
/* Module parameters */
/* 2 = do 16-bit transfers, 1 = do 8-bit transfers */
static int bankwidth = 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_param(bankwidth, int, 0);
MODULE_PARM_DESC(bankwidth, "Set bankwidth (1=8 bit, 2=16 bit, default=2)");
module_param(mem_speed, int, 0);
MODULE_PARM_DESC(mem_speed, "Set memory access speed in ns");
module_param(force_size, int, 0);
MODULE_PARM_DESC(force_size, "Force size of card in MiB (1-64)");
module_param(setvpp, int, 0);
MODULE_PARM_DESC(setvpp, "Set Vpp (0=Never, 1=On writes, 2=Always on, default=0)");
module_param(vpp, int, 0);
MODULE_PARM_DESC(vpp, "Vpp value in 1/10ths eg 33=3.3V 120=12V (Dangerous)");
module_param(mem_type, int, 0);
MODULE_PARM_DESC(mem_type, "Set Memory type (0=Flash, 1=RAM, 2=ROM, default=0)");
/* 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;
struct resource *win = (struct resource *) map->map_priv_2;
unsigned int offset;
int ret;
if (!pcmcia_dev_present(dev->p_dev)) {
pr_debug("device removed\n");
return NULL;
}
offset = to & ~(dev->win_size-1);
if (offset != dev->offset) {
pr_debug("Remapping window from 0x%8.8x to 0x%8.8x\n",
dev->offset, offset);
ret = pcmcia_map_mem_page(dev->p_dev, win, offset);
if (ret != 0)
return NULL;
dev->offset = offset;
}
return dev->win_base + (to & (dev->win_size-1));
}
static map_word pcmcia_read8_remap(struct map_info *map, unsigned long ofs)
{
caddr_t addr;
map_word d = {{0}};
addr = remap_window(map, ofs);
if(!addr)
return d;
d.x[0] = readb(addr);
pr_debug("ofs = 0x%08lx (%p) data = 0x%02lx\n", ofs, addr, d.x[0]);
return d;
}
static map_word pcmcia_read16_remap(struct map_info *map, unsigned long ofs)
{
caddr_t addr;
map_word d = {{0}};
addr = remap_window(map, ofs);
if(!addr)
return d;
d.x[0] = readw(addr);
pr_debug("ofs = 0x%08lx (%p) data = 0x%04lx\n", ofs, addr, d.x[0]);
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;
pr_debug("to = %p from = %lu len = %zd\n", 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;
pr_debug("memcpy from %p to %p len = %d\n", addr, to, toread);
memcpy_fromio(to, addr, toread);
len -= toread;
to += toread;
from += toread;
}
}
static void pcmcia_write8_remap(struct map_info *map, map_word d, unsigned long adr)
{
caddr_t addr = remap_window(map, adr);
if(!addr)
return;
pr_debug("adr = 0x%08lx (%p) data = 0x%02lx\n", adr, addr, d.x[0]);
writeb(d.x[0], addr);
}
static void pcmcia_write16_remap(struct map_info *map, map_word d, unsigned long adr)
{
caddr_t addr = remap_window(map, adr);
if(!addr)
return;
pr_debug("adr = 0x%08lx (%p) data = 0x%04lx\n", adr, addr, d.x[0]);
writew(d.x[0], 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;
pr_debug("to = %lu from = %p len = %zd\n", 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;
pr_debug("memcpy from %p to %p len = %d\n", 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 */
#define DEV_REMOVED(x) (!(pcmcia_dev_present(((struct pcmciamtd_dev *)map->map_priv_1)->p_dev)))
static map_word pcmcia_read8(struct map_info *map, unsigned long ofs)
{
caddr_t win_base = (caddr_t)map->map_priv_2;
map_word d = {{0}};
if(DEV_REMOVED(map))
return d;
d.x[0] = readb(win_base + ofs);
pr_debug("ofs = 0x%08lx (%p) data = 0x%02lx\n",
ofs, win_base + ofs, d.x[0]);
return d;
}
static map_word pcmcia_read16(struct map_info *map, unsigned long ofs)
{
caddr_t win_base = (caddr_t)map->map_priv_2;
map_word d = {{0}};
if(DEV_REMOVED(map))
return d;
d.x[0] = readw(win_base + ofs);
pr_debug("ofs = 0x%08lx (%p) data = 0x%04lx\n",
ofs, win_base + ofs, d.x[0]);
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;
if(DEV_REMOVED(map))
return;
pr_debug("to = %p from = %lu len = %zd\n", to, from, len);
memcpy_fromio(to, win_base + from, len);
}
static void pcmcia_write8(struct map_info *map, map_word d, unsigned long adr)
{
caddr_t win_base = (caddr_t)map->map_priv_2;
if(DEV_REMOVED(map))
return;
pr_debug("adr = 0x%08lx (%p) data = 0x%02lx\n",
adr, win_base + adr, d.x[0]);
writeb(d.x[0], win_base + adr);
}
static void pcmcia_write16(struct map_info *map, map_word d, unsigned long adr)
{
caddr_t win_base = (caddr_t)map->map_priv_2;
if(DEV_REMOVED(map))
return;
pr_debug("adr = 0x%08lx (%p) data = 0x%04lx\n",
adr, win_base + adr, d.x[0]);
writew(d.x[0], 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;
if(DEV_REMOVED(map))
return;
pr_debug("to = %lu from = %p len = %zd\n", to, from, len);
memcpy_toio(win_base + to, from, len);
}
static DEFINE_SPINLOCK(pcmcia_vpp_lock);
static int pcmcia_vpp_refcnt;
static void pcmciamtd_set_vpp(struct map_info *map, int on)
{
struct pcmciamtd_dev *dev = (struct pcmciamtd_dev *)map->map_priv_1;
struct pcmcia_device *link = dev->p_dev;
unsigned long flags;
pr_debug("dev = %p on = %d vpp = %d\n\n", dev, on, dev->vpp);
spin_lock_irqsave(&pcmcia_vpp_lock, flags);
if (on) {
if (++pcmcia_vpp_refcnt == 1) /* first nested 'on' */
pcmcia_fixup_vpp(link, dev->vpp);
} else {
if (--pcmcia_vpp_refcnt == 0) /* last nested 'off' */
pcmcia_fixup_vpp(link, 0);
}
spin_unlock_irqrestore(&pcmcia_vpp_lock, flags);
}
static void pcmciamtd_release(struct pcmcia_device *link)
{
struct pcmciamtd_dev *dev = link->priv;
pr_debug("link = 0x%p\n", link);
if (link->resource[2]->end) {
if(dev->win_base) {
iounmap(dev->win_base);
dev->win_base = NULL;
}
}
pcmcia_disable_device(link);
}
static int pcmciamtd_cistpl_format(struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv_data)
{
cisparse_t parse;
if (!pcmcia_parse_tuple(tuple, &parse)) {
cistpl_format_t *t = &parse.format;
(void)t; /* Shut up, gcc */
pr_debug("Format type: %u, Error Detection: %u, offset = %u, length =%u\n",
t->type, t->edc, t->offset, t->length);
}
return -ENOSPC;
}
static int pcmciamtd_cistpl_jedec(struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv_data)
{
cisparse_t parse;
int i;
if (!pcmcia_parse_tuple(tuple, &parse)) {
cistpl_jedec_t *t = &parse.jedec;
for (i = 0; i < t->nid; i++)
pr_debug("JEDEC: 0x%02x 0x%02x\n",
t->id[i].mfr, t->id[i].info);
}
return -ENOSPC;
}
static int pcmciamtd_cistpl_device(struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv_data)
{
struct pcmciamtd_dev *dev = priv_data;
cisparse_t parse;
cistpl_device_t *t = &parse.device;
int i;
if (pcmcia_parse_tuple(tuple, &parse))
return -EINVAL;
pr_debug("Common memory:\n");
dev->pcmcia_map.size = t->dev[0].size;
/* from here on: DEBUG only */
for (i = 0; i < t->ndev; i++) {
pr_debug("Region %d, type = %u\n", i, t->dev[i].type);
pr_debug("Region %d, wp = %u\n", i, t->dev[i].wp);
pr_debug("Region %d, speed = %u ns\n", i, t->dev[i].speed);
pr_debug("Region %d, size = %u bytes\n", i, t->dev[i].size);
}
return 0;
}
static int pcmciamtd_cistpl_geo(struct pcmcia_device *p_dev,
tuple_t *tuple,
void *priv_data)
{
struct pcmciamtd_dev *dev = priv_data;
cisparse_t parse;
cistpl_device_geo_t *t = &parse.device_geo;
int i;
if (pcmcia_parse_tuple(tuple, &parse))
return -EINVAL;
dev->pcmcia_map.bankwidth = t->geo[0].buswidth;
/* from here on: DEBUG only */
for (i = 0; i < t->ngeo; i++) {
pr_debug("region: %d bankwidth = %u\n", i, t->geo[i].buswidth);
pr_debug("region: %d erase_block = %u\n", i, t->geo[i].erase_block);
pr_debug("region: %d read_block = %u\n", i, t->geo[i].read_block);
pr_debug("region: %d write_block = %u\n", i, t->geo[i].write_block);
pr_debug("region: %d partition = %u\n", i, t->geo[i].partition);
pr_debug("region: %d interleave = %u\n", i, t->geo[i].interleave);
}
return 0;
}
static void card_settings(struct pcmciamtd_dev *dev, struct pcmcia_device *p_dev, int *new_name)
{
int i;
if (p_dev->prod_id[0]) {
dev->mtd_name[0] = '\0';
for (i = 0; i < 4; i++) {
if (i)
strcat(dev->mtd_name, " ");
if (p_dev->prod_id[i])
strcat(dev->mtd_name, p_dev->prod_id[i]);
}
pr_debug("Found name: %s\n", dev->mtd_name);
}
pcmcia_loop_tuple(p_dev, CISTPL_FORMAT, pcmciamtd_cistpl_format, NULL);
pcmcia_loop_tuple(p_dev, CISTPL_JEDEC_C, pcmciamtd_cistpl_jedec, NULL);
pcmcia_loop_tuple(p_dev, CISTPL_DEVICE, pcmciamtd_cistpl_device, dev);
pcmcia_loop_tuple(p_dev, CISTPL_DEVICE_GEO, pcmciamtd_cistpl_geo, dev);
if(!dev->pcmcia_map.size)
dev->pcmcia_map.size = MAX_PCMCIA_ADDR;
if(!dev->pcmcia_map.bankwidth)
dev->pcmcia_map.bankwidth = 2;
if(force_size) {
dev->pcmcia_map.size = force_size << 20;
pr_debug("size forced to %dM\n", force_size);
}
if(bankwidth) {
dev->pcmcia_map.bankwidth = bankwidth;
pr_debug("bankwidth forced to %d\n", bankwidth);
}
dev->pcmcia_map.name = dev->mtd_name;
if(!dev->mtd_name[0]) {
strcpy(dev->mtd_name, "PCMCIA Memory card");
*new_name = 1;
}
pr_debug("Device: Size: %lu Width:%d Name: %s\n",
dev->pcmcia_map.size,
dev->pcmcia_map.bankwidth << 3, dev->mtd_name);
}
static int pcmciamtd_config(struct pcmcia_device *link)
{
struct pcmciamtd_dev *dev = link->priv;
struct mtd_info *mtd = NULL;
int ret;
int i, j = 0;
static char *probes[] = { "jedec_probe", "cfi_probe" };
int new_name = 0;
pr_debug("link=0x%p\n", link);
card_settings(dev, link, &new_name);
dev->pcmcia_map.phys = NO_XIP;
dev->pcmcia_map.copy_from = pcmcia_copy_from_remap;
dev->pcmcia_map.copy_to = pcmcia_copy_to_remap;
if (dev->pcmcia_map.bankwidth == 1) {
dev->pcmcia_map.read = pcmcia_read8_remap;
dev->pcmcia_map.write = pcmcia_write8_remap;
} else {
dev->pcmcia_map.read = pcmcia_read16_remap;
dev->pcmcia_map.write = pcmcia_write16_remap;
}
if(setvpp == 1)
dev->pcmcia_map.set_vpp = pcmciamtd_set_vpp;
/* Request a memory window for PCMCIA. Some architeures can map windows
* up to the maximum that PCMCIA can support (64MiB) - this is ideal and
* we aim for a window the size of the whole card - otherwise we try
* smaller windows until we succeed
*/
link->resource[2]->flags |= WIN_MEMORY_TYPE_CM | WIN_ENABLE;
link->resource[2]->flags |= (dev->pcmcia_map.bankwidth == 1) ?
WIN_DATA_WIDTH_8 : WIN_DATA_WIDTH_16;
link->resource[2]->start = 0;
link->resource[2]->end = (force_size) ? force_size << 20 :
MAX_PCMCIA_ADDR;
dev->win_size = 0;
do {
int ret;
pr_debug("requesting window with size = %luKiB memspeed = %d\n",
(unsigned long) resource_size(link->resource[2]) >> 10,
mem_speed);
ret = pcmcia_request_window(link, link->resource[2], mem_speed);
pr_debug("ret = %d dev->win_size = %d\n", ret, dev->win_size);
if(ret) {
j++;
link->resource[2]->start = 0;
link->resource[2]->end = (force_size) ?
force_size << 20 : MAX_PCMCIA_ADDR;
link->resource[2]->end >>= j;
} else {
pr_debug("Got window of size %luKiB\n", (unsigned long)
resource_size(link->resource[2]) >> 10);
dev->win_size = resource_size(link->resource[2]);
break;
}
} while (link->resource[2]->end >= 0x1000);
pr_debug("dev->win_size = %d\n", dev->win_size);
if(!dev->win_size) {
dev_err(&dev->p_dev->dev, "Cannot allocate memory window\n");
pcmciamtd_release(link);
return -ENODEV;
}
pr_debug("Allocated a window of %dKiB\n", dev->win_size >> 10);
/* Get write protect status */
dev->win_base = ioremap(link->resource[2]->start,
resource_size(link->resource[2]));
if(!dev->win_base) {
dev_err(&dev->p_dev->dev, "ioremap(%pR) failed\n",
link->resource[2]);
pcmciamtd_release(link);
return -ENODEV;
}
pr_debug("mapped window dev = %p @ %pR, base = %p\n",
dev, link->resource[2], dev->win_base);
dev->offset = 0;
dev->pcmcia_map.map_priv_1 = (unsigned long)dev;
dev->pcmcia_map.map_priv_2 = (unsigned long)link->resource[2];
dev->vpp = (vpp) ? vpp : link->socket->socket.Vpp;
if(setvpp == 2) {
link->vpp = dev->vpp;
} else {
link->vpp = 0;
}
link->config_index = 0;
pr_debug("Setting Configuration\n");
ret = pcmcia_enable_device(link);
if (ret != 0) {
if (dev->win_base) {
iounmap(dev->win_base);
dev->win_base = NULL;
}
return -ENODEV;
}
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 < ARRAY_SIZE(probes); i++) {
pr_debug("Trying %s\n", probes[i]);
mtd = do_map_probe(probes[i], &dev->pcmcia_map);
if(mtd)
break;
pr_debug("FAILED: %s\n", probes[i]);
}
}
if(!mtd) {
pr_debug("Can not find an MTD\n");
pcmciamtd_release(link);
return -ENODEV;
}
dev->mtd_info = mtd;
mtd->owner = THIS_MODULE;
if(new_name) {
int size = 0;
char unit = ' ';
/* Since we are using a default name, make it better by adding
* in the size
*/
if(mtd->size < 1048576) { /* <1MiB in size, show size in KiB */
size = mtd->size >> 10;
unit = 'K';
} else {
size = mtd->size >> 20;
unit = 'M';
}
snprintf(dev->mtd_name, sizeof(dev->mtd_name), "%d%ciB %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(mtd->size <= dev->win_size) {
pr_debug("Using non remapping memory functions\n");
dev->pcmcia_map.map_priv_2 = (unsigned long)dev->win_base;
if (dev->pcmcia_map.bankwidth == 1) {
dev->pcmcia_map.read = pcmcia_read8;
dev->pcmcia_map.write = pcmcia_write8;
} else {
dev->pcmcia_map.read = pcmcia_read16;
dev->pcmcia_map.write = pcmcia_write16;
}
dev->pcmcia_map.copy_from = pcmcia_copy_from;
dev->pcmcia_map.copy_to = pcmcia_copy_to;
}
if (mtd_device_register(mtd, NULL, 0)) {
map_destroy(mtd);
dev->mtd_info = NULL;
dev_err(&dev->p_dev->dev,
"Could not register the MTD device\n");
pcmciamtd_release(link);
return -ENODEV;
}
dev_info(&dev->p_dev->dev, "mtd%d: %s\n", mtd->index, mtd->name);
return 0;
}
static int pcmciamtd_suspend(struct pcmcia_device *dev)
{
pr_debug("EVENT_PM_RESUME\n");
/* get_lock(link); */
return 0;
}
static int pcmciamtd_resume(struct pcmcia_device *dev)
{
pr_debug("EVENT_PM_SUSPEND\n");
/* free_lock(link); */
return 0;
}
static void pcmciamtd_detach(struct pcmcia_device *link)
{
struct pcmciamtd_dev *dev = link->priv;
pr_debug("link=0x%p\n", link);
if(dev->mtd_info) {
mtd_device_unregister(dev->mtd_info);
dev_info(&dev->p_dev->dev, "mtd%d: Removing\n",
dev->mtd_info->index);
map_destroy(dev->mtd_info);
}
pcmciamtd_release(link);
}
static int pcmciamtd_probe(struct pcmcia_device *link)
{
struct pcmciamtd_dev *dev;
/* Create new memory card device */
dev = kzalloc(sizeof(*dev), GFP_KERNEL);
if (!dev) return -ENOMEM;
pr_debug("dev=0x%p\n", dev);
dev->p_dev = link;
link->priv = dev;
return pcmciamtd_config(link);
}
static const struct pcmcia_device_id pcmciamtd_ids[] = {
PCMCIA_DEVICE_FUNC_ID(1),
PCMCIA_DEVICE_PROD_ID123("IO DATA", "PCS-2M", "2MB SRAM", 0x547e66dc, 0x1fed36cd, 0x36eadd21),
PCMCIA_DEVICE_PROD_ID12("IBM", "2MB SRAM", 0xb569a6e5, 0x36eadd21),
PCMCIA_DEVICE_PROD_ID12("IBM", "4MB FLASH", 0xb569a6e5, 0x8bc54d2a),
PCMCIA_DEVICE_PROD_ID12("IBM", "8MB FLASH", 0xb569a6e5, 0x6df1be3e),
PCMCIA_DEVICE_PROD_ID12("Intel", "S2E20SW", 0x816cc815, 0xd14c9dcf),
PCMCIA_DEVICE_PROD_ID12("Intel", "S2E8 SW", 0x816cc815, 0xa2d7dedb),
PCMCIA_DEVICE_PROD_ID12("intel", "SERIES2-02 ", 0x40ade711, 0x145cea5c),
PCMCIA_DEVICE_PROD_ID12("intel", "SERIES2-04 ", 0x40ade711, 0x42064dda),
PCMCIA_DEVICE_PROD_ID12("intel", "SERIES2-20 ", 0x40ade711, 0x25ee5cb0),
PCMCIA_DEVICE_PROD_ID12("intel", "VALUE SERIES 100 ", 0x40ade711, 0xdf8506d8),
PCMCIA_DEVICE_PROD_ID12("KINGMAX TECHNOLOGY INC.", "SRAM 256K Bytes", 0x54d0c69c, 0xad12c29c),
PCMCIA_DEVICE_PROD_ID12("Maxtor", "MAXFL MobileMax Flash Memory Card", 0xb68968c8, 0x2dfb47b0),
PCMCIA_DEVICE_PROD_ID123("M-Systems", "M-SYS Flash Memory Card", "(c) M-Systems", 0x7ed2ad87, 0x675dc3fb, 0x7aef3965),
PCMCIA_DEVICE_PROD_ID12("PRETEC", " 2MB SRAM CARD", 0xebf91155, 0x805360ca),
PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB101EN20", 0xf9876baf, 0xad0b207b),
PCMCIA_DEVICE_PROD_ID12("SEIKO EPSON", "WWB513EN20", 0xf9876baf, 0xe8d884ad),
PCMCIA_DEVICE_PROD_ID12("SMART Modular Technologies", " 4MB FLASH Card", 0x96fd8277, 0x737a5b05),
PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-3000", 0x05ddca47, 0xe7d67bca),
PCMCIA_DEVICE_PROD_ID12("Starfish, Inc.", "REX-4100", 0x05ddca47, 0x7bc32944),
/* the following was commented out in pcmcia-cs-3.2.7 */
/* PCMCIA_DEVICE_PROD_ID12("RATOC Systems,Inc.", "SmartMedia ADAPTER PC Card", 0xf4a2fefe, 0x5885b2ae), */
#ifdef CONFIG_MTD_PCMCIA_ANONYMOUS
{ .match_flags = PCMCIA_DEV_ID_MATCH_ANONYMOUS, },
#endif
PCMCIA_DEVICE_NULL
};
MODULE_DEVICE_TABLE(pcmcia, pcmciamtd_ids);
static struct pcmcia_driver pcmciamtd_driver = {
.name = "pcmciamtd",
.probe = pcmciamtd_probe,
.remove = pcmciamtd_detach,
.owner = THIS_MODULE,
.id_table = pcmciamtd_ids,
.suspend = pcmciamtd_suspend,
.resume = pcmciamtd_resume,
};
static int __init init_pcmciamtd(void)
{
if(bankwidth && bankwidth != 1 && bankwidth != 2) {
info("bad bankwidth (%d), using default", bankwidth);
bankwidth = 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;
}
return pcmcia_register_driver(&pcmciamtd_driver);
}
static void __exit exit_pcmciamtd(void)
{
pr_debug(DRIVER_DESC " unloading");
pcmcia_unregister_driver(&pcmciamtd_driver);
}
module_init(init_pcmciamtd);
module_exit(exit_pcmciamtd);

282
drivers/mtd/maps/physmap.c Normal file
View file

@ -0,0 +1,282 @@
/*
* Normal mappings of chips in physical memory
*
* Copyright (C) 2003 MontaVista Software Inc.
* Author: Jun Sun, jsun@mvista.com or jsun@junsun.net
*
* 031022 - [jsun] add run-time configure and partition setup
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/physmap.h>
#include <linux/mtd/concat.h>
#include <linux/io.h>
#define MAX_RESOURCES 4
struct physmap_flash_info {
struct mtd_info *mtd[MAX_RESOURCES];
struct mtd_info *cmtd;
struct map_info map[MAX_RESOURCES];
spinlock_t vpp_lock;
int vpp_refcnt;
};
static int physmap_flash_remove(struct platform_device *dev)
{
struct physmap_flash_info *info;
struct physmap_flash_data *physmap_data;
int i;
info = platform_get_drvdata(dev);
if (info == NULL)
return 0;
physmap_data = dev_get_platdata(&dev->dev);
if (info->cmtd) {
mtd_device_unregister(info->cmtd);
if (info->cmtd != info->mtd[0])
mtd_concat_destroy(info->cmtd);
}
for (i = 0; i < MAX_RESOURCES; i++) {
if (info->mtd[i] != NULL)
map_destroy(info->mtd[i]);
}
if (physmap_data->exit)
physmap_data->exit(dev);
return 0;
}
static void physmap_set_vpp(struct map_info *map, int state)
{
struct platform_device *pdev;
struct physmap_flash_data *physmap_data;
struct physmap_flash_info *info;
unsigned long flags;
pdev = (struct platform_device *)map->map_priv_1;
physmap_data = dev_get_platdata(&pdev->dev);
if (!physmap_data->set_vpp)
return;
info = platform_get_drvdata(pdev);
spin_lock_irqsave(&info->vpp_lock, flags);
if (state) {
if (++info->vpp_refcnt == 1) /* first nested 'on' */
physmap_data->set_vpp(pdev, 1);
} else {
if (--info->vpp_refcnt == 0) /* last nested 'off' */
physmap_data->set_vpp(pdev, 0);
}
spin_unlock_irqrestore(&info->vpp_lock, flags);
}
static const char * const rom_probe_types[] = {
"cfi_probe", "jedec_probe", "qinfo_probe", "map_rom", NULL };
static const char * const part_probe_types[] = {
"cmdlinepart", "RedBoot", "afs", NULL };
static int physmap_flash_probe(struct platform_device *dev)
{
struct physmap_flash_data *physmap_data;
struct physmap_flash_info *info;
const char * const *probe_type;
const char * const *part_types;
int err = 0;
int i;
int devices_found = 0;
physmap_data = dev_get_platdata(&dev->dev);
if (physmap_data == NULL)
return -ENODEV;
info = devm_kzalloc(&dev->dev, sizeof(struct physmap_flash_info),
GFP_KERNEL);
if (info == NULL) {
err = -ENOMEM;
goto err_out;
}
if (physmap_data->init) {
err = physmap_data->init(dev);
if (err)
goto err_out;
}
platform_set_drvdata(dev, info);
for (i = 0; i < dev->num_resources; i++) {
printk(KERN_NOTICE "physmap platform flash device: %.8llx at %.8llx\n",
(unsigned long long)resource_size(&dev->resource[i]),
(unsigned long long)dev->resource[i].start);
if (!devm_request_mem_region(&dev->dev,
dev->resource[i].start,
resource_size(&dev->resource[i]),
dev_name(&dev->dev))) {
dev_err(&dev->dev, "Could not reserve memory region\n");
err = -ENOMEM;
goto err_out;
}
info->map[i].name = dev_name(&dev->dev);
info->map[i].phys = dev->resource[i].start;
info->map[i].size = resource_size(&dev->resource[i]);
info->map[i].bankwidth = physmap_data->width;
info->map[i].set_vpp = physmap_set_vpp;
info->map[i].pfow_base = physmap_data->pfow_base;
info->map[i].map_priv_1 = (unsigned long)dev;
info->map[i].virt = devm_ioremap(&dev->dev, info->map[i].phys,
info->map[i].size);
if (info->map[i].virt == NULL) {
dev_err(&dev->dev, "Failed to ioremap flash region\n");
err = -EIO;
goto err_out;
}
simple_map_init(&info->map[i]);
probe_type = rom_probe_types;
if (physmap_data->probe_type == NULL) {
for (; info->mtd[i] == NULL && *probe_type != NULL; probe_type++)
info->mtd[i] = do_map_probe(*probe_type, &info->map[i]);
} else
info->mtd[i] = do_map_probe(physmap_data->probe_type, &info->map[i]);
if (info->mtd[i] == NULL) {
dev_err(&dev->dev, "map_probe failed\n");
err = -ENXIO;
goto err_out;
} else {
devices_found++;
}
info->mtd[i]->owner = THIS_MODULE;
info->mtd[i]->dev.parent = &dev->dev;
}
if (devices_found == 1) {
info->cmtd = info->mtd[0];
} else if (devices_found > 1) {
/*
* We detected multiple devices. Concatenate them together.
*/
info->cmtd = mtd_concat_create(info->mtd, devices_found, dev_name(&dev->dev));
if (info->cmtd == NULL)
err = -ENXIO;
}
if (err)
goto err_out;
spin_lock_init(&info->vpp_lock);
part_types = physmap_data->part_probe_types ? : part_probe_types;
mtd_device_parse_register(info->cmtd, part_types, NULL,
physmap_data->parts, physmap_data->nr_parts);
return 0;
err_out:
physmap_flash_remove(dev);
return err;
}
#ifdef CONFIG_PM
static void physmap_flash_shutdown(struct platform_device *dev)
{
struct physmap_flash_info *info = platform_get_drvdata(dev);
int i;
for (i = 0; i < MAX_RESOURCES && info->mtd[i]; i++)
if (mtd_suspend(info->mtd[i]) == 0)
mtd_resume(info->mtd[i]);
}
#else
#define physmap_flash_shutdown NULL
#endif
static struct platform_driver physmap_flash_driver = {
.probe = physmap_flash_probe,
.remove = physmap_flash_remove,
.shutdown = physmap_flash_shutdown,
.driver = {
.name = "physmap-flash",
.owner = THIS_MODULE,
},
};
#ifdef CONFIG_MTD_PHYSMAP_COMPAT
static struct physmap_flash_data physmap_flash_data = {
.width = CONFIG_MTD_PHYSMAP_BANKWIDTH,
};
static struct resource physmap_flash_resource = {
.start = CONFIG_MTD_PHYSMAP_START,
.end = CONFIG_MTD_PHYSMAP_START + CONFIG_MTD_PHYSMAP_LEN - 1,
.flags = IORESOURCE_MEM,
};
static struct platform_device physmap_flash = {
.name = "physmap-flash",
.id = 0,
.dev = {
.platform_data = &physmap_flash_data,
},
.num_resources = 1,
.resource = &physmap_flash_resource,
};
#endif
static int __init physmap_init(void)
{
int err;
err = platform_driver_register(&physmap_flash_driver);
#ifdef CONFIG_MTD_PHYSMAP_COMPAT
if (err == 0) {
err = platform_device_register(&physmap_flash);
if (err)
platform_driver_unregister(&physmap_flash_driver);
}
#endif
return err;
}
static void __exit physmap_exit(void)
{
#ifdef CONFIG_MTD_PHYSMAP_COMPAT
platform_device_unregister(&physmap_flash);
#endif
platform_driver_unregister(&physmap_flash_driver);
}
module_init(physmap_init);
module_exit(physmap_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("Generic configurable MTD map driver");
/* legacy platform drivers can't hotplug or coldplg */
#ifndef CONFIG_MTD_PHYSMAP_COMPAT
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:physmap-flash");
#endif

View file

@ -0,0 +1,368 @@
/*
* Flash mappings described by the OF (or flattened) device tree
*
* Copyright (C) 2006 MontaVista Software Inc.
* Author: Vitaly Wool <vwool@ru.mvista.com>
*
* Revised to handle newer style flash binding by:
* Copyright (C) 2007 David Gibson, IBM Corporation.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_platform.h>
#include <linux/slab.h>
struct of_flash_list {
struct mtd_info *mtd;
struct map_info map;
struct resource *res;
};
struct of_flash {
struct mtd_info *cmtd;
int list_size; /* number of elements in of_flash_list */
struct of_flash_list list[0];
};
static int of_flash_remove(struct platform_device *dev)
{
struct of_flash *info;
int i;
info = dev_get_drvdata(&dev->dev);
if (!info)
return 0;
dev_set_drvdata(&dev->dev, NULL);
if (info->cmtd != info->list[0].mtd) {
mtd_device_unregister(info->cmtd);
mtd_concat_destroy(info->cmtd);
}
if (info->cmtd)
mtd_device_unregister(info->cmtd);
for (i = 0; i < info->list_size; i++) {
if (info->list[i].mtd)
map_destroy(info->list[i].mtd);
if (info->list[i].map.virt)
iounmap(info->list[i].map.virt);
if (info->list[i].res) {
release_resource(info->list[i].res);
kfree(info->list[i].res);
}
}
return 0;
}
static const char * const rom_probe_types[] = {
"cfi_probe", "jedec_probe", "map_rom" };
/* Helper function to handle probing of the obsolete "direct-mapped"
* compatible binding, which has an extra "probe-type" property
* describing the type of flash probe necessary. */
static struct mtd_info *obsolete_probe(struct platform_device *dev,
struct map_info *map)
{
struct device_node *dp = dev->dev.of_node;
const char *of_probe;
struct mtd_info *mtd;
int i;
dev_warn(&dev->dev, "Device tree uses obsolete \"direct-mapped\" "
"flash binding\n");
of_probe = of_get_property(dp, "probe-type", NULL);
if (!of_probe) {
for (i = 0; i < ARRAY_SIZE(rom_probe_types); i++) {
mtd = do_map_probe(rom_probe_types[i], map);
if (mtd)
return mtd;
}
return NULL;
} else if (strcmp(of_probe, "CFI") == 0) {
return do_map_probe("cfi_probe", map);
} else if (strcmp(of_probe, "JEDEC") == 0) {
return do_map_probe("jedec_probe", map);
} else {
if (strcmp(of_probe, "ROM") != 0)
dev_warn(&dev->dev, "obsolete_probe: don't know probe "
"type '%s', mapping as rom\n", of_probe);
return do_map_probe("map_rom", map);
}
}
/* When partitions are set we look for a linux,part-probe property which
specifies the list of partition probers to use. If none is given then the
default is use. These take precedence over other device tree
information. */
static const char * const part_probe_types_def[] = {
"cmdlinepart", "RedBoot", "ofpart", "ofoldpart", NULL };
static const char * const *of_get_probes(struct device_node *dp)
{
const char *cp;
int cplen;
unsigned int l;
unsigned int count;
const char **res;
cp = of_get_property(dp, "linux,part-probe", &cplen);
if (cp == NULL)
return part_probe_types_def;
count = 0;
for (l = 0; l != cplen; l++)
if (cp[l] == 0)
count++;
res = kzalloc((count + 1)*sizeof(*res), GFP_KERNEL);
count = 0;
while (cplen > 0) {
res[count] = cp;
l = strlen(cp) + 1;
cp += l;
cplen -= l;
count++;
}
return res;
}
static void of_free_probes(const char * const *probes)
{
if (probes != part_probe_types_def)
kfree(probes);
}
static struct of_device_id of_flash_match[];
static int of_flash_probe(struct platform_device *dev)
{
const char * const *part_probe_types;
const struct of_device_id *match;
struct device_node *dp = dev->dev.of_node;
struct resource res;
struct of_flash *info;
const char *probe_type;
const __be32 *width;
int err;
int i;
int count;
const __be32 *p;
int reg_tuple_size;
struct mtd_info **mtd_list = NULL;
resource_size_t res_size;
struct mtd_part_parser_data ppdata;
bool map_indirect;
const char *mtd_name = NULL;
match = of_match_device(of_flash_match, &dev->dev);
if (!match)
return -EINVAL;
probe_type = match->data;
reg_tuple_size = (of_n_addr_cells(dp) + of_n_size_cells(dp)) * sizeof(u32);
of_property_read_string(dp, "linux,mtd-name", &mtd_name);
/*
* Get number of "reg" tuples. Scan for MTD devices on area's
* described by each "reg" region. This makes it possible (including
* the concat support) to support the Intel P30 48F4400 chips which
* consists internally of 2 non-identical NOR chips on one die.
*/
p = of_get_property(dp, "reg", &count);
if (count % reg_tuple_size != 0) {
dev_err(&dev->dev, "Malformed reg property on %s\n",
dev->dev.of_node->full_name);
err = -EINVAL;
goto err_flash_remove;
}
count /= reg_tuple_size;
map_indirect = of_property_read_bool(dp, "no-unaligned-direct-access");
err = -ENOMEM;
info = devm_kzalloc(&dev->dev,
sizeof(struct of_flash) +
sizeof(struct of_flash_list) * count, GFP_KERNEL);
if (!info)
goto err_flash_remove;
dev_set_drvdata(&dev->dev, info);
mtd_list = kzalloc(sizeof(*mtd_list) * count, GFP_KERNEL);
if (!mtd_list)
goto err_flash_remove;
for (i = 0; i < count; i++) {
err = -ENXIO;
if (of_address_to_resource(dp, i, &res)) {
/*
* Continue with next register tuple if this
* one is not mappable
*/
continue;
}
dev_dbg(&dev->dev, "of_flash device: %pR\n", &res);
err = -EBUSY;
res_size = resource_size(&res);
info->list[i].res = request_mem_region(res.start, res_size,
dev_name(&dev->dev));
if (!info->list[i].res)
goto err_out;
err = -ENXIO;
width = of_get_property(dp, "bank-width", NULL);
if (!width) {
dev_err(&dev->dev, "Can't get bank width from device"
" tree\n");
goto err_out;
}
info->list[i].map.name = mtd_name ?: dev_name(&dev->dev);
info->list[i].map.phys = res.start;
info->list[i].map.size = res_size;
info->list[i].map.bankwidth = be32_to_cpup(width);
info->list[i].map.device_node = dp;
err = -ENOMEM;
info->list[i].map.virt = ioremap(info->list[i].map.phys,
info->list[i].map.size);
if (!info->list[i].map.virt) {
dev_err(&dev->dev, "Failed to ioremap() flash"
" region\n");
goto err_out;
}
simple_map_init(&info->list[i].map);
/*
* On some platforms (e.g. MPC5200) a direct 1:1 mapping
* may cause problems with JFFS2 usage, as the local bus (LPB)
* doesn't support unaligned accesses as implemented in the
* JFFS2 code via memcpy(). By setting NO_XIP, the
* flash will not be exposed directly to the MTD users
* (e.g. JFFS2) any more.
*/
if (map_indirect)
info->list[i].map.phys = NO_XIP;
if (probe_type) {
info->list[i].mtd = do_map_probe(probe_type,
&info->list[i].map);
} else {
info->list[i].mtd = obsolete_probe(dev,
&info->list[i].map);
}
mtd_list[i] = info->list[i].mtd;
err = -ENXIO;
if (!info->list[i].mtd) {
dev_err(&dev->dev, "do_map_probe() failed\n");
goto err_out;
} else {
info->list_size++;
}
info->list[i].mtd->owner = THIS_MODULE;
info->list[i].mtd->dev.parent = &dev->dev;
}
err = 0;
info->cmtd = NULL;
if (info->list_size == 1) {
info->cmtd = info->list[0].mtd;
} else if (info->list_size > 1) {
/*
* We detected multiple devices. Concatenate them together.
*/
info->cmtd = mtd_concat_create(mtd_list, info->list_size,
dev_name(&dev->dev));
}
if (info->cmtd == NULL)
err = -ENXIO;
if (err)
goto err_out;
ppdata.of_node = dp;
part_probe_types = of_get_probes(dp);
mtd_device_parse_register(info->cmtd, part_probe_types, &ppdata,
NULL, 0);
of_free_probes(part_probe_types);
kfree(mtd_list);
return 0;
err_out:
kfree(mtd_list);
err_flash_remove:
of_flash_remove(dev);
return err;
}
static struct of_device_id of_flash_match[] = {
{
.compatible = "cfi-flash",
.data = (void *)"cfi_probe",
},
{
/* FIXME: JEDEC chips can't be safely and reliably
* probed, although the mtd code gets it right in
* practice most of the time. We should use the
* vendor and device ids specified by the binding to
* bypass the heuristic probe code, but the mtd layer
* provides, at present, no interface for doing so
* :(. */
.compatible = "jedec-flash",
.data = (void *)"jedec_probe",
},
{
.compatible = "mtd-ram",
.data = (void *)"map_ram",
},
{
.compatible = "mtd-rom",
.data = (void *)"map_rom",
},
{
.type = "rom",
.compatible = "direct-mapped"
},
{ },
};
MODULE_DEVICE_TABLE(of, of_flash_match);
static struct platform_driver of_flash_driver = {
.driver = {
.name = "of-flash",
.owner = THIS_MODULE,
.of_match_table = of_flash_match,
},
.probe = of_flash_probe,
.remove = of_flash_remove,
};
module_platform_driver(of_flash_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Vitaly Wool <vwool@ru.mvista.com>");
MODULE_DESCRIPTION("Device tree based MTD map driver");

292
drivers/mtd/maps/pismo.c Normal file
View file

@ -0,0 +1,292 @@
/*
* PISMO memory driver - http://www.pismoworld.org/
*
* For ARM Realview and Versatile platforms
*
* 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.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/spinlock.h>
#include <linux/mutex.h>
#include <linux/mtd/physmap.h>
#include <linux/mtd/plat-ram.h>
#include <linux/mtd/pismo.h>
#define PISMO_NUM_CS 5
struct pismo_cs_block {
u8 type;
u8 width;
__le16 access;
__le32 size;
u32 reserved[2];
char device[32];
} __packed;
struct pismo_eeprom {
struct pismo_cs_block cs[PISMO_NUM_CS];
char board[15];
u8 sum;
} __packed;
struct pismo_mem {
phys_addr_t base;
u32 size;
u16 access;
u8 width;
u8 type;
};
struct pismo_data {
struct i2c_client *client;
void (*vpp)(void *, int);
void *vpp_data;
struct platform_device *dev[PISMO_NUM_CS];
};
static void pismo_set_vpp(struct platform_device *pdev, int on)
{
struct i2c_client *client = to_i2c_client(pdev->dev.parent);
struct pismo_data *pismo = i2c_get_clientdata(client);
pismo->vpp(pismo->vpp_data, on);
}
static unsigned int pismo_width_to_bytes(unsigned int width)
{
width &= 15;
if (width > 2)
return 0;
return 1 << width;
}
static int pismo_eeprom_read(struct i2c_client *client, void *buf, u8 addr,
size_t size)
{
int ret;
struct i2c_msg msg[] = {
{
.addr = client->addr,
.len = sizeof(addr),
.buf = &addr,
}, {
.addr = client->addr,
.flags = I2C_M_RD,
.len = size,
.buf = buf,
},
};
ret = i2c_transfer(client->adapter, msg, ARRAY_SIZE(msg));
return ret == ARRAY_SIZE(msg) ? size : -EIO;
}
static int pismo_add_device(struct pismo_data *pismo, int i,
struct pismo_mem *region, const char *name,
void *pdata, size_t psize)
{
struct platform_device *dev;
struct resource res = { };
phys_addr_t base = region->base;
int ret;
if (base == ~0)
return -ENXIO;
res.start = base;
res.end = base + region->size - 1;
res.flags = IORESOURCE_MEM;
dev = platform_device_alloc(name, i);
if (!dev)
return -ENOMEM;
dev->dev.parent = &pismo->client->dev;
do {
ret = platform_device_add_resources(dev, &res, 1);
if (ret)
break;
ret = platform_device_add_data(dev, pdata, psize);
if (ret)
break;
ret = platform_device_add(dev);
if (ret)
break;
pismo->dev[i] = dev;
return 0;
} while (0);
platform_device_put(dev);
return ret;
}
static int pismo_add_nor(struct pismo_data *pismo, int i,
struct pismo_mem *region)
{
struct physmap_flash_data data = {
.width = region->width,
};
if (pismo->vpp)
data.set_vpp = pismo_set_vpp;
return pismo_add_device(pismo, i, region, "physmap-flash",
&data, sizeof(data));
}
static int pismo_add_sram(struct pismo_data *pismo, int i,
struct pismo_mem *region)
{
struct platdata_mtd_ram data = {
.bankwidth = region->width,
};
return pismo_add_device(pismo, i, region, "mtd-ram",
&data, sizeof(data));
}
static void pismo_add_one(struct pismo_data *pismo, int i,
const struct pismo_cs_block *cs, phys_addr_t base)
{
struct device *dev = &pismo->client->dev;
struct pismo_mem region;
region.base = base;
region.type = cs->type;
region.width = pismo_width_to_bytes(cs->width);
region.access = le16_to_cpu(cs->access);
region.size = le32_to_cpu(cs->size);
if (region.width == 0) {
dev_err(dev, "cs%u: bad width: %02x, ignoring\n", i, cs->width);
return;
}
/*
* FIXME: may need to the platforms memory controller here, but at
* the moment we assume that it has already been correctly setup.
* The memory controller can also tell us the base address as well.
*/
dev_info(dev, "cs%u: %.32s: type %02x access %u00ps size %uK\n",
i, cs->device, region.type, region.access, region.size / 1024);
switch (region.type) {
case 0:
break;
case 1:
/* static DOC */
break;
case 2:
/* static NOR */
pismo_add_nor(pismo, i, &region);
break;
case 3:
/* static RAM */
pismo_add_sram(pismo, i, &region);
break;
}
}
static int pismo_remove(struct i2c_client *client)
{
struct pismo_data *pismo = i2c_get_clientdata(client);
int i;
for (i = 0; i < ARRAY_SIZE(pismo->dev); i++)
platform_device_unregister(pismo->dev[i]);
kfree(pismo);
return 0;
}
static int pismo_probe(struct i2c_client *client,
const struct i2c_device_id *id)
{
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
struct pismo_pdata *pdata = client->dev.platform_data;
struct pismo_eeprom eeprom;
struct pismo_data *pismo;
int ret, i;
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
dev_err(&client->dev, "functionality mismatch\n");
return -EIO;
}
pismo = kzalloc(sizeof(*pismo), GFP_KERNEL);
if (!pismo)
return -ENOMEM;
pismo->client = client;
if (pdata) {
pismo->vpp = pdata->set_vpp;
pismo->vpp_data = pdata->vpp_data;
}
i2c_set_clientdata(client, pismo);
ret = pismo_eeprom_read(client, &eeprom, 0, sizeof(eeprom));
if (ret < 0) {
dev_err(&client->dev, "error reading EEPROM: %d\n", ret);
goto exit_free;
}
dev_info(&client->dev, "%.15s board found\n", eeprom.board);
for (i = 0; i < ARRAY_SIZE(eeprom.cs); i++)
if (eeprom.cs[i].type != 0xff)
pismo_add_one(pismo, i, &eeprom.cs[i],
pdata->cs_addrs[i]);
return 0;
exit_free:
kfree(pismo);
return ret;
}
static const struct i2c_device_id pismo_id[] = {
{ "pismo" },
{ },
};
MODULE_DEVICE_TABLE(i2c, pismo_id);
static struct i2c_driver pismo_driver = {
.driver = {
.name = "pismo",
.owner = THIS_MODULE,
},
.probe = pismo_probe,
.remove = pismo_remove,
.id_table = pismo_id,
};
static int __init pismo_init(void)
{
BUILD_BUG_ON(sizeof(struct pismo_cs_block) != 48);
BUILD_BUG_ON(sizeof(struct pismo_eeprom) != 256);
return i2c_add_driver(&pismo_driver);
}
module_init(pismo_init);
static void __exit pismo_exit(void)
{
i2c_del_driver(&pismo_driver);
}
module_exit(pismo_exit);
MODULE_AUTHOR("Russell King <linux@arm.linux.org.uk>");
MODULE_DESCRIPTION("PISMO memory driver");
MODULE_LICENSE("GPL");

262
drivers/mtd/maps/plat-ram.c Normal file
View file

@ -0,0 +1,262 @@
/* drivers/mtd/maps/plat-ram.c
*
* (c) 2004-2005 Simtec Electronics
* http://www.simtec.co.uk/products/SWLINUX/
* Ben Dooks <ben@simtec.co.uk>
*
* Generic platform device based RAM map
*
* 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/string.h>
#include <linux/ioport.h>
#include <linux/device.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/plat-ram.h>
#include <asm/io.h>
/* private structure for each mtd platform ram device created */
struct platram_info {
struct device *dev;
struct mtd_info *mtd;
struct map_info map;
struct resource *area;
struct platdata_mtd_ram *pdata;
};
/* to_platram_info()
*
* device private data to struct platram_info conversion
*/
static inline struct platram_info *to_platram_info(struct platform_device *dev)
{
return platform_get_drvdata(dev);
}
/* platram_setrw
*
* call the platform device's set rw/ro control
*
* to = 0 => read-only
* = 1 => read-write
*/
static inline void platram_setrw(struct platram_info *info, int to)
{
if (info->pdata == NULL)
return;
if (info->pdata->set_rw != NULL)
(info->pdata->set_rw)(info->dev, to);
}
/* platram_remove
*
* called to remove the device from the driver's control
*/
static int platram_remove(struct platform_device *pdev)
{
struct platram_info *info = to_platram_info(pdev);
dev_dbg(&pdev->dev, "removing device\n");
if (info == NULL)
return 0;
if (info->mtd) {
mtd_device_unregister(info->mtd);
map_destroy(info->mtd);
}
/* ensure ram is left read-only */
platram_setrw(info, PLATRAM_RO);
/* release resources */
if (info->area) {
release_resource(info->area);
kfree(info->area);
}
if (info->map.virt != NULL)
iounmap(info->map.virt);
kfree(info);
return 0;
}
/* platram_probe
*
* called from device drive system when a device matching our
* driver is found.
*/
static int platram_probe(struct platform_device *pdev)
{
struct platdata_mtd_ram *pdata;
struct platram_info *info;
struct resource *res;
int err = 0;
dev_dbg(&pdev->dev, "probe entered\n");
if (dev_get_platdata(&pdev->dev) == NULL) {
dev_err(&pdev->dev, "no platform data supplied\n");
err = -ENOENT;
goto exit_error;
}
pdata = dev_get_platdata(&pdev->dev);
info = kzalloc(sizeof(*info), GFP_KERNEL);
if (info == NULL) {
err = -ENOMEM;
goto exit_error;
}
platform_set_drvdata(pdev, info);
info->dev = &pdev->dev;
info->pdata = pdata;
/* get the resource for the memory mapping */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "no memory resource specified\n");
err = -ENOENT;
goto exit_free;
}
dev_dbg(&pdev->dev, "got platform resource %p (0x%llx)\n", res,
(unsigned long long)res->start);
/* setup map parameters */
info->map.phys = res->start;
info->map.size = resource_size(res);
info->map.name = pdata->mapname != NULL ?
(char *)pdata->mapname : (char *)pdev->name;
info->map.bankwidth = pdata->bankwidth;
/* register our usage of the memory area */
info->area = request_mem_region(res->start, info->map.size, pdev->name);
if (info->area == NULL) {
dev_err(&pdev->dev, "failed to request memory region\n");
err = -EIO;
goto exit_free;
}
/* remap the memory area */
info->map.virt = ioremap(res->start, info->map.size);
dev_dbg(&pdev->dev, "virt %p, %lu bytes\n", info->map.virt, info->map.size);
if (info->map.virt == NULL) {
dev_err(&pdev->dev, "failed to ioremap() region\n");
err = -EIO;
goto exit_free;
}
simple_map_init(&info->map);
dev_dbg(&pdev->dev, "initialised map, probing for mtd\n");
/* probe for the right mtd map driver
* supplied by the platform_data struct */
if (pdata->map_probes) {
const char * const *map_probes = pdata->map_probes;
for ( ; !info->mtd && *map_probes; map_probes++)
info->mtd = do_map_probe(*map_probes , &info->map);
}
/* fallback to map_ram */
else
info->mtd = do_map_probe("map_ram", &info->map);
if (info->mtd == NULL) {
dev_err(&pdev->dev, "failed to probe for map_ram\n");
err = -ENOMEM;
goto exit_free;
}
info->mtd->owner = THIS_MODULE;
info->mtd->dev.parent = &pdev->dev;
platram_setrw(info, PLATRAM_RW);
/* check to see if there are any available partitions, or whether
* to add this device whole */
err = mtd_device_parse_register(info->mtd, pdata->probes, NULL,
pdata->partitions,
pdata->nr_partitions);
if (!err)
dev_info(&pdev->dev, "registered mtd device\n");
if (pdata->nr_partitions) {
/* add the whole device. */
err = mtd_device_register(info->mtd, NULL, 0);
if (err) {
dev_err(&pdev->dev,
"failed to register the entire device\n");
}
}
return err;
exit_free:
platram_remove(pdev);
exit_error:
return err;
}
/* device driver info */
/* work with hotplug and coldplug */
MODULE_ALIAS("platform:mtd-ram");
static struct platform_driver platram_driver = {
.probe = platram_probe,
.remove = platram_remove,
.driver = {
.name = "mtd-ram",
.owner = THIS_MODULE,
},
};
module_platform_driver(platram_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_DESCRIPTION("MTD platform RAM map driver");

View file

@ -0,0 +1,229 @@
/*
* Mapping of a custom board with both AMD CFI and JEDEC flash in partitions.
* Config with both CFI and JEDEC device support.
*
* Basically physmap.c with the addition of partitions and
* an array of mapping info to accommodate more than one flash type per board.
*
* Copyright 2005-2007 PMC-Sierra, Inc.
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN
* NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
* INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
* NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
* USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
* ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
* THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*
* You should have received a copy of the GNU General Public License along
* with this program; if not, write to the Free Software Foundation, Inc.,
* 675 Mass Ave, Cambridge, MA 02139, USA.
*/
#include <linux/slab.h>
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <msp_prom.h>
#include <msp_regs.h>
static struct mtd_info **msp_flash;
static struct mtd_partition **msp_parts;
static struct map_info *msp_maps;
static int fcnt;
#define DEBUG_MARKER printk(KERN_NOTICE "%s[%d]\n", __func__, __LINE__)
static int __init init_msp_flash(void)
{
int i, j, ret = -ENOMEM;
int offset, coff;
char *env;
int pcnt;
char flash_name[] = "flash0";
char part_name[] = "flash0_0";
unsigned addr, size;
/* If ELB is disabled by "ful-mux" mode, we can't get at flash */
if ((*DEV_ID_REG & DEV_ID_SINGLE_PC) &&
(*ELB_1PC_EN_REG & SINGLE_PCCARD)) {
printk(KERN_NOTICE "Single PC Card mode: no flash access\n");
return -ENXIO;
}
/* examine the prom environment for flash devices */
for (fcnt = 0; (env = prom_getenv(flash_name)); fcnt++)
flash_name[5] = '0' + fcnt + 1;
if (fcnt < 1)
return -ENXIO;
printk(KERN_NOTICE "Found %d PMC flash devices\n", fcnt);
msp_flash = kmalloc(fcnt * sizeof(struct map_info *), GFP_KERNEL);
if (!msp_flash)
return -ENOMEM;
msp_parts = kmalloc(fcnt * sizeof(struct mtd_partition *), GFP_KERNEL);
if (!msp_parts)
goto free_msp_flash;
msp_maps = kcalloc(fcnt, sizeof(struct mtd_info), GFP_KERNEL);
if (!msp_maps)
goto free_msp_parts;
/* loop over the flash devices, initializing each */
for (i = 0; i < fcnt; i++) {
/* examine the prom environment for flash partititions */
part_name[5] = '0' + i;
part_name[7] = '0';
for (pcnt = 0; (env = prom_getenv(part_name)); pcnt++)
part_name[7] = '0' + pcnt + 1;
if (pcnt == 0) {
printk(KERN_NOTICE "Skipping flash device %d "
"(no partitions defined)\n", i);
continue;
}
msp_parts[i] = kcalloc(pcnt, sizeof(struct mtd_partition),
GFP_KERNEL);
if (!msp_parts[i])
goto cleanup_loop;
/* now initialize the devices proper */
flash_name[5] = '0' + i;
env = prom_getenv(flash_name);
if (sscanf(env, "%x:%x", &addr, &size) < 2) {
ret = -ENXIO;
kfree(msp_parts[i]);
goto cleanup_loop;
}
addr = CPHYSADDR(addr);
printk(KERN_NOTICE
"MSP flash device \"%s\": 0x%08x at 0x%08x\n",
flash_name, size, addr);
/* This must matchs the actual size of the flash chip */
msp_maps[i].size = size;
msp_maps[i].phys = addr;
/*
* Platforms have a specific limit of the size of memory
* which may be mapped for flash:
*/
if (size > CONFIG_MSP_FLASH_MAP_LIMIT)
size = CONFIG_MSP_FLASH_MAP_LIMIT;
msp_maps[i].virt = ioremap(addr, size);
if (msp_maps[i].virt == NULL) {
ret = -ENXIO;
kfree(msp_parts[i]);
goto cleanup_loop;
}
msp_maps[i].bankwidth = 1;
msp_maps[i].name = kmalloc(7, GFP_KERNEL);
if (!msp_maps[i].name) {
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
}
msp_maps[i].name = strncpy(msp_maps[i].name, flash_name, 7);
for (j = 0; j < pcnt; j++) {
part_name[5] = '0' + i;
part_name[7] = '0' + j;
env = prom_getenv(part_name);
if (sscanf(env, "%x:%x:%n", &offset, &size,
&coff) < 2) {
ret = -ENXIO;
kfree(msp_maps[i].name);
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
}
msp_parts[i][j].size = size;
msp_parts[i][j].offset = offset;
msp_parts[i][j].name = env + coff;
}
/* now probe and add the device */
simple_map_init(&msp_maps[i]);
msp_flash[i] = do_map_probe("cfi_probe", &msp_maps[i]);
if (msp_flash[i]) {
msp_flash[i]->owner = THIS_MODULE;
mtd_device_register(msp_flash[i], msp_parts[i], pcnt);
} else {
printk(KERN_ERR "map probe failed for flash\n");
ret = -ENXIO;
kfree(msp_maps[i].name);
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
goto cleanup_loop;
}
}
return 0;
cleanup_loop:
while (i--) {
mtd_device_unregister(msp_flash[i]);
map_destroy(msp_flash[i]);
kfree(msp_maps[i].name);
iounmap(msp_maps[i].virt);
kfree(msp_parts[i]);
}
kfree(msp_maps);
free_msp_parts:
kfree(msp_parts);
free_msp_flash:
kfree(msp_flash);
return ret;
}
static void __exit cleanup_msp_flash(void)
{
int i;
for (i = 0; i < fcnt; i++) {
mtd_device_unregister(msp_flash[i]);
map_destroy(msp_flash[i]);
iounmap((void *)msp_maps[i].virt);
/* free the memory */
kfree(msp_maps[i].name);
kfree(msp_parts[i]);
}
kfree(msp_flash);
kfree(msp_parts);
kfree(msp_maps);
}
MODULE_AUTHOR("PMC-Sierra, Inc");
MODULE_DESCRIPTION("MTD map driver for PMC-Sierra MSP boards");
MODULE_LICENSE("GPL");
module_init(init_msp_flash);
module_exit(cleanup_msp_flash);

View file

@ -0,0 +1,145 @@
/*
* Map driver for Intel XScale PXA2xx platforms.
*
* Author: Nicolas Pitre
* Copyright: (C) 2001 MontaVista Software Inc.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/slab.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <mach/hardware.h>
#include <asm/mach/flash.h>
#define CACHELINESIZE 32
static void pxa2xx_map_inval_cache(struct map_info *map, unsigned long from,
ssize_t len)
{
unsigned long start = (unsigned long)map->cached + from;
unsigned long end = start + len;
start &= ~(CACHELINESIZE - 1);
while (start < end) {
/* invalidate D cache line */
asm volatile ("mcr p15, 0, %0, c7, c6, 1" : : "r" (start));
start += CACHELINESIZE;
}
}
struct pxa2xx_flash_info {
struct mtd_info *mtd;
struct map_info map;
};
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
static int pxa2xx_flash_probe(struct platform_device *pdev)
{
struct flash_platform_data *flash = dev_get_platdata(&pdev->dev);
struct pxa2xx_flash_info *info;
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
info = kzalloc(sizeof(struct pxa2xx_flash_info), GFP_KERNEL);
if (!info)
return -ENOMEM;
info->map.name = flash->name;
info->map.bankwidth = flash->width;
info->map.phys = res->start;
info->map.size = resource_size(res);
info->map.virt = ioremap(info->map.phys, info->map.size);
if (!info->map.virt) {
printk(KERN_WARNING "Failed to ioremap %s\n",
info->map.name);
return -ENOMEM;
}
info->map.cached =
ioremap_cache(info->map.phys, info->map.size);
if (!info->map.cached)
printk(KERN_WARNING "Failed to ioremap cached %s\n",
info->map.name);
info->map.inval_cache = pxa2xx_map_inval_cache;
simple_map_init(&info->map);
printk(KERN_NOTICE
"Probing %s at physical address 0x%08lx"
" (%d-bit bankwidth)\n",
info->map.name, (unsigned long)info->map.phys,
info->map.bankwidth * 8);
info->mtd = do_map_probe(flash->map_name, &info->map);
if (!info->mtd) {
iounmap((void *)info->map.virt);
if (info->map.cached)
iounmap(info->map.cached);
return -EIO;
}
info->mtd->owner = THIS_MODULE;
mtd_device_parse_register(info->mtd, probes, NULL, flash->parts,
flash->nr_parts);
platform_set_drvdata(pdev, info);
return 0;
}
static int pxa2xx_flash_remove(struct platform_device *dev)
{
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
mtd_device_unregister(info->mtd);
map_destroy(info->mtd);
iounmap(info->map.virt);
if (info->map.cached)
iounmap(info->map.cached);
kfree(info);
return 0;
}
#ifdef CONFIG_PM
static void pxa2xx_flash_shutdown(struct platform_device *dev)
{
struct pxa2xx_flash_info *info = platform_get_drvdata(dev);
if (info && mtd_suspend(info->mtd) == 0)
mtd_resume(info->mtd);
}
#else
#define pxa2xx_flash_shutdown NULL
#endif
static struct platform_driver pxa2xx_flash_driver = {
.driver = {
.name = "pxa2xx-flash",
.owner = THIS_MODULE,
},
.probe = pxa2xx_flash_probe,
.remove = pxa2xx_flash_remove,
.shutdown = pxa2xx_flash_shutdown,
};
module_platform_driver(pxa2xx_flash_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Nicolas Pitre <nico@fluxnic.net>");
MODULE_DESCRIPTION("MTD map driver for Intel XScale PXA2xx");

View file

@ -0,0 +1,138 @@
/*
* rbtx4939-flash (based on physmap.c)
*
* This is a simplified physmap driver with map_init callback function.
*
* 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.
*
* Copyright (C) 2009 Atsushi Nemoto <anemo@mba.ocn.ne.jp>
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/slab.h>
#include <linux/device.h>
#include <linux/platform_device.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/txx9/rbtx4939.h>
struct rbtx4939_flash_info {
struct mtd_info *mtd;
struct map_info map;
};
static int rbtx4939_flash_remove(struct platform_device *dev)
{
struct rbtx4939_flash_info *info;
info = platform_get_drvdata(dev);
if (!info)
return 0;
if (info->mtd) {
mtd_device_unregister(info->mtd);
map_destroy(info->mtd);
}
return 0;
}
static const char * const rom_probe_types[] = {
"cfi_probe", "jedec_probe", NULL };
static int rbtx4939_flash_probe(struct platform_device *dev)
{
struct rbtx4939_flash_data *pdata;
struct rbtx4939_flash_info *info;
struct resource *res;
const char * const *probe_type;
int err = 0;
unsigned long size;
pdata = dev_get_platdata(&dev->dev);
if (!pdata)
return -ENODEV;
res = platform_get_resource(dev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
info = devm_kzalloc(&dev->dev, sizeof(struct rbtx4939_flash_info),
GFP_KERNEL);
if (!info)
return -ENOMEM;
platform_set_drvdata(dev, info);
size = resource_size(res);
pr_notice("rbtx4939 platform flash device: %pR\n", res);
if (!devm_request_mem_region(&dev->dev, res->start, size,
dev_name(&dev->dev)))
return -EBUSY;
info->map.name = dev_name(&dev->dev);
info->map.phys = res->start;
info->map.size = size;
info->map.bankwidth = pdata->width;
info->map.virt = devm_ioremap(&dev->dev, info->map.phys, size);
if (!info->map.virt)
return -EBUSY;
if (pdata->map_init)
(*pdata->map_init)(&info->map);
else
simple_map_init(&info->map);
probe_type = rom_probe_types;
for (; !info->mtd && *probe_type; probe_type++)
info->mtd = do_map_probe(*probe_type, &info->map);
if (!info->mtd) {
dev_err(&dev->dev, "map_probe failed\n");
err = -ENXIO;
goto err_out;
}
info->mtd->owner = THIS_MODULE;
err = mtd_device_parse_register(info->mtd, NULL, NULL, pdata->parts,
pdata->nr_parts);
if (err)
goto err_out;
return 0;
err_out:
rbtx4939_flash_remove(dev);
return err;
}
#ifdef CONFIG_PM
static void rbtx4939_flash_shutdown(struct platform_device *dev)
{
struct rbtx4939_flash_info *info = platform_get_drvdata(dev);
if (mtd_suspend(info->mtd) == 0)
mtd_resume(info->mtd);
}
#else
#define rbtx4939_flash_shutdown NULL
#endif
static struct platform_driver rbtx4939_flash_driver = {
.probe = rbtx4939_flash_probe,
.remove = rbtx4939_flash_remove,
.shutdown = rbtx4939_flash_shutdown,
.driver = {
.name = "rbtx4939-flash",
.owner = THIS_MODULE,
},
};
module_platform_driver(rbtx4939_flash_driver);
MODULE_LICENSE("GPL");
MODULE_DESCRIPTION("RBTX4939 MTD map driver");
MODULE_ALIAS("platform:rbtx4939-flash");

View file

@ -0,0 +1,301 @@
/*
* Flash memory access on SA11x0 based devices
*
* (C) 2000 Nicolas Pitre <nico@fluxnic.net>
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/ioport.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/slab.h>
#include <linux/platform_device.h>
#include <linux/err.h>
#include <linux/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/mtd/concat.h>
#include <mach/hardware.h>
#include <asm/sizes.h>
#include <asm/mach/flash.h>
struct sa_subdev_info {
char name[16];
struct map_info map;
struct mtd_info *mtd;
struct flash_platform_data *plat;
};
struct sa_info {
struct mtd_info *mtd;
int num_subdev;
struct sa_subdev_info subdev[0];
};
static DEFINE_SPINLOCK(sa1100_vpp_lock);
static int sa1100_vpp_refcnt;
static void sa1100_set_vpp(struct map_info *map, int on)
{
struct sa_subdev_info *subdev = container_of(map, struct sa_subdev_info, map);
unsigned long flags;
spin_lock_irqsave(&sa1100_vpp_lock, flags);
if (on) {
if (++sa1100_vpp_refcnt == 1) /* first nested 'on' */
subdev->plat->set_vpp(1);
} else {
if (--sa1100_vpp_refcnt == 0) /* last nested 'off' */
subdev->plat->set_vpp(0);
}
spin_unlock_irqrestore(&sa1100_vpp_lock, flags);
}
static void sa1100_destroy_subdev(struct sa_subdev_info *subdev)
{
if (subdev->mtd)
map_destroy(subdev->mtd);
if (subdev->map.virt)
iounmap(subdev->map.virt);
release_mem_region(subdev->map.phys, subdev->map.size);
}
static int sa1100_probe_subdev(struct sa_subdev_info *subdev, struct resource *res)
{
unsigned long phys;
unsigned int size;
int ret;
phys = res->start;
size = res->end - phys + 1;
/*
* Retrieve the bankwidth from the MSC registers.
* We currently only implement CS0 and CS1 here.
*/
switch (phys) {
default:
printk(KERN_WARNING "SA1100 flash: unknown base address "
"0x%08lx, assuming CS0\n", phys);
case SA1100_CS0_PHYS:
subdev->map.bankwidth = (MSC0 & MSC_RBW) ? 2 : 4;
break;
case SA1100_CS1_PHYS:
subdev->map.bankwidth = ((MSC0 >> 16) & MSC_RBW) ? 2 : 4;
break;
}
if (!request_mem_region(phys, size, subdev->name)) {
ret = -EBUSY;
goto out;
}
if (subdev->plat->set_vpp)
subdev->map.set_vpp = sa1100_set_vpp;
subdev->map.phys = phys;
subdev->map.size = size;
subdev->map.virt = ioremap(phys, size);
if (!subdev->map.virt) {
ret = -ENOMEM;
goto err;
}
simple_map_init(&subdev->map);
/*
* Now let's probe for the actual flash. Do it here since
* specific machine settings might have been set above.
*/
subdev->mtd = do_map_probe(subdev->plat->map_name, &subdev->map);
if (subdev->mtd == NULL) {
ret = -ENXIO;
goto err;
}
subdev->mtd->owner = THIS_MODULE;
printk(KERN_INFO "SA1100 flash: CFI device at 0x%08lx, %uMiB, %d-bit\n",
phys, (unsigned)(subdev->mtd->size >> 20),
subdev->map.bankwidth * 8);
return 0;
err:
sa1100_destroy_subdev(subdev);
out:
return ret;
}
static void sa1100_destroy(struct sa_info *info, struct flash_platform_data *plat)
{
int i;
if (info->mtd) {
mtd_device_unregister(info->mtd);
if (info->mtd != info->subdev[0].mtd)
mtd_concat_destroy(info->mtd);
}
for (i = info->num_subdev - 1; i >= 0; i--)
sa1100_destroy_subdev(&info->subdev[i]);
kfree(info);
if (plat->exit)
plat->exit();
}
static struct sa_info *sa1100_setup_mtd(struct platform_device *pdev,
struct flash_platform_data *plat)
{
struct sa_info *info;
int nr, size, i, ret = 0;
/*
* Count number of devices.
*/
for (nr = 0; ; nr++)
if (!platform_get_resource(pdev, IORESOURCE_MEM, nr))
break;
if (nr == 0) {
ret = -ENODEV;
goto out;
}
size = sizeof(struct sa_info) + sizeof(struct sa_subdev_info) * nr;
/*
* Allocate the map_info structs in one go.
*/
info = kzalloc(size, GFP_KERNEL);
if (!info) {
ret = -ENOMEM;
goto out;
}
if (plat->init) {
ret = plat->init();
if (ret)
goto err;
}
/*
* Claim and then map the memory regions.
*/
for (i = 0; i < nr; i++) {
struct sa_subdev_info *subdev = &info->subdev[i];
struct resource *res;
res = platform_get_resource(pdev, IORESOURCE_MEM, i);
if (!res)
break;
subdev->map.name = subdev->name;
sprintf(subdev->name, "%s-%d", plat->name, i);
subdev->plat = plat;
ret = sa1100_probe_subdev(subdev, res);
if (ret)
break;
}
info->num_subdev = i;
/*
* ENXIO is special. It means we didn't find a chip when we probed.
*/
if (ret != 0 && !(ret == -ENXIO && info->num_subdev > 0))
goto err;
/*
* 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. Either way, it'll be called "sa1100".
*/
if (info->num_subdev == 1) {
strcpy(info->subdev[0].name, plat->name);
info->mtd = info->subdev[0].mtd;
ret = 0;
} else if (info->num_subdev > 1) {
struct mtd_info *cdev[nr];
/*
* We detected multiple devices. Concatenate them together.
*/
for (i = 0; i < info->num_subdev; i++)
cdev[i] = info->subdev[i].mtd;
info->mtd = mtd_concat_create(cdev, info->num_subdev,
plat->name);
if (info->mtd == NULL)
ret = -ENXIO;
}
if (ret == 0)
return info;
err:
sa1100_destroy(info, plat);
out:
return ERR_PTR(ret);
}
static const char * const part_probes[] = { "cmdlinepart", "RedBoot", NULL };
static int sa1100_mtd_probe(struct platform_device *pdev)
{
struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
struct sa_info *info;
int err;
if (!plat)
return -ENODEV;
info = sa1100_setup_mtd(pdev, plat);
if (IS_ERR(info)) {
err = PTR_ERR(info);
goto out;
}
/*
* Partition selection stuff.
*/
mtd_device_parse_register(info->mtd, part_probes, NULL, plat->parts,
plat->nr_parts);
platform_set_drvdata(pdev, info);
err = 0;
out:
return err;
}
static int __exit sa1100_mtd_remove(struct platform_device *pdev)
{
struct sa_info *info = platform_get_drvdata(pdev);
struct flash_platform_data *plat = dev_get_platdata(&pdev->dev);
sa1100_destroy(info, plat);
return 0;
}
static struct platform_driver sa1100_mtd_driver = {
.probe = sa1100_mtd_probe,
.remove = __exit_p(sa1100_mtd_remove),
.driver = {
.name = "sa1100-mtd",
.owner = THIS_MODULE,
},
};
module_platform_driver(sa1100_mtd_driver);
MODULE_AUTHOR("Nicolas Pitre");
MODULE_DESCRIPTION("SA1100 CFI map driver");
MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:sa1100-mtd");

236
drivers/mtd/maps/sbc_gxx.c Normal file
View file

@ -0,0 +1,236 @@
/* sbc_gxx.c -- MTD map driver for Arcom Control Systems SBC-MediaGX,
SBC-GXm and SBC-GX1 series boards.
Copyright (C) 2001 Arcom Control System Ltd
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
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
The SBC-MediaGX / SBC-GXx has up to 16 MiB of
Intel StrataFlash (28F320/28F640) in x8 mode.
This driver uses the CFI probe and Intel Extended Command Set drivers.
The flash is accessed as follows:
16 KiB memory window at 0xdc000-0xdffff
Two IO address locations for paging
0x258
bit 0-7: address bit 14-21
0x259
bit 0-1: address bit 22-23
bit 7: 0 - reset/powered down
1 - device enabled
The single flash device is divided into 3 partition which appear as
separate MTD devices.
25/04/2001 AJL (Arcom) Modified signon strings and partition sizes
(to support bzImages up to 638KiB-ish)
*/
// Includes
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
// Defines
// - Hardware specific
#define WINDOW_START 0xdc000
/* Number of bits in offset. */
#define WINDOW_SHIFT 14
#define WINDOW_LENGTH (1 << WINDOW_SHIFT)
/* The bits for the offset into the window. */
#define WINDOW_MASK (WINDOW_LENGTH-1)
#define PAGE_IO 0x258
#define PAGE_IO_SIZE 2
/* bit 7 of 0x259 must be 1 to enable device. */
#define DEVICE_ENABLE 0x8000
// - Flash / Partition sizing
#define MAX_SIZE_KiB 16384
#define BOOT_PARTITION_SIZE_KiB 768
#define DATA_PARTITION_SIZE_KiB 1280
#define APP_PARTITION_SIZE_KiB 6144
// Globals
static volatile int page_in_window = -1; // Current page in window.
static void __iomem *iomapadr;
static DEFINE_SPINLOCK(sbc_gxx_spin);
/* partition_info gives details on the logical partitions that the split the
* single flash device into. If the size if zero we use up to the end of the
* device. */
static struct mtd_partition partition_info[]={
{ .name = "SBC-GXx flash boot partition",
.offset = 0,
.size = BOOT_PARTITION_SIZE_KiB*1024 },
{ .name = "SBC-GXx flash data partition",
.offset = BOOT_PARTITION_SIZE_KiB*1024,
.size = (DATA_PARTITION_SIZE_KiB)*1024 },
{ .name = "SBC-GXx flash application partition",
.offset = (BOOT_PARTITION_SIZE_KiB+DATA_PARTITION_SIZE_KiB)*1024 }
};
#define NUM_PARTITIONS 3
static inline void sbc_gxx_page(struct map_info *map, unsigned long ofs)
{
unsigned long page = ofs >> WINDOW_SHIFT;
if( page!=page_in_window ) {
outw( page | DEVICE_ENABLE, PAGE_IO );
page_in_window = page;
}
}
static map_word sbc_gxx_read8(struct map_info *map, unsigned long ofs)
{
map_word ret;
spin_lock(&sbc_gxx_spin);
sbc_gxx_page(map, ofs);
ret.x[0] = readb(iomapadr + (ofs & WINDOW_MASK));
spin_unlock(&sbc_gxx_spin);
return ret;
}
static void sbc_gxx_copy_from(struct map_info *map, void *to, unsigned long from, ssize_t len)
{
while(len) {
unsigned long thislen = len;
if (len > (WINDOW_LENGTH - (from & WINDOW_MASK)))
thislen = WINDOW_LENGTH-(from & WINDOW_MASK);
spin_lock(&sbc_gxx_spin);
sbc_gxx_page(map, from);
memcpy_fromio(to, iomapadr + (from & WINDOW_MASK), thislen);
spin_unlock(&sbc_gxx_spin);
to += thislen;
from += thislen;
len -= thislen;
}
}
static void sbc_gxx_write8(struct map_info *map, map_word d, unsigned long adr)
{
spin_lock(&sbc_gxx_spin);
sbc_gxx_page(map, adr);
writeb(d.x[0], iomapadr + (adr & WINDOW_MASK));
spin_unlock(&sbc_gxx_spin);
}
static void sbc_gxx_copy_to(struct map_info *map, unsigned long to, const void *from, ssize_t len)
{
while(len) {
unsigned long thislen = len;
if (len > (WINDOW_LENGTH - (to & WINDOW_MASK)))
thislen = WINDOW_LENGTH-(to & WINDOW_MASK);
spin_lock(&sbc_gxx_spin);
sbc_gxx_page(map, to);
memcpy_toio(iomapadr + (to & WINDOW_MASK), from, thislen);
spin_unlock(&sbc_gxx_spin);
to += thislen;
from += thislen;
len -= thislen;
}
}
static struct map_info sbc_gxx_map = {
.name = "SBC-GXx flash",
.phys = NO_XIP,
.size = MAX_SIZE_KiB*1024, /* this must be set to a maximum possible amount
of flash so the cfi probe routines find all
the chips */
.bankwidth = 1,
.read = sbc_gxx_read8,
.copy_from = sbc_gxx_copy_from,
.write = sbc_gxx_write8,
.copy_to = sbc_gxx_copy_to
};
/* MTD device for all of the flash. */
static struct mtd_info *all_mtd;
static void cleanup_sbc_gxx(void)
{
if( all_mtd ) {
mtd_device_unregister(all_mtd);
map_destroy( all_mtd );
}
iounmap(iomapadr);
release_region(PAGE_IO,PAGE_IO_SIZE);
}
static int __init init_sbc_gxx(void)
{
iomapadr = ioremap(WINDOW_START, WINDOW_LENGTH);
if (!iomapadr) {
printk( KERN_ERR"%s: failed to ioremap memory region\n",
sbc_gxx_map.name );
return -EIO;
}
if (!request_region( PAGE_IO, PAGE_IO_SIZE, "SBC-GXx flash")) {
printk( KERN_ERR"%s: IO ports 0x%x-0x%x in use\n",
sbc_gxx_map.name,
PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1 );
iounmap(iomapadr);
return -EAGAIN;
}
printk( KERN_INFO"%s: IO:0x%x-0x%x MEM:0x%x-0x%x\n",
sbc_gxx_map.name,
PAGE_IO, PAGE_IO+PAGE_IO_SIZE-1,
WINDOW_START, WINDOW_START+WINDOW_LENGTH-1 );
/* Probe for chip. */
all_mtd = do_map_probe( "cfi_probe", &sbc_gxx_map );
if( !all_mtd ) {
cleanup_sbc_gxx();
return -ENXIO;
}
all_mtd->owner = THIS_MODULE;
/* Create MTD devices for each partition. */
mtd_device_register(all_mtd, partition_info, NUM_PARTITIONS);
return 0;
}
module_init(init_sbc_gxx);
module_exit(cleanup_sbc_gxx);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Arcom Control Systems Ltd.");
MODULE_DESCRIPTION("MTD map driver for SBC-GXm and SBC-GX1 series boards");

302
drivers/mtd/maps/sc520cdp.c Normal file
View file

@ -0,0 +1,302 @@
/* sc520cdp.c -- MTD map driver for AMD SC520 Customer Development Platform
*
* Copyright (C) 2001 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 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
*
*
* The SC520CDP is an evaluation board for the Elan SC520 processor available
* from AMD. It has two banks of 32-bit Flash ROM, each 8 Megabytes in size,
* and up to 512 KiB of 8-bit DIL Flash ROM.
* For details see http://www.amd.com/products/epd/desiging/evalboards/18.elansc520/520_cdp_brief/index.html
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/concat.h>
/*
** The Embedded Systems BIOS decodes the first FLASH starting at
** 0x8400000. This is a *terrible* place for it because accessing
** the flash at this location causes the A22 address line to be high
** (that's what 0x8400000 binary's ought to be). But this is the highest
** order address line on the raw flash devices themselves!!
** This causes the top HALF of the flash to be accessed first. Beyond
** the physical limits of the flash, the flash chip aliases over (to
** 0x880000 which causes the bottom half to be accessed. This splits the
** flash into two and inverts it! If you then try to access this from another
** program that does NOT do this insanity, then you *will* access the
** first half of the flash, but not find what you expect there. That
** stuff is in the *second* half! Similarly, the address used by the
** BIOS for the second FLASH bank is also quite a bad choice.
** If REPROGRAM_PAR is defined below (the default), then this driver will
** choose more useful addresses for the FLASH banks by reprogramming the
** responsible PARxx registers in the SC520's MMCR region. This will
** cause the settings to be incompatible with the BIOS's settings, which
** shouldn't be a problem since you are running Linux, (i.e. the BIOS is
** not much use anyway). However, if you need to be compatible with
** the BIOS for some reason, just undefine REPROGRAM_PAR.
*/
#define REPROGRAM_PAR
#ifdef REPROGRAM_PAR
/* These are the addresses we want.. */
#define WINDOW_ADDR_0 0x08800000
#define WINDOW_ADDR_1 0x09000000
#define WINDOW_ADDR_2 0x09800000
/* .. and these are the addresses the BIOS gives us */
#define WINDOW_ADDR_0_BIOS 0x08400000
#define WINDOW_ADDR_1_BIOS 0x08c00000
#define WINDOW_ADDR_2_BIOS 0x09400000
#else
#define WINDOW_ADDR_0 0x08400000
#define WINDOW_ADDR_1 0x08C00000
#define WINDOW_ADDR_2 0x09400000
#endif
#define WINDOW_SIZE_0 0x00800000
#define WINDOW_SIZE_1 0x00800000
#define WINDOW_SIZE_2 0x00080000
static struct map_info sc520cdp_map[] = {
{
.name = "SC520CDP Flash Bank #0",
.size = WINDOW_SIZE_0,
.bankwidth = 4,
.phys = WINDOW_ADDR_0
},
{
.name = "SC520CDP Flash Bank #1",
.size = WINDOW_SIZE_1,
.bankwidth = 4,
.phys = WINDOW_ADDR_1
},
{
.name = "SC520CDP DIL Flash",
.size = WINDOW_SIZE_2,
.bankwidth = 1,
.phys = WINDOW_ADDR_2
},
};
#define NUM_FLASH_BANKS ARRAY_SIZE(sc520cdp_map)
static struct mtd_info *mymtd[NUM_FLASH_BANKS];
static struct mtd_info *merged_mtd;
#ifdef REPROGRAM_PAR
/*
** The SC520 MMCR (memory mapped control register) region resides
** at 0xFFFEF000. The 16 Programmable Address Region (PAR) registers
** are at offset 0x88 in the MMCR:
*/
#define SC520_MMCR_BASE 0xFFFEF000
#define SC520_MMCR_EXTENT 0x1000
#define SC520_PAR(x) ((0x88/sizeof(unsigned long)) + (x))
#define NUM_SC520_PAR 16 /* total number of PAR registers */
/*
** The highest three bits in a PAR register determine what target
** device is controlled by this PAR. Here, only ROMCS? and BOOTCS
** devices are of interest.
*/
#define SC520_PAR_BOOTCS (0x4<<29)
#define SC520_PAR_ROMCS0 (0x5<<29)
#define SC520_PAR_ROMCS1 (0x6<<29)
#define SC520_PAR_TRGDEV (0x7<<29)
/*
** Bits 28 thru 26 determine some attributes for the
** region controlled by the PAR. (We only use non-cacheable)
*/
#define SC520_PAR_WRPROT (1<<26) /* write protected */
#define SC520_PAR_NOCACHE (1<<27) /* non-cacheable */
#define SC520_PAR_NOEXEC (1<<28) /* code execution denied */
/*
** Bit 25 determines the granularity: 4K or 64K
*/
#define SC520_PAR_PG_SIZ4 (0<<25)
#define SC520_PAR_PG_SIZ64 (1<<25)
/*
** Build a value to be written into a PAR register.
** We only need ROM entries, 64K page size:
*/
#define SC520_PAR_ENTRY(trgdev, address, size) \
((trgdev) | SC520_PAR_NOCACHE | SC520_PAR_PG_SIZ64 | \
(address) >> 16 | (((size) >> 16) - 1) << 14)
struct sc520_par_table
{
unsigned long trgdev;
unsigned long new_par;
unsigned long default_address;
};
static const struct sc520_par_table par_table[NUM_FLASH_BANKS] =
{
{ /* Flash Bank #0: selected by ROMCS0 */
SC520_PAR_ROMCS0,
SC520_PAR_ENTRY(SC520_PAR_ROMCS0, WINDOW_ADDR_0, WINDOW_SIZE_0),
WINDOW_ADDR_0_BIOS
},
{ /* Flash Bank #1: selected by ROMCS1 */
SC520_PAR_ROMCS1,
SC520_PAR_ENTRY(SC520_PAR_ROMCS1, WINDOW_ADDR_1, WINDOW_SIZE_1),
WINDOW_ADDR_1_BIOS
},
{ /* DIL (BIOS) Flash: selected by BOOTCS */
SC520_PAR_BOOTCS,
SC520_PAR_ENTRY(SC520_PAR_BOOTCS, WINDOW_ADDR_2, WINDOW_SIZE_2),
WINDOW_ADDR_2_BIOS
}
};
static void sc520cdp_setup_par(void)
{
unsigned long __iomem *mmcr;
unsigned long mmcr_val;
int i, j;
/* map in SC520's MMCR area */
mmcr = ioremap_nocache(SC520_MMCR_BASE, SC520_MMCR_EXTENT);
if(!mmcr) { /* ioremap_nocache failed: skip the PAR reprogramming */
/* force physical address fields to BIOS defaults: */
for(i = 0; i < NUM_FLASH_BANKS; i++)
sc520cdp_map[i].phys = par_table[i].default_address;
return;
}
/*
** Find the PARxx registers that are responsible for activating
** ROMCS0, ROMCS1 and BOOTCS. Reprogram each of these with a
** new value from the table.
*/
for(i = 0; i < NUM_FLASH_BANKS; i++) { /* for each par_table entry */
for(j = 0; j < NUM_SC520_PAR; j++) { /* for each PAR register */
mmcr_val = readl(&mmcr[SC520_PAR(j)]);
/* if target device field matches, reprogram the PAR */
if((mmcr_val & SC520_PAR_TRGDEV) == par_table[i].trgdev)
{
writel(par_table[i].new_par, &mmcr[SC520_PAR(j)]);
break;
}
}
if(j == NUM_SC520_PAR)
{ /* no matching PAR found: try default BIOS address */
printk(KERN_NOTICE "Could not find PAR responsible for %s\n",
sc520cdp_map[i].name);
printk(KERN_NOTICE "Trying default address 0x%lx\n",
par_table[i].default_address);
sc520cdp_map[i].phys = par_table[i].default_address;
}
}
iounmap(mmcr);
}
#endif
static int __init init_sc520cdp(void)
{
int i, devices_found = 0;
#ifdef REPROGRAM_PAR
/* reprogram PAR registers so flash appears at the desired addresses */
sc520cdp_setup_par();
#endif
for (i = 0; i < NUM_FLASH_BANKS; i++) {
printk(KERN_NOTICE "SC520 CDP flash device: 0x%Lx at 0x%Lx\n",
(unsigned long long)sc520cdp_map[i].size,
(unsigned long long)sc520cdp_map[i].phys);
sc520cdp_map[i].virt = ioremap_nocache(sc520cdp_map[i].phys, sc520cdp_map[i].size);
if (!sc520cdp_map[i].virt) {
printk("Failed to ioremap_nocache\n");
return -EIO;
}
simple_map_init(&sc520cdp_map[i]);
mymtd[i] = do_map_probe("cfi_probe", &sc520cdp_map[i]);
if(!mymtd[i])
mymtd[i] = do_map_probe("jedec_probe", &sc520cdp_map[i]);
if(!mymtd[i])
mymtd[i] = do_map_probe("map_rom", &sc520cdp_map[i]);
if (mymtd[i]) {
mymtd[i]->owner = THIS_MODULE;
++devices_found;
}
else {
iounmap(sc520cdp_map[i].virt);
}
}
if(devices_found >= 2) {
/* Combine the two flash banks into a single MTD device & register it: */
merged_mtd = mtd_concat_create(mymtd, 2, "SC520CDP Flash Banks #0 and #1");
if(merged_mtd)
mtd_device_register(merged_mtd, NULL, 0);
}
if(devices_found == 3) /* register the third (DIL-Flash) device */
mtd_device_register(mymtd[2], NULL, 0);
return(devices_found ? 0 : -ENXIO);
}
static void __exit cleanup_sc520cdp(void)
{
int i;
if (merged_mtd) {
mtd_device_unregister(merged_mtd);
mtd_concat_destroy(merged_mtd);
}
if (mymtd[2])
mtd_device_unregister(mymtd[2]);
for (i = 0; i < NUM_FLASH_BANKS; i++) {
if (mymtd[i])
map_destroy(mymtd[i]);
if (sc520cdp_map[i].virt) {
iounmap(sc520cdp_map[i].virt);
sc520cdp_map[i].virt = NULL;
}
}
}
module_init(init_sc520cdp);
module_exit(cleanup_sc520cdp);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sysgo Real-Time Solutions GmbH");
MODULE_DESCRIPTION("MTD map driver for AMD SC520 Customer Development Platform");

View file

@ -0,0 +1,238 @@
/*
* MTD map driver for BIOS Flash on Intel SCB2 boards
* Copyright (C) 2002 Sun Microsystems, Inc.
* Tim Hockin <thockin@sun.com>
*
* A few notes on this MTD map:
*
* This was developed with a small number of SCB2 boards to test on.
* Hopefully, Intel has not introducted too many unaccounted variables in the
* making of this board.
*
* The BIOS marks its own memory region as 'reserved' in the e820 map. We
* try to request it here, but if it fails, we carry on anyway.
*
* This is how the chip is attached, so said the schematic:
* * a 4 MiB (32 Mib) 16 bit chip
* * a 1 MiB memory region
* * A20 and A21 pulled up
* * D8-D15 ignored
* What this means is that, while we are addressing bytes linearly, we are
* really addressing words, and discarding the other byte. This means that
* the chip MUST BE at least 2 MiB. This also means that every block is
* actually half as big as the chip reports. It also means that accesses of
* logical address 0 hit higher-address sections of the chip, not physical 0.
* One can only hope that these 4MiB x16 chips were a lot cheaper than 1MiB x8
* chips.
*
* This driver assumes the chip is not write-protected by an external signal.
* As of the this writing, that is true, but may change, just to spite me.
*
* The actual BIOS layout has been mostly reverse engineered. Intel BIOS
* updates for this board include 10 related (*.bio - &.bi9) binary files and
* another separate (*.bbo) binary file. The 10 files are 64k of data + a
* small header. If the headers are stripped off, the 10 64k files can be
* concatenated into a 640k image. This is your BIOS image, proper. The
* separate .bbo file also has a small header. It is the 'Boot Block'
* recovery BIOS. Once the header is stripped, no further prep is needed.
* As best I can tell, the BIOS is arranged as such:
* offset 0x00000 to 0x4ffff (320k): unknown - SCSI BIOS, etc?
* offset 0x50000 to 0xeffff (640k): BIOS proper
* offset 0xf0000 ty 0xfffff (64k): Boot Block region
*
* Intel's BIOS update program flashes the BIOS and Boot Block in separate
* steps. Probably a wise thing to do.
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/cfi.h>
#include <linux/pci.h>
#include <linux/pci_ids.h>
#define MODNAME "scb2_flash"
#define SCB2_ADDR 0xfff00000
#define SCB2_WINDOW 0x00100000
static void __iomem *scb2_ioaddr;
static struct mtd_info *scb2_mtd;
static struct map_info scb2_map = {
.name = "SCB2 BIOS Flash",
.size = 0,
.bankwidth = 1,
};
static int region_fail;
static int scb2_fixup_mtd(struct mtd_info *mtd)
{
int i;
int done = 0;
struct map_info *map = mtd->priv;
struct cfi_private *cfi = map->fldrv_priv;
/* barf if this doesn't look right */
if (cfi->cfiq->InterfaceDesc != CFI_INTERFACE_X16_ASYNC) {
printk(KERN_ERR MODNAME ": unsupported InterfaceDesc: %#x\n",
cfi->cfiq->InterfaceDesc);
return -1;
}
/* I wasn't here. I didn't see. dwmw2. */
/* the chip is sometimes bigger than the map - what a waste */
mtd->size = map->size;
/*
* We only REALLY get half the chip, due to the way it is
* wired up - D8-D15 are tossed away. We read linear bytes,
* but in reality we are getting 1/2 of each 16-bit read,
* which LOOKS linear to us. Because CFI code accounts for
* things like lock/unlock/erase by eraseregions, we need to
* fudge them to reflect this. Erases go like this:
* * send an erase to an address
* * the chip samples the address and erases the block
* * add the block erasesize to the address and repeat
* -- the problem is that addresses are 16-bit addressable
* -- we end up erasing every-other block
*/
mtd->erasesize /= 2;
for (i = 0; i < mtd->numeraseregions; i++) {
struct mtd_erase_region_info *region = &mtd->eraseregions[i];
region->erasesize /= 2;
}
/*
* If the chip is bigger than the map, it is wired with the high
* address lines pulled up. This makes us access the top portion of
* the chip, so all our erase-region info is wrong. Start cutting from
* the bottom.
*/
for (i = 0; !done && i < mtd->numeraseregions; i++) {
struct mtd_erase_region_info *region = &mtd->eraseregions[i];
if (region->numblocks * region->erasesize > mtd->size) {
region->numblocks = ((unsigned long)mtd->size /
region->erasesize);
done = 1;
} else {
region->numblocks = 0;
}
region->offset = 0;
}
return 0;
}
/* CSB5's 'Function Control Register' has bits for decoding @ >= 0xffc00000 */
#define CSB5_FCR 0x41
#define CSB5_FCR_DECODE_ALL 0x0e
static int scb2_flash_probe(struct pci_dev *dev,
const struct pci_device_id *ent)
{
u8 reg;
/* enable decoding of the flash region in the south bridge */
pci_read_config_byte(dev, CSB5_FCR, &reg);
pci_write_config_byte(dev, CSB5_FCR, reg | CSB5_FCR_DECODE_ALL);
if (!request_mem_region(SCB2_ADDR, SCB2_WINDOW, scb2_map.name)) {
/*
* The BIOS seems to mark the flash region as 'reserved'
* in the e820 map. Warn and go about our business.
*/
printk(KERN_WARNING MODNAME
": warning - can't reserve rom window, continuing\n");
region_fail = 1;
}
/* remap the IO window (w/o caching) */
scb2_ioaddr = ioremap_nocache(SCB2_ADDR, SCB2_WINDOW);
if (!scb2_ioaddr) {
printk(KERN_ERR MODNAME ": Failed to ioremap window!\n");
if (!region_fail)
release_mem_region(SCB2_ADDR, SCB2_WINDOW);
return -ENOMEM;
}
scb2_map.phys = SCB2_ADDR;
scb2_map.virt = scb2_ioaddr;
scb2_map.size = SCB2_WINDOW;
simple_map_init(&scb2_map);
/* try to find a chip */
scb2_mtd = do_map_probe("cfi_probe", &scb2_map);
if (!scb2_mtd) {
printk(KERN_ERR MODNAME ": flash probe failed!\n");
iounmap(scb2_ioaddr);
if (!region_fail)
release_mem_region(SCB2_ADDR, SCB2_WINDOW);
return -ENODEV;
}
scb2_mtd->owner = THIS_MODULE;
if (scb2_fixup_mtd(scb2_mtd) < 0) {
mtd_device_unregister(scb2_mtd);
map_destroy(scb2_mtd);
iounmap(scb2_ioaddr);
if (!region_fail)
release_mem_region(SCB2_ADDR, SCB2_WINDOW);
return -ENODEV;
}
printk(KERN_NOTICE MODNAME ": chip size 0x%llx at offset 0x%llx\n",
(unsigned long long)scb2_mtd->size,
(unsigned long long)(SCB2_WINDOW - scb2_mtd->size));
mtd_device_register(scb2_mtd, NULL, 0);
return 0;
}
static void scb2_flash_remove(struct pci_dev *dev)
{
if (!scb2_mtd)
return;
/* disable flash writes */
mtd_lock(scb2_mtd, 0, scb2_mtd->size);
mtd_device_unregister(scb2_mtd);
map_destroy(scb2_mtd);
iounmap(scb2_ioaddr);
scb2_ioaddr = NULL;
if (!region_fail)
release_mem_region(SCB2_ADDR, SCB2_WINDOW);
}
static struct pci_device_id scb2_flash_pci_ids[] = {
{
.vendor = PCI_VENDOR_ID_SERVERWORKS,
.device = PCI_DEVICE_ID_SERVERWORKS_CSB5,
.subvendor = PCI_ANY_ID,
.subdevice = PCI_ANY_ID
},
{ 0, }
};
static struct pci_driver scb2_flash_driver = {
.name = "Intel SCB2 BIOS Flash",
.id_table = scb2_flash_pci_ids,
.probe = scb2_flash_probe,
.remove = scb2_flash_remove,
};
module_pci_driver(scb2_flash_driver);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Tim Hockin <thockin@sun.com>");
MODULE_DESCRIPTION("MTD map driver for Intel SCB2 BIOS Flash");
MODULE_DEVICE_TABLE(pci, scb2_flash_pci_ids);

View file

@ -0,0 +1,225 @@
/* linux/drivers/mtd/maps/scx200_docflash.c
Copyright (c) 2001,2002 Christer Weinigel <wingel@nano-system.com>
National Semiconductor SCx200 flash mapped with DOCCS
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/pci.h>
#include <linux/scx200.h>
#define NAME "scx200_docflash"
MODULE_AUTHOR("Christer Weinigel <wingel@hack.org>");
MODULE_DESCRIPTION("NatSemi SCx200 DOCCS Flash Driver");
MODULE_LICENSE("GPL");
static int probe = 0; /* Don't autoprobe */
static unsigned size = 0x1000000; /* 16 MiB the whole ISA address space */
static unsigned width = 8; /* Default to 8 bits wide */
static char *flashtype = "cfi_probe";
module_param(probe, int, 0);
MODULE_PARM_DESC(probe, "Probe for a BIOS mapping");
module_param(size, int, 0);
MODULE_PARM_DESC(size, "Size of the flash mapping");
module_param(width, int, 0);
MODULE_PARM_DESC(width, "Data width of the flash mapping (8/16)");
module_param(flashtype, charp, 0);
MODULE_PARM_DESC(flashtype, "Type of MTD probe to do");
static struct resource docmem = {
.flags = IORESOURCE_MEM,
.name = "NatSemi SCx200 DOCCS Flash",
};
static struct mtd_info *mymtd;
static struct mtd_partition partition_info[] = {
{
.name = "DOCCS Boot kernel",
.offset = 0,
.size = 0xc0000
},
{
.name = "DOCCS Low BIOS",
.offset = 0xc0000,
.size = 0x40000
},
{
.name = "DOCCS File system",
.offset = 0x100000,
.size = ~0 /* calculate from flash size */
},
{
.name = "DOCCS High BIOS",
.offset = ~0, /* calculate from flash size */
.size = 0x80000
},
};
#define NUM_PARTITIONS ARRAY_SIZE(partition_info)
static struct map_info scx200_docflash_map = {
.name = "NatSemi SCx200 DOCCS Flash",
};
static int __init init_scx200_docflash(void)
{
unsigned u;
unsigned base;
unsigned ctrl;
unsigned pmr;
struct pci_dev *bridge;
printk(KERN_DEBUG NAME ": NatSemi SCx200 DOCCS Flash Driver\n");
if ((bridge = pci_get_device(PCI_VENDOR_ID_NS,
PCI_DEVICE_ID_NS_SCx200_BRIDGE,
NULL)) == NULL)
return -ENODEV;
/* check that we have found the configuration block */
if (!scx200_cb_present()) {
pci_dev_put(bridge);
return -ENODEV;
}
if (probe) {
/* Try to use the present flash mapping if any */
pci_read_config_dword(bridge, SCx200_DOCCS_BASE, &base);
pci_read_config_dword(bridge, SCx200_DOCCS_CTRL, &ctrl);
pci_dev_put(bridge);
pmr = inl(scx200_cb_base + SCx200_PMR);
if (base == 0
|| (ctrl & 0x07000000) != 0x07000000
|| (ctrl & 0x0007ffff) == 0)
return -ENODEV;
size = ((ctrl&0x1fff)<<13) + (1<<13);
for (u = size; u > 1; u >>= 1)
;
if (u != 1)
return -ENODEV;
if (pmr & (1<<6))
width = 16;
else
width = 8;
docmem.start = base;
docmem.end = base + size;
if (request_resource(&iomem_resource, &docmem)) {
printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
return -ENOMEM;
}
} else {
pci_dev_put(bridge);
for (u = size; u > 1; u >>= 1)
;
if (u != 1) {
printk(KERN_ERR NAME ": invalid size for flash mapping\n");
return -EINVAL;
}
if (width != 8 && width != 16) {
printk(KERN_ERR NAME ": invalid bus width for flash mapping\n");
return -EINVAL;
}
if (allocate_resource(&iomem_resource, &docmem,
size,
0xc0000000, 0xffffffff,
size, NULL, NULL)) {
printk(KERN_ERR NAME ": unable to allocate memory for flash mapping\n");
return -ENOMEM;
}
ctrl = 0x07000000 | ((size-1) >> 13);
printk(KERN_INFO "DOCCS BASE=0x%08lx, CTRL=0x%08lx\n", (long)docmem.start, (long)ctrl);
pci_write_config_dword(bridge, SCx200_DOCCS_BASE, docmem.start);
pci_write_config_dword(bridge, SCx200_DOCCS_CTRL, ctrl);
pmr = inl(scx200_cb_base + SCx200_PMR);
if (width == 8) {
pmr &= ~(1<<6);
} else {
pmr |= (1<<6);
}
outl(pmr, scx200_cb_base + SCx200_PMR);
}
printk(KERN_INFO NAME ": DOCCS mapped at %pR, width %d\n",
&docmem, width);
scx200_docflash_map.size = size;
if (width == 8)
scx200_docflash_map.bankwidth = 1;
else
scx200_docflash_map.bankwidth = 2;
simple_map_init(&scx200_docflash_map);
scx200_docflash_map.phys = docmem.start;
scx200_docflash_map.virt = ioremap(docmem.start, scx200_docflash_map.size);
if (!scx200_docflash_map.virt) {
printk(KERN_ERR NAME ": failed to ioremap the flash\n");
release_resource(&docmem);
return -EIO;
}
mymtd = do_map_probe(flashtype, &scx200_docflash_map);
if (!mymtd) {
printk(KERN_ERR NAME ": unable to detect flash\n");
iounmap(scx200_docflash_map.virt);
release_resource(&docmem);
return -ENXIO;
}
if (size < mymtd->size)
printk(KERN_WARNING NAME ": warning, flash mapping is smaller than flash size\n");
mymtd->owner = THIS_MODULE;
partition_info[3].offset = mymtd->size-partition_info[3].size;
partition_info[2].size = partition_info[3].offset-partition_info[2].offset;
mtd_device_register(mymtd, partition_info, NUM_PARTITIONS);
return 0;
}
static void __exit cleanup_scx200_docflash(void)
{
if (mymtd) {
mtd_device_unregister(mymtd);
map_destroy(mymtd);
}
if (scx200_docflash_map.virt) {
iounmap(scx200_docflash_map.virt);
release_resource(&docmem);
}
}
module_init(init_scx200_docflash);
module_exit(cleanup_scx200_docflash);
/*
Local variables:
compile-command: "make -k -C ../../.. SUBDIRS=drivers/mtd/maps modules"
c-basic-offset: 8
End:
*/

View file

@ -0,0 +1,95 @@
/*
* Flash and EPROM on Hitachi Solution Engine and similar boards.
*
* (C) 2001 Red Hat, Inc.
*
* GPL'd
*/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <linux/errno.h>
static struct mtd_info *flash_mtd;
static struct mtd_info *eprom_mtd;
struct map_info soleng_eprom_map = {
.name = "Solution Engine EPROM",
.size = 0x400000,
.bankwidth = 4,
};
struct map_info soleng_flash_map = {
.name = "Solution Engine FLASH",
.size = 0x400000,
.bankwidth = 4,
};
static const char * const probes[] = { "RedBoot", "cmdlinepart", NULL };
static int __init init_soleng_maps(void)
{
/* First probe at offset 0 */
soleng_flash_map.phys = 0;
soleng_flash_map.virt = (void __iomem *)P2SEGADDR(0);
soleng_eprom_map.phys = 0x01000000;
soleng_eprom_map.virt = (void __iomem *)P1SEGADDR(0x01000000);
simple_map_init(&soleng_eprom_map);
simple_map_init(&soleng_flash_map);
printk(KERN_NOTICE "Probing for flash chips at 0x00000000:\n");
flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map);
if (!flash_mtd) {
/* Not there. Try swapping */
printk(KERN_NOTICE "Probing for flash chips at 0x01000000:\n");
soleng_flash_map.phys = 0x01000000;
soleng_flash_map.virt = P2SEGADDR(0x01000000);
soleng_eprom_map.phys = 0;
soleng_eprom_map.virt = P1SEGADDR(0);
flash_mtd = do_map_probe("cfi_probe", &soleng_flash_map);
if (!flash_mtd) {
/* Eep. */
printk(KERN_NOTICE "Flash chips not detected at either possible location.\n");
return -ENXIO;
}
}
printk(KERN_NOTICE "Solution Engine: Flash at 0x%08lx, EPROM at 0x%08lx\n",
soleng_flash_map.phys & 0x1fffffff,
soleng_eprom_map.phys & 0x1fffffff);
flash_mtd->owner = THIS_MODULE;
eprom_mtd = do_map_probe("map_rom", &soleng_eprom_map);
if (eprom_mtd) {
eprom_mtd->owner = THIS_MODULE;
mtd_device_register(eprom_mtd, NULL, 0);
}
mtd_device_parse_register(flash_mtd, probes, NULL, NULL, 0);
return 0;
}
static void __exit cleanup_soleng_maps(void)
{
if (eprom_mtd) {
mtd_device_unregister(eprom_mtd);
map_destroy(eprom_mtd);
}
mtd_device_unregister(flash_mtd);
map_destroy(flash_mtd);
}
module_init(init_soleng_maps);
module_exit(cleanup_soleng_maps);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("David Woodhouse <dwmw2@infradead.org>");
MODULE_DESCRIPTION("MTD map driver for Hitachi SolutionEngine (and similar) boards");

View file

@ -0,0 +1,160 @@
/* sun_uflash.c - Driver for user-programmable flash on
* Sun Microsystems SME boardsets.
*
* This driver does NOT provide access to the OBP-flash for
* safety reasons-- use <linux>/drivers/sbus/char/flash.c instead.
*
* Copyright (c) 2001 Eric Brower (ebrower@usa.net)
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/ioport.h>
#include <linux/of.h>
#include <linux/of_device.h>
#include <linux/slab.h>
#include <asm/prom.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#define UFLASH_OBPNAME "flashprom"
#define DRIVER_NAME "sun_uflash"
#define PFX DRIVER_NAME ": "
#define UFLASH_WINDOW_SIZE 0x200000
#define UFLASH_BUSWIDTH 1 /* EBus is 8-bit */
MODULE_AUTHOR("Eric Brower <ebrower@usa.net>");
MODULE_DESCRIPTION("User-programmable flash device on Sun Microsystems boardsets");
MODULE_SUPPORTED_DEVICE(DRIVER_NAME);
MODULE_LICENSE("GPL");
MODULE_VERSION("2.1");
struct uflash_dev {
const char *name; /* device name */
struct map_info map; /* mtd map info */
struct mtd_info *mtd; /* mtd info */
};
struct map_info uflash_map_templ = {
.name = "SUNW,???-????",
.size = UFLASH_WINDOW_SIZE,
.bankwidth = UFLASH_BUSWIDTH,
};
int uflash_devinit(struct platform_device *op, struct device_node *dp)
{
struct uflash_dev *up;
if (op->resource[1].flags) {
/* Non-CFI userflash device-- once I find one we
* can work on supporting it.
*/
printk(KERN_ERR PFX "Unsupported device at %s, 0x%llx\n",
dp->full_name, (unsigned long long)op->resource[0].start);
return -ENODEV;
}
up = kzalloc(sizeof(struct uflash_dev), GFP_KERNEL);
if (!up) {
printk(KERN_ERR PFX "Cannot allocate struct uflash_dev\n");
return -ENOMEM;
}
/* copy defaults and tweak parameters */
memcpy(&up->map, &uflash_map_templ, sizeof(uflash_map_templ));
up->map.size = resource_size(&op->resource[0]);
up->name = of_get_property(dp, "model", NULL);
if (up->name && 0 < strlen(up->name))
up->map.name = up->name;
up->map.phys = op->resource[0].start;
up->map.virt = of_ioremap(&op->resource[0], 0, up->map.size,
DRIVER_NAME);
if (!up->map.virt) {
printk(KERN_ERR PFX "Failed to map device.\n");
kfree(up);
return -EINVAL;
}
simple_map_init(&up->map);
/* MTD registration */
up->mtd = do_map_probe("cfi_probe", &up->map);
if (!up->mtd) {
of_iounmap(&op->resource[0], up->map.virt, up->map.size);
kfree(up);
return -ENXIO;
}
up->mtd->owner = THIS_MODULE;
mtd_device_register(up->mtd, NULL, 0);
dev_set_drvdata(&op->dev, up);
return 0;
}
static int uflash_probe(struct platform_device *op)
{
struct device_node *dp = op->dev.of_node;
/* Flashprom must have the "user" property in order to
* be used by this driver.
*/
if (!of_find_property(dp, "user", NULL))
return -ENODEV;
return uflash_devinit(op, dp);
}
static int uflash_remove(struct platform_device *op)
{
struct uflash_dev *up = dev_get_drvdata(&op->dev);
if (up->mtd) {
mtd_device_unregister(up->mtd);
map_destroy(up->mtd);
}
if (up->map.virt) {
of_iounmap(&op->resource[0], up->map.virt, up->map.size);
up->map.virt = NULL;
}
kfree(up);
return 0;
}
static const struct of_device_id uflash_match[] = {
{
.name = UFLASH_OBPNAME,
},
{},
};
MODULE_DEVICE_TABLE(of, uflash_match);
static struct platform_driver uflash_driver = {
.driver = {
.name = DRIVER_NAME,
.owner = THIS_MODULE,
.of_match_table = uflash_match,
},
.probe = uflash_probe,
.remove = uflash_remove,
};
module_platform_driver(uflash_driver);

View file

@ -0,0 +1,121 @@
/*
* ts5500_flash.c -- MTD map driver for Technology Systems TS-5500 board
*
* Copyright (C) 2004 Sean Young <sean@mess.org>
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA
*
* Note:
* - In order for detection to work, jumper 3 must be set.
* - Drive A and B use the resident flash disk (RFD) flash translation layer.
* - If you have created your own jffs file system and the bios overwrites
* it during boot, try disabling Drive A: and B: in the boot order.
*/
#include <linux/init.h>
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/partitions.h>
#include <linux/types.h>
#define WINDOW_ADDR 0x09400000
#define WINDOW_SIZE 0x00200000
static struct map_info ts5500_map = {
.name = "TS-5500 Flash",
.size = WINDOW_SIZE,
.bankwidth = 1,
.phys = WINDOW_ADDR
};
static struct mtd_partition ts5500_partitions[] = {
{
.name = "Drive A",
.offset = 0,
.size = 0x0e0000
},
{
.name = "BIOS",
.offset = 0x0e0000,
.size = 0x020000,
},
{
.name = "Drive B",
.offset = 0x100000,
.size = 0x100000
}
};
#define NUM_PARTITIONS ARRAY_SIZE(ts5500_partitions)
static struct mtd_info *mymtd;
static int __init init_ts5500_map(void)
{
int rc = 0;
ts5500_map.virt = ioremap_nocache(ts5500_map.phys, ts5500_map.size);
if (!ts5500_map.virt) {
printk(KERN_ERR "Failed to ioremap_nocache\n");
rc = -EIO;
goto err2;
}
simple_map_init(&ts5500_map);
mymtd = do_map_probe("jedec_probe", &ts5500_map);
if (!mymtd)
mymtd = do_map_probe("map_rom", &ts5500_map);
if (!mymtd) {
rc = -ENXIO;
goto err1;
}
mymtd->owner = THIS_MODULE;
mtd_device_register(mymtd, ts5500_partitions, NUM_PARTITIONS);
return 0;
err1:
iounmap(ts5500_map.virt);
err2:
return rc;
}
static void __exit cleanup_ts5500_map(void)
{
if (mymtd) {
mtd_device_unregister(mymtd);
map_destroy(mymtd);
}
if (ts5500_map.virt) {
iounmap(ts5500_map.virt);
ts5500_map.virt = NULL;
}
}
module_init(init_ts5500_map);
module_exit(cleanup_ts5500_map);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Sean Young <sean@mess.org>");
MODULE_DESCRIPTION("MTD map driver for Techology Systems TS-5500 board");

View file

@ -0,0 +1,108 @@
/*
* tsunami_flash.c
*
* flash chip on alpha ds10...
*/
#include <asm/io.h>
#include <asm/core_tsunami.h>
#include <linux/init.h>
#include <linux/mtd/map.h>
#include <linux/mtd/mtd.h>
#define FLASH_ENABLE_PORT 0x00C00001
#define FLASH_ENABLE_BYTE 0x01
#define FLASH_DISABLE_BYTE 0x00
#define MAX_TIG_FLASH_SIZE (12*1024*1024)
static inline map_word tsunami_flash_read8(struct map_info *map, unsigned long offset)
{
map_word val;
val.x[0] = tsunami_tig_readb(offset);
return val;
}
static void tsunami_flash_write8(struct map_info *map, map_word value, unsigned long offset)
{
tsunami_tig_writeb(value.x[0], offset);
}
static void tsunami_flash_copy_from(
struct map_info *map, void *addr, unsigned long offset, ssize_t len)
{
unsigned char *dest;
dest = addr;
while(len && (offset < MAX_TIG_FLASH_SIZE)) {
*dest = tsunami_tig_readb(offset);
offset++;
dest++;
len--;
}
}
static void tsunami_flash_copy_to(
struct map_info *map, unsigned long offset,
const void *addr, ssize_t len)
{
const unsigned char *src;
src = addr;
while(len && (offset < MAX_TIG_FLASH_SIZE)) {
tsunami_tig_writeb(*src, offset);
offset++;
src++;
len--;
}
}
/*
* Deliberately don't provide operations wider than 8 bits. I don't
* have then and it scares me to think how you could mess up if
* you tried to use them. Buswidth is correctly so I'm safe.
*/
static struct map_info tsunami_flash_map = {
.name = "flash chip on the Tsunami TIG bus",
.size = MAX_TIG_FLASH_SIZE,
.phys = NO_XIP,
.bankwidth = 1,
.read = tsunami_flash_read8,
.copy_from = tsunami_flash_copy_from,
.write = tsunami_flash_write8,
.copy_to = tsunami_flash_copy_to,
};
static struct mtd_info *tsunami_flash_mtd;
static void __exit cleanup_tsunami_flash(void)
{
struct mtd_info *mtd;
mtd = tsunami_flash_mtd;
if (mtd) {
mtd_device_unregister(mtd);
map_destroy(mtd);
}
tsunami_flash_mtd = 0;
}
static const char * const rom_probe_types[] = {
"cfi_probe", "jedec_probe", "map_rom", NULL };
static int __init init_tsunami_flash(void)
{
const char * const *type;
tsunami_tig_writeb(FLASH_ENABLE_BYTE, FLASH_ENABLE_PORT);
tsunami_flash_mtd = 0;
type = rom_probe_types;
for(; !tsunami_flash_mtd && *type; type++) {
tsunami_flash_mtd = do_map_probe(*type, &tsunami_flash_map);
}
if (tsunami_flash_mtd) {
tsunami_flash_mtd->owner = THIS_MODULE;
mtd_device_register(tsunami_flash_mtd, NULL, 0);
return 0;
}
return -ENXIO;
}
module_init(init_tsunami_flash);
module_exit(cleanup_tsunami_flash);

143
drivers/mtd/maps/uclinux.c Normal file
View file

@ -0,0 +1,143 @@
/****************************************************************************/
/*
* uclinux.c -- generic memory mapped MTD driver for uclinux
*
* (C) Copyright 2002, Greg Ungerer (gerg@snapgear.com)
*/
/****************************************************************************/
#include <linux/module.h>
#include <linux/types.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/fs.h>
#include <linux/mm.h>
#include <linux/major.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
#include <linux/mtd/partitions.h>
#include <asm/io.h>
#include <asm/sections.h>
/****************************************************************************/
#ifdef CONFIG_MTD_ROM
#define MAP_NAME "rom"
#else
#define MAP_NAME "ram"
#endif
/*
* Blackfin uses uclinux_ram_map during startup, so it must not be static.
* Provide a dummy declaration to make sparse happy.
*/
extern struct map_info uclinux_ram_map;
struct map_info uclinux_ram_map = {
.name = MAP_NAME,
.size = 0,
};
static unsigned long physaddr = -1;
module_param(physaddr, ulong, S_IRUGO);
static struct mtd_info *uclinux_ram_mtdinfo;
/****************************************************************************/
static struct mtd_partition uclinux_romfs[] = {
{ .name = "ROMfs" }
};
#define NUM_PARTITIONS ARRAY_SIZE(uclinux_romfs)
/****************************************************************************/
static int uclinux_point(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, void **virt, resource_size_t *phys)
{
struct map_info *map = mtd->priv;
*virt = map->virt + from;
if (phys)
*phys = map->phys + from;
*retlen = len;
return(0);
}
/****************************************************************************/
static int __init uclinux_mtd_init(void)
{
struct mtd_info *mtd;
struct map_info *mapp;
mapp = &uclinux_ram_map;
if (physaddr == -1)
mapp->phys = (resource_size_t)__bss_stop;
else
mapp->phys = physaddr;
if (!mapp->size)
mapp->size = PAGE_ALIGN(ntohl(*((unsigned long *)(mapp->phys + 8))));
mapp->bankwidth = 4;
printk("uclinux[mtd]: probe address=0x%x size=0x%x\n",
(int) mapp->phys, (int) mapp->size);
/*
* The filesystem is guaranteed to be in direct mapped memory. It is
* directly following the kernels own bss region. Following the same
* mechanism used by architectures setting up traditional initrds we
* use phys_to_virt to get the virtual address of its start.
*/
mapp->virt = phys_to_virt(mapp->phys);
if (mapp->virt == 0) {
printk("uclinux[mtd]: no virtual mapping?\n");
return(-EIO);
}
simple_map_init(mapp);
mtd = do_map_probe("map_" MAP_NAME, mapp);
if (!mtd) {
printk("uclinux[mtd]: failed to find a mapping?\n");
return(-ENXIO);
}
mtd->owner = THIS_MODULE;
mtd->_point = uclinux_point;
mtd->priv = mapp;
uclinux_ram_mtdinfo = mtd;
mtd_device_register(mtd, uclinux_romfs, NUM_PARTITIONS);
return(0);
}
/****************************************************************************/
static void __exit uclinux_mtd_cleanup(void)
{
if (uclinux_ram_mtdinfo) {
mtd_device_unregister(uclinux_ram_mtdinfo);
map_destroy(uclinux_ram_mtdinfo);
uclinux_ram_mtdinfo = NULL;
}
if (uclinux_ram_map.virt)
uclinux_ram_map.virt = 0;
}
/****************************************************************************/
module_init(uclinux_mtd_init);
module_exit(uclinux_mtd_cleanup);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Greg Ungerer <gerg@snapgear.com>");
MODULE_DESCRIPTION("Generic MTD for uClinux");
/****************************************************************************/

View file

@ -0,0 +1,824 @@
/* vmu-flash.c
* Driver for SEGA Dreamcast Visual Memory Unit
*
* Copyright (c) Adrian McMenamin 2002 - 2009
* Copyright (c) Paul Mundt 2001
*
* Licensed under version 2 of the
* GNU General Public Licence
*/
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/sched.h>
#include <linux/delay.h>
#include <linux/maple.h>
#include <linux/mtd/mtd.h>
#include <linux/mtd/map.h>
struct vmu_cache {
unsigned char *buffer; /* Cache */
unsigned int block; /* Which block was cached */
unsigned long jiffies_atc; /* When was it cached? */
int valid;
};
struct mdev_part {
struct maple_device *mdev;
int partition;
};
struct vmupart {
u16 user_blocks;
u16 root_block;
u16 numblocks;
char *name;
struct vmu_cache *pcache;
};
struct memcard {
u16 tempA;
u16 tempB;
u32 partitions;
u32 blocklen;
u32 writecnt;
u32 readcnt;
u32 removeable;
int partition;
int read;
unsigned char *blockread;
struct vmupart *parts;
struct mtd_info *mtd;
};
struct vmu_block {
unsigned int num; /* block number */
unsigned int ofs; /* block offset */
};
static struct vmu_block *ofs_to_block(unsigned long src_ofs,
struct mtd_info *mtd, int partition)
{
struct vmu_block *vblock;
struct maple_device *mdev;
struct memcard *card;
struct mdev_part *mpart;
int num;
mpart = mtd->priv;
mdev = mpart->mdev;
card = maple_get_drvdata(mdev);
if (src_ofs >= card->parts[partition].numblocks * card->blocklen)
goto failed;
num = src_ofs / card->blocklen;
if (num > card->parts[partition].numblocks)
goto failed;
vblock = kmalloc(sizeof(struct vmu_block), GFP_KERNEL);
if (!vblock)
goto failed;
vblock->num = num;
vblock->ofs = src_ofs % card->blocklen;
return vblock;
failed:
return NULL;
}
/* Maple bus callback function for reads */
static void vmu_blockread(struct mapleq *mq)
{
struct maple_device *mdev;
struct memcard *card;
mdev = mq->dev;
card = maple_get_drvdata(mdev);
/* copy the read in data */
if (unlikely(!card->blockread))
return;
memcpy(card->blockread, mq->recvbuf->buf + 12,
card->blocklen/card->readcnt);
}
/* Interface with maple bus to read blocks
* caching the results so that other parts
* of the driver can access block reads */
static int maple_vmu_read_block(unsigned int num, unsigned char *buf,
struct mtd_info *mtd)
{
struct memcard *card;
struct mdev_part *mpart;
struct maple_device *mdev;
int partition, error = 0, x, wait;
unsigned char *blockread = NULL;
struct vmu_cache *pcache;
__be32 sendbuf;
mpart = mtd->priv;
mdev = mpart->mdev;
partition = mpart->partition;
card = maple_get_drvdata(mdev);
pcache = card->parts[partition].pcache;
pcache->valid = 0;
/* prepare the cache for this block */
if (!pcache->buffer) {
pcache->buffer = kmalloc(card->blocklen, GFP_KERNEL);
if (!pcache->buffer) {
dev_err(&mdev->dev, "VMU at (%d, %d) - read fails due"
" to lack of memory\n", mdev->port,
mdev->unit);
error = -ENOMEM;
goto outB;
}
}
/*
* Reads may be phased - again the hardware spec
* supports this - though may not be any devices in
* the wild that implement it, but we will here
*/
for (x = 0; x < card->readcnt; x++) {
sendbuf = cpu_to_be32(partition << 24 | x << 16 | num);
if (atomic_read(&mdev->busy) == 1) {
wait_event_interruptible_timeout(mdev->maple_wait,
atomic_read(&mdev->busy) == 0, HZ);
if (atomic_read(&mdev->busy) == 1) {
dev_notice(&mdev->dev, "VMU at (%d, %d)"
" is busy\n", mdev->port, mdev->unit);
error = -EAGAIN;
goto outB;
}
}
atomic_set(&mdev->busy, 1);
blockread = kmalloc(card->blocklen/card->readcnt, GFP_KERNEL);
if (!blockread) {
error = -ENOMEM;
atomic_set(&mdev->busy, 0);
goto outB;
}
card->blockread = blockread;
maple_getcond_callback(mdev, vmu_blockread, 0,
MAPLE_FUNC_MEMCARD);
error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
MAPLE_COMMAND_BREAD, 2, &sendbuf);
/* Very long timeouts seem to be needed when box is stressed */
wait = wait_event_interruptible_timeout(mdev->maple_wait,
(atomic_read(&mdev->busy) == 0 ||
atomic_read(&mdev->busy) == 2), HZ * 3);
/*
* MTD layer does not handle hotplugging well
* so have to return errors when VMU is unplugged
* in the middle of a read (busy == 2)
*/
if (error || atomic_read(&mdev->busy) == 2) {
if (atomic_read(&mdev->busy) == 2)
error = -ENXIO;
atomic_set(&mdev->busy, 0);
card->blockread = NULL;
goto outA;
}
if (wait == 0 || wait == -ERESTARTSYS) {
card->blockread = NULL;
atomic_set(&mdev->busy, 0);
error = -EIO;
list_del_init(&(mdev->mq->list));
kfree(mdev->mq->sendbuf);
mdev->mq->sendbuf = NULL;
if (wait == -ERESTARTSYS) {
dev_warn(&mdev->dev, "VMU read on (%d, %d)"
" interrupted on block 0x%X\n",
mdev->port, mdev->unit, num);
} else
dev_notice(&mdev->dev, "VMU read on (%d, %d)"
" timed out on block 0x%X\n",
mdev->port, mdev->unit, num);
goto outA;
}
memcpy(buf + (card->blocklen/card->readcnt) * x, blockread,
card->blocklen/card->readcnt);
memcpy(pcache->buffer + (card->blocklen/card->readcnt) * x,
card->blockread, card->blocklen/card->readcnt);
card->blockread = NULL;
pcache->block = num;
pcache->jiffies_atc = jiffies;
pcache->valid = 1;
kfree(blockread);
}
return error;
outA:
kfree(blockread);
outB:
return error;
}
/* communicate with maple bus for phased writing */
static int maple_vmu_write_block(unsigned int num, const unsigned char *buf,
struct mtd_info *mtd)
{
struct memcard *card;
struct mdev_part *mpart;
struct maple_device *mdev;
int partition, error, locking, x, phaselen, wait;
__be32 *sendbuf;
mpart = mtd->priv;
mdev = mpart->mdev;
partition = mpart->partition;
card = maple_get_drvdata(mdev);
phaselen = card->blocklen/card->writecnt;
sendbuf = kmalloc(phaselen + 4, GFP_KERNEL);
if (!sendbuf) {
error = -ENOMEM;
goto fail_nosendbuf;
}
for (x = 0; x < card->writecnt; x++) {
sendbuf[0] = cpu_to_be32(partition << 24 | x << 16 | num);
memcpy(&sendbuf[1], buf + phaselen * x, phaselen);
/* wait until the device is not busy doing something else
* or 1 second - which ever is longer */
if (atomic_read(&mdev->busy) == 1) {
wait_event_interruptible_timeout(mdev->maple_wait,
atomic_read(&mdev->busy) == 0, HZ);
if (atomic_read(&mdev->busy) == 1) {
error = -EBUSY;
dev_notice(&mdev->dev, "VMU write at (%d, %d)"
"failed - device is busy\n",
mdev->port, mdev->unit);
goto fail_nolock;
}
}
atomic_set(&mdev->busy, 1);
locking = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
MAPLE_COMMAND_BWRITE, phaselen / 4 + 2, sendbuf);
wait = wait_event_interruptible_timeout(mdev->maple_wait,
atomic_read(&mdev->busy) == 0, HZ/10);
if (locking) {
error = -EIO;
atomic_set(&mdev->busy, 0);
goto fail_nolock;
}
if (atomic_read(&mdev->busy) == 2) {
atomic_set(&mdev->busy, 0);
} else if (wait == 0 || wait == -ERESTARTSYS) {
error = -EIO;
dev_warn(&mdev->dev, "Write at (%d, %d) of block"
" 0x%X at phase %d failed: could not"
" communicate with VMU", mdev->port,
mdev->unit, num, x);
atomic_set(&mdev->busy, 0);
kfree(mdev->mq->sendbuf);
mdev->mq->sendbuf = NULL;
list_del_init(&(mdev->mq->list));
goto fail_nolock;
}
}
kfree(sendbuf);
return card->blocklen;
fail_nolock:
kfree(sendbuf);
fail_nosendbuf:
dev_err(&mdev->dev, "VMU (%d, %d): write failed\n", mdev->port,
mdev->unit);
return error;
}
/* mtd function to simulate reading byte by byte */
static unsigned char vmu_flash_read_char(unsigned long ofs, int *retval,
struct mtd_info *mtd)
{
struct vmu_block *vblock;
struct memcard *card;
struct mdev_part *mpart;
struct maple_device *mdev;
unsigned char *buf, ret;
int partition, error;
mpart = mtd->priv;
mdev = mpart->mdev;
partition = mpart->partition;
card = maple_get_drvdata(mdev);
*retval = 0;
buf = kmalloc(card->blocklen, GFP_KERNEL);
if (!buf) {
*retval = 1;
ret = -ENOMEM;
goto finish;
}
vblock = ofs_to_block(ofs, mtd, partition);
if (!vblock) {
*retval = 3;
ret = -ENOMEM;
goto out_buf;
}
error = maple_vmu_read_block(vblock->num, buf, mtd);
if (error) {
ret = error;
*retval = 2;
goto out_vblock;
}
ret = buf[vblock->ofs];
out_vblock:
kfree(vblock);
out_buf:
kfree(buf);
finish:
return ret;
}
/* mtd higher order function to read flash */
static int vmu_flash_read(struct mtd_info *mtd, loff_t from, size_t len,
size_t *retlen, u_char *buf)
{
struct maple_device *mdev;
struct memcard *card;
struct mdev_part *mpart;
struct vmu_cache *pcache;
struct vmu_block *vblock;
int index = 0, retval, partition, leftover, numblocks;
unsigned char cx;
mpart = mtd->priv;
mdev = mpart->mdev;
partition = mpart->partition;
card = maple_get_drvdata(mdev);
numblocks = card->parts[partition].numblocks;
if (from + len > numblocks * card->blocklen)
len = numblocks * card->blocklen - from;
if (len == 0)
return -EIO;
/* Have we cached this bit already? */
pcache = card->parts[partition].pcache;
do {
vblock = ofs_to_block(from + index, mtd, partition);
if (!vblock)
return -ENOMEM;
/* Have we cached this and is the cache valid and timely? */
if (pcache->valid &&
time_before(jiffies, pcache->jiffies_atc + HZ) &&
(pcache->block == vblock->num)) {
/* we have cached it, so do necessary copying */
leftover = card->blocklen - vblock->ofs;
if (vblock->ofs + len - index < card->blocklen) {
/* only a bit of this block to copy */
memcpy(buf + index,
pcache->buffer + vblock->ofs,
len - index);
index = len;
} else {
/* otherwise copy remainder of whole block */
memcpy(buf + index, pcache->buffer +
vblock->ofs, leftover);
index += leftover;
}
} else {
/*
* Not cached so read one byte -
* but cache the rest of the block
*/
cx = vmu_flash_read_char(from + index, &retval, mtd);
if (retval) {
*retlen = index;
kfree(vblock);
return cx;
}
memset(buf + index, cx, 1);
index++;
}
kfree(vblock);
} while (len > index);
*retlen = index;
return 0;
}
static int vmu_flash_write(struct mtd_info *mtd, loff_t to, size_t len,
size_t *retlen, const u_char *buf)
{
struct maple_device *mdev;
struct memcard *card;
struct mdev_part *mpart;
int index = 0, partition, error = 0, numblocks;
struct vmu_cache *pcache;
struct vmu_block *vblock;
unsigned char *buffer;
mpart = mtd->priv;
mdev = mpart->mdev;
partition = mpart->partition;
card = maple_get_drvdata(mdev);
numblocks = card->parts[partition].numblocks;
if (to + len > numblocks * card->blocklen)
len = numblocks * card->blocklen - to;
if (len == 0) {
error = -EIO;
goto failed;
}
vblock = ofs_to_block(to, mtd, partition);
if (!vblock) {
error = -ENOMEM;
goto failed;
}
buffer = kmalloc(card->blocklen, GFP_KERNEL);
if (!buffer) {
error = -ENOMEM;
goto fail_buffer;
}
do {
/* Read in the block we are to write to */
error = maple_vmu_read_block(vblock->num, buffer, mtd);
if (error)
goto fail_io;
do {
buffer[vblock->ofs] = buf[index];
vblock->ofs++;
index++;
if (index >= len)
break;
} while (vblock->ofs < card->blocklen);
/* write out new buffer */
error = maple_vmu_write_block(vblock->num, buffer, mtd);
/* invalidate the cache */
pcache = card->parts[partition].pcache;
pcache->valid = 0;
if (error != card->blocklen)
goto fail_io;
vblock->num++;
vblock->ofs = 0;
} while (len > index);
kfree(buffer);
*retlen = index;
kfree(vblock);
return 0;
fail_io:
kfree(buffer);
fail_buffer:
kfree(vblock);
failed:
dev_err(&mdev->dev, "VMU write failing with error %d\n", error);
return error;
}
static void vmu_flash_sync(struct mtd_info *mtd)
{
/* Do nothing here */
}
/* Maple bus callback function to recursively query hardware details */
static void vmu_queryblocks(struct mapleq *mq)
{
struct maple_device *mdev;
unsigned short *res;
struct memcard *card;
__be32 partnum;
struct vmu_cache *pcache;
struct mdev_part *mpart;
struct mtd_info *mtd_cur;
struct vmupart *part_cur;
int error;
mdev = mq->dev;
card = maple_get_drvdata(mdev);
res = (unsigned short *) (mq->recvbuf->buf);
card->tempA = res[12];
card->tempB = res[6];
dev_info(&mdev->dev, "VMU device at partition %d has %d user "
"blocks with a root block at %d\n", card->partition,
card->tempA, card->tempB);
part_cur = &card->parts[card->partition];
part_cur->user_blocks = card->tempA;
part_cur->root_block = card->tempB;
part_cur->numblocks = card->tempB + 1;
part_cur->name = kmalloc(12, GFP_KERNEL);
if (!part_cur->name)
goto fail_name;
sprintf(part_cur->name, "vmu%d.%d.%d",
mdev->port, mdev->unit, card->partition);
mtd_cur = &card->mtd[card->partition];
mtd_cur->name = part_cur->name;
mtd_cur->type = 8;
mtd_cur->flags = MTD_WRITEABLE|MTD_NO_ERASE;
mtd_cur->size = part_cur->numblocks * card->blocklen;
mtd_cur->erasesize = card->blocklen;
mtd_cur->_write = vmu_flash_write;
mtd_cur->_read = vmu_flash_read;
mtd_cur->_sync = vmu_flash_sync;
mtd_cur->writesize = card->blocklen;
mpart = kmalloc(sizeof(struct mdev_part), GFP_KERNEL);
if (!mpart)
goto fail_mpart;
mpart->mdev = mdev;
mpart->partition = card->partition;
mtd_cur->priv = mpart;
mtd_cur->owner = THIS_MODULE;
pcache = kzalloc(sizeof(struct vmu_cache), GFP_KERNEL);
if (!pcache)
goto fail_cache_create;
part_cur->pcache = pcache;
error = mtd_device_register(mtd_cur, NULL, 0);
if (error)
goto fail_mtd_register;
maple_getcond_callback(mdev, NULL, 0,
MAPLE_FUNC_MEMCARD);
/*
* Set up a recursive call to the (probably theoretical)
* second or more partition
*/
if (++card->partition < card->partitions) {
partnum = cpu_to_be32(card->partition << 24);
maple_getcond_callback(mdev, vmu_queryblocks, 0,
MAPLE_FUNC_MEMCARD);
maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
MAPLE_COMMAND_GETMINFO, 2, &partnum);
}
return;
fail_mtd_register:
dev_err(&mdev->dev, "Could not register maple device at (%d, %d)"
"error is 0x%X\n", mdev->port, mdev->unit, error);
for (error = 0; error <= card->partition; error++) {
kfree(((card->parts)[error]).pcache);
((card->parts)[error]).pcache = NULL;
}
fail_cache_create:
fail_mpart:
for (error = 0; error <= card->partition; error++) {
kfree(((card->mtd)[error]).priv);
((card->mtd)[error]).priv = NULL;
}
maple_getcond_callback(mdev, NULL, 0,
MAPLE_FUNC_MEMCARD);
kfree(part_cur->name);
fail_name:
return;
}
/* Handles very basic info about the flash, queries for details */
static int vmu_connect(struct maple_device *mdev)
{
unsigned long test_flash_data, basic_flash_data;
int c, error;
struct memcard *card;
u32 partnum = 0;
test_flash_data = be32_to_cpu(mdev->devinfo.function);
/* Need to count how many bits are set - to find out which
* function_data element has details of the memory card
*/
c = hweight_long(test_flash_data);
basic_flash_data = be32_to_cpu(mdev->devinfo.function_data[c - 1]);
card = kmalloc(sizeof(struct memcard), GFP_KERNEL);
if (!card) {
error = -ENOMEM;
goto fail_nomem;
}
card->partitions = (basic_flash_data >> 24 & 0xFF) + 1;
card->blocklen = ((basic_flash_data >> 16 & 0xFF) + 1) << 5;
card->writecnt = basic_flash_data >> 12 & 0xF;
card->readcnt = basic_flash_data >> 8 & 0xF;
card->removeable = basic_flash_data >> 7 & 1;
card->partition = 0;
/*
* Not sure there are actually any multi-partition devices in the
* real world, but the hardware supports them, so, so will we
*/
card->parts = kmalloc(sizeof(struct vmupart) * card->partitions,
GFP_KERNEL);
if (!card->parts) {
error = -ENOMEM;
goto fail_partitions;
}
card->mtd = kmalloc(sizeof(struct mtd_info) * card->partitions,
GFP_KERNEL);
if (!card->mtd) {
error = -ENOMEM;
goto fail_mtd_info;
}
maple_set_drvdata(mdev, card);
/*
* We want to trap meminfo not get cond
* so set interval to zero, but rely on maple bus
* driver to pass back the results of the meminfo
*/
maple_getcond_callback(mdev, vmu_queryblocks, 0,
MAPLE_FUNC_MEMCARD);
/* Make sure we are clear to go */
if (atomic_read(&mdev->busy) == 1) {
wait_event_interruptible_timeout(mdev->maple_wait,
atomic_read(&mdev->busy) == 0, HZ);
if (atomic_read(&mdev->busy) == 1) {
dev_notice(&mdev->dev, "VMU at (%d, %d) is busy\n",
mdev->port, mdev->unit);
error = -EAGAIN;
goto fail_device_busy;
}
}
atomic_set(&mdev->busy, 1);
/*
* Set up the minfo call: vmu_queryblocks will handle
* the information passed back
*/
error = maple_add_packet(mdev, MAPLE_FUNC_MEMCARD,
MAPLE_COMMAND_GETMINFO, 2, &partnum);
if (error) {
dev_err(&mdev->dev, "Could not lock VMU at (%d, %d)"
" error is 0x%X\n", mdev->port, mdev->unit, error);
goto fail_mtd_info;
}
return 0;
fail_device_busy:
kfree(card->mtd);
fail_mtd_info:
kfree(card->parts);
fail_partitions:
kfree(card);
fail_nomem:
return error;
}
static void vmu_disconnect(struct maple_device *mdev)
{
struct memcard *card;
struct mdev_part *mpart;
int x;
mdev->callback = NULL;
card = maple_get_drvdata(mdev);
for (x = 0; x < card->partitions; x++) {
mpart = ((card->mtd)[x]).priv;
mpart->mdev = NULL;
mtd_device_unregister(&((card->mtd)[x]));
kfree(((card->parts)[x]).name);
}
kfree(card->parts);
kfree(card->mtd);
kfree(card);
}
/* Callback to handle eccentricities of both mtd subsystem
* and general flakyness of Dreamcast VMUs
*/
static int vmu_can_unload(struct maple_device *mdev)
{
struct memcard *card;
int x;
struct mtd_info *mtd;
card = maple_get_drvdata(mdev);
for (x = 0; x < card->partitions; x++) {
mtd = &((card->mtd)[x]);
if (mtd->usecount > 0)
return 0;
}
return 1;
}
#define ERRSTR "VMU at (%d, %d) file error -"
static void vmu_file_error(struct maple_device *mdev, void *recvbuf)
{
enum maple_file_errors error = ((int *)recvbuf)[1];
switch (error) {
case MAPLE_FILEERR_INVALID_PARTITION:
dev_notice(&mdev->dev, ERRSTR " invalid partition number\n",
mdev->port, mdev->unit);
break;
case MAPLE_FILEERR_PHASE_ERROR:
dev_notice(&mdev->dev, ERRSTR " phase error\n",
mdev->port, mdev->unit);
break;
case MAPLE_FILEERR_INVALID_BLOCK:
dev_notice(&mdev->dev, ERRSTR " invalid block number\n",
mdev->port, mdev->unit);
break;
case MAPLE_FILEERR_WRITE_ERROR:
dev_notice(&mdev->dev, ERRSTR " write error\n",
mdev->port, mdev->unit);
break;
case MAPLE_FILEERR_INVALID_WRITE_LENGTH:
dev_notice(&mdev->dev, ERRSTR " invalid write length\n",
mdev->port, mdev->unit);
break;
case MAPLE_FILEERR_BAD_CRC:
dev_notice(&mdev->dev, ERRSTR " bad CRC\n",
mdev->port, mdev->unit);
break;
default:
dev_notice(&mdev->dev, ERRSTR " 0x%X\n",
mdev->port, mdev->unit, error);
}
}
static int probe_maple_vmu(struct device *dev)
{
int error;
struct maple_device *mdev = to_maple_dev(dev);
struct maple_driver *mdrv = to_maple_driver(dev->driver);
mdev->can_unload = vmu_can_unload;
mdev->fileerr_handler = vmu_file_error;
mdev->driver = mdrv;
error = vmu_connect(mdev);
if (error)
return error;
return 0;
}
static int remove_maple_vmu(struct device *dev)
{
struct maple_device *mdev = to_maple_dev(dev);
vmu_disconnect(mdev);
return 0;
}
static struct maple_driver vmu_flash_driver = {
.function = MAPLE_FUNC_MEMCARD,
.drv = {
.name = "Dreamcast_visual_memory",
.probe = probe_maple_vmu,
.remove = remove_maple_vmu,
},
};
static int __init vmu_flash_map_init(void)
{
return maple_driver_register(&vmu_flash_driver);
}
static void __exit vmu_flash_map_exit(void)
{
maple_driver_unregister(&vmu_flash_driver);
}
module_init(vmu_flash_map_init);
module_exit(vmu_flash_map_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("Adrian McMenamin");
MODULE_DESCRIPTION("Flash mapping for Sega Dreamcast visual memory");